From 4ad39a5c96dd99acf819ce189b13fec946d7506b Mon Sep 17 00:00:00 2001 From: talasila Date: Tue, 7 Feb 2017 15:03:57 -0500 Subject: Initial OpenECOMP Portal commit Change-Id: I804b80e0830c092e307da1599bd9fbb5c3e2da77 Signed-off-by: talasila --- ecomp-portal-FE/.bowerrc | 3 + ecomp-portal-FE/.gitignore | 6 + ecomp-portal-FE/Gruntfile.js | 779 + ecomp-portal-FE/README.md | 51 + ecomp-portal-FE/bower.json | 36 + ecomp-portal-FE/build-ecomportal-fe.sh | 181 + ecomp-portal-FE/client/.htaccess | 543 + ecomp-portal-FE/client/.jshintrc | 36 + ecomp-portal-FE/client/app/app.js | 120 + ecomp-portal-FE/client/app/app.less | 78 + ecomp-portal-FE/client/app/configurations.js | 5 + .../directives/auto-focus/auto-focus.directive.js | 46 + .../image-upload/image-upload.directive.js | 222 + .../directives/left-menu/left-menu.directive.js | 96 + .../client/app/directives/left-menu/left-menu.less | 133 + .../app/directives/left-menu/left-menu.tpl.html | 57 + .../multiple-select/multiple-select.directive.js | 111 + .../multiple-select/multiple-select.less | 81 + .../multiple-select/multiple-select.tpl.html | 38 + .../right-click-menu/right-click-menu.directive.js | 38 + .../right-click/ng-right-click-directive.js | 32 + .../directives/right-menu/right-menu.directive.js | 140 + .../app/directives/right-menu/right-menu.less | 178 + .../app/directives/right-menu/right-menu.tpl.html | 40 + .../directives/scroll-top/scroll-top.directive.js | 35 + .../search-users/search-users.controller.js | 161 + .../search-users/search-users.controller.spec.js | 176 + .../search-users/search-users.directive.js | 34 + .../app/directives/search-users/search-users.less | 154 + .../directives/search-users/search-users.tpl.html | 144 + .../client/app/filters/elipsis/elipsis.filter.js | 38 + .../app/filters/elipsis/elipsis.filter.spec.js | 58 + .../app/filters/trusted-url/trusted-url.filter.js | 26 + ecomp-portal-FE/client/app/router.js | 528 + .../client/app/services/admins/admins.service.js | 176 + .../services/applications/applications.service.js | 346 + .../client/app/services/catalog/catalog.service.js | 95 + .../services/confirm-box/confirm-box.service.js | 153 + .../app/services/contact-us/contact-us.service.js | 247 + .../app/services/dashboard/dashboard.service.js | 185 + .../error-messages/error-messages.service.js | 23 + .../functionalMenu/functionalMenu.service.js | 253 + .../services/global-constants/global-constants.js | 21 + .../kpi-dashboard/kpi-dashboard.service.js | 185 + .../app/services/manifest/manifest.service.js | 64 + .../client/app/services/menus/menus.service.js | 145 + .../portal-admins/portal-admins.service.js | 107 + .../support/getAccess/get-access.service.js | 61 + .../services/support/session/session.service.js | 56 + .../services/userProfile/userProfile.service.js | 240 + .../app/services/userbar/userbar.update.service.js | 97 + .../client/app/services/users/users.service.js | 161 + .../client/app/services/utils/utils.service.js | 57 + .../client/app/services/widgets/widgets.service.js | 178 + ecomp-portal-FE/client/app/styles/att-abs.less | 86 + ecomp-portal-FE/client/app/styles/buttons.less | 72 + .../client/app/styles/ecomp-general.less | 76 + ecomp-portal-FE/client/app/styles/fonts.less | 81 + ecomp-portal-FE/client/app/styles/form.less | 189 + .../client/app/styles/kpi-dashboard.less | 111 + ecomp-portal-FE/client/app/styles/mixins.less | 296 + ecomp-portal-FE/client/app/styles/ng-dialog.less | 88 + ecomp-portal-FE/client/app/styles/reset.less | 79 + ecomp-portal-FE/client/app/styles/select2.less | 102 + ecomp-portal-FE/client/app/styles/spinner.less | 56 + ecomp-portal-FE/client/app/styles/sprites.less | 118 + ecomp-portal-FE/client/app/styles/variables.less | 86 + .../add-admin-dialogs/new-admin.controller.js | 214 + .../add-admin-dialogs/new-admin.controller.spec.js | 130 + .../admins/add-admin-dialogs/new-admin.modal.html | 66 + .../admins/add-admin-dialogs/new-admin.modal.less | 99 + .../client/app/views/admins/admins.controller.js | 151 + .../app/views/admins/admins.controller.spec.js | 19 + .../client/app/views/admins/admins.less | 47 + .../client/app/views/admins/admins.tpl.html | 82 + .../application-details.controller.js | 244 + .../application-details.controller.spec.js | 19 + .../application-details.modal.html | 183 + .../application-details.modal.less | 122 + .../views/applications/applications.controller.js | 111 + .../applications/applications.controller.spec.js | 19 + .../app/views/applications/applications.less | 45 + .../app/views/applications/applications.tpl.html | 100 + .../client/app/views/catalog/catalog.controller.js | 166 + .../client/app/views/catalog/catalog.less | 439 + .../client/app/views/catalog/catalog.tpl.html | 91 + .../catalog/catalogconfirmation.controller.js | 62 + .../app/views/catalog/information-box.tpl.html | 42 + .../admin-confirmation-box.tpl.html | 31 + .../confirmation-box.controller.js | 41 + .../views/confirmation-box/confirmation-box.less | 53 + .../confirmation-box/confirmation-box.tpl.html | 28 + .../dragdrop-confirmation-box.tpl.html | 31 + .../confirmation-box/information-box.tpl.html | 27 + .../views/dashboard/dashboard-widget-manage.html | 152 + .../views/dashboard/dashboard-widget.controller.js | 422 + .../dashboard/dashboard-widget.controller.less | 101 + .../app/views/dashboard/dashboard.controller.js | 312 + .../views/dashboard/dashboard.controller.spec.js | 78 + .../client/app/views/dashboard/dashboard.less | 766 + .../client/app/views/dashboard/dashboard.tpl.html | 346 + .../app/views/dashboard/newsticker.controller.js | 47 + .../client/app/views/errors/error.404.tpl.html | 31 + .../client/app/views/errors/error.controller.js | 36 + .../client/app/views/errors/error.tpl.html | 26 + .../client/app/views/footer/footer.controller.js | 49 + .../app/views/footer/footer.controller.spec.js | 19 + .../client/app/views/footer/footer.less | 53 + .../client/app/views/footer/footer.tpl.html | 36 + .../menu-details.controller.js | 384 + .../menu-details.delete.modal.html | 35 + .../functionalMenu-dialog/menu-details.modal.html | 106 + .../functionalMenu-dialog/modal-details.modal.less | 103 + .../functionalMenu/functionalMenu.controller.js | 331 + .../app/views/functionalMenu/functionalMenu.less | 70 + .../views/functionalMenu/functionalMenu.tpl.html | 48 + .../app/views/functionalMenu/jqTreeContextMenu.js | 192 + .../client/app/views/header/header.controller.js | 419 + .../app/views/header/header.controller.spec.js | 19 + .../client/app/views/header/header.less | 380 + .../client/app/views/header/header.tpl.html | 277 + .../views/header/user-edit/edit-user.controller.js | 134 + .../app/views/header/user-edit/edit-user.less | 61 + .../app/views/header/user-edit/edit-user.tpl.html | 69 + .../applications-home.controller.js | 244 + .../applications-home.controller.spec.js | 77 + .../home/applications-home/applications-home.less | 164 + .../applications-home/applications-home.tpl.html | 59 + .../home/widgets-home/widgets-home.controller.js | 284 + .../widgets-home/widgets-home.controller.spec.js | 1 + .../app/views/home/widgets-home/widgets-home.less | 137 + .../views/home/widgets-home/widgets-home.tpl.html | 123 + .../new-portal-admin.controller.js | 85 + .../new-portal-admin.controller.spec.js | 19 + .../new-portal-admin/new-portal-admin.modal.html | 32 + .../new-portal-admin/new-portal-admin.modal.less | 99 + .../views/portal-admin/portal-admin-controller.js | 127 + .../app/views/portal-admin/portal-admin.tpl.html | 73 + .../app/views/portal-admin/portal-admins.less | 56 + .../client/app/views/search/search.controller.js | 184 + .../app/views/search/search.controller.spec.js | 19 + .../client/app/views/search/search.less | 55 + .../client/app/views/search/search.tpl.html | 82 + .../client/app/views/sidebar/sidebar.controller.js | 143 + .../client/app/views/sidebar/sidebar.less | 37 + .../client/app/views/sidebar/sidebar.tpl.html | 20 + .../contact-us-manage.controller.js | 194 + .../contact-us-manage.controller.less | 159 + .../contact-us-manage/contact-us-manage.html | 136 + .../support/contact-us/contact-us.controller.js | 166 + .../contact-us/contact-us.controller.spec.js | 19 + .../app/views/support/contact-us/contact-us.less | 104 + .../views/support/contact-us/contact-us.tpl.html | 125 + .../support/get-access/get-access.controller.js | 70 + .../get-access/get-access.controller.spec.js | 19 + .../app/views/support/get-access/get-access.less | 67 + .../views/support/get-access/get-access.tpl.html | 88 + .../client/app/views/tabs/tabframe.html | 22 + .../client/app/views/tabs/tabs.controller.js | 231 + .../client/app/views/tabs/tabs.controller.spec.js | 80 + ecomp-portal-FE/client/app/views/tabs/tabs.less | 658 + .../client/app/views/tabs/tabs.tpl.html | 46 + .../client/app/views/userbar/userbar.controller.js | 262 + .../client/app/views/userbar/userbar.less | 41 + .../client/app/views/userbar/userbar.tpl.html | 20 + .../users/new-user-dialogs/new-user.controller.js | 211 + .../new-user-dialogs/new-user.controller.spec.js | 222 + .../users/new-user-dialogs/new-user.modal.html | 70 + .../users/new-user-dialogs/new-user.modal.less | 126 + .../client/app/views/users/users.controller.js | 187 + .../app/views/users/users.controller.spec.js | 19 + ecomp-portal-FE/client/app/views/users/users.less | 59 + .../client/app/views/users/users.tpl.html | 105 + .../widget-details.controller.js | 202 + .../widget-details.controller.spec.js | 19 + .../widget-details.modal.html | 132 + .../widget-details.modal.less | 94 + .../client/app/views/widgets/widgets.controller.js | 151 + .../app/views/widgets/widgets.controller.spec.js | 19 + .../client/app/views/widgets/widgets.less | 60 + .../client/app/views/widgets/widgets.tpl.html | 77 + ecomp-portal-FE/client/assets/images/cloud.png | Bin 0 -> 132244 bytes .../client/assets/images/ecomp_logo.png | Bin 0 -> 3868 bytes ecomp-portal-FE/client/assets/images/grips.png | Bin 0 -> 951 bytes ecomp-portal-FE/client/assets/images/photo.png | Bin 0 -> 835 bytes ecomp-portal-FE/client/assets/images/spinner.gif | Bin 0 -> 4178 bytes ecomp-portal-FE/client/assets/images/sprite.png | Bin 0 -> 446 bytes .../bower_components/angular-animate/.bower.json | 20 + .../bower_components/angular-animate/README.md | 68 + .../angular-animate/angular-animate.js | 4121 ++ .../angular-animate/angular-animate.min.js | 56 + .../angular-animate/angular-animate.min.js.map | 8 + .../bower_components/angular-animate/bower.json | 10 + .../bower_components/angular-animate/index.js | 2 + .../bower_components/angular-animate/package.json | 26 + .../bower_components/angular-aria/.bower.json | 20 + .../client/bower_components/angular-aria/README.md | 67 + .../bower_components/angular-aria/angular-aria.js | 398 + .../angular-aria/angular-aria.min.js | 14 + .../angular-aria/angular-aria.min.js.map | 8 + .../bower_components/angular-aria/bower.json | 10 + .../client/bower_components/angular-aria/index.js | 2 + .../bower_components/angular-aria/package.json | 27 + .../bower_components/angular-bootstrap/.bower.json | 31 + .../bower_components/angular-bootstrap/.gitignore | 1 + .../bower_components/angular-bootstrap/.npmignore | 1 + .../bower_components/angular-bootstrap/README.md | 120 + .../bower_components/angular-bootstrap/bower.json | 19 + .../bower_components/angular-bootstrap/index.js | 2 + .../angular-bootstrap/package.json | 23 + .../angular-bootstrap/ui-bootstrap-csp.css | 115 + .../angular-bootstrap/ui-bootstrap-tpls.js | 7715 ++++ .../angular-bootstrap/ui-bootstrap-tpls.min.js | 10 + .../angular-bootstrap/ui-bootstrap.js | 7351 +++ .../angular-bootstrap/ui-bootstrap.min.js | 10 + .../bower_components/angular-cache/.bower.json | 45 + .../bower_components/angular-cache/CHANGELOG.md | 353 + .../client/bower_components/angular-cache/LICENSE | 20 + .../bower_components/angular-cache/README.md | 671 + .../bower_components/angular-cache/TRANSITION.md | 98 + .../bower_components/angular-cache/bower.json | 35 + .../build_examples/browserify/README.md | 1 + .../angular-cache/build_examples/browserify/app.js | 11 + .../build_examples/browserify/index.html | 11 + .../angular-cache/build_examples/r.js/README.md | 3 + .../angular-cache/build_examples/r.js/app.js | 9 + .../angular-cache/build_examples/r.js/index.html | 14 + .../angular-cache/build_examples/r.js/main.js | 22 + .../build_examples/r.js/require.config.js | 6 + .../angular-cache/build_examples/webpack/README.md | 1 + .../angular-cache/build_examples/webpack/app.js | 8 + .../build_examples/webpack/index.html | 11 + .../build_examples/webpack/webpack.config.js | 11 + .../build_examples/webpack_es6/README.md | 1 + .../build_examples/webpack_es6/app.js | 8 + .../build_examples/webpack_es6/index.html | 11 + .../build_examples/webpack_es6/webpack.config.js | 16 + .../build_examples/webpack_es6_2/README.md | 1 + .../build_examples/webpack_es6_2/app.js | 8 + .../build_examples/webpack_es6_2/index.html | 11 + .../build_examples/webpack_es6_2/webpack.config.js | 16 + .../bower_components/angular-cache/circle.yml | 6 + .../angular-cache/dist/angular-cache.js | 1111 + .../angular-cache/dist/angular-cache.js.map | 1 + .../angular-cache/dist/angular-cache.min.js | 2 + .../angular-cache/dist/angular-cache.min.map | 1 + .../angular-cache/rollup.config.js | 22 + .../angular-cache/scripts/postbuild.js | 21 + .../bower_components/angular-cookies/.bower.json | 20 + .../bower_components/angular-cookies/README.md | 68 + .../angular-cookies/angular-cookies.js | 322 + .../angular-cookies/angular-cookies.min.js | 9 + .../angular-cookies/angular-cookies.min.js.map | 8 + .../bower_components/angular-cookies/bower.json | 10 + .../bower_components/angular-cookies/index.js | 2 + .../bower_components/angular-cookies/package.json | 26 + .../bower_components/angular-gestures/.bower.json | 32 + .../bower_components/angular-gestures/LICENSE.md | 21 + .../bower_components/angular-gestures/README.md | 82 + .../bower_components/angular-gestures/bower.json | 23 + .../bower_components/angular-gestures/gestures.js | 158 + .../angular-gestures/gestures.min.js | 1 + .../bower_components/angular-material/.bower.json | 24 + .../bower_components/angular-material/.gitignore | 5 + .../bower_components/angular-material/CHANGELOG.md | 1543 + .../bower_components/angular-material/LICENSE | 21 + .../bower_components/angular-material/README.md | 240 + .../angular-material/angular-material-mocks.js | 77 + .../angular-material/angular-material.css | 6795 +++ .../angular-material/angular-material.js | 13909 ++++++ .../angular-material/angular-material.min.css | 6 + .../angular-material/angular-material.min.js | 15 + .../bower_components/angular-material/bower.json | 14 + .../bottomSheet/demoBasicUsage/img/icons/copy.svg | 1 + .../bottomSheet/demoBasicUsage/img/icons/copy2.svg | 1 + .../demoBasicUsage/img/icons/facebook.svg | 1 + .../demoBasicUsage/img/icons/hangout.svg | 1 + .../bottomSheet/demoBasicUsage/img/icons/mail.svg | 1 + .../demoBasicUsage/img/icons/message.svg | 1 + .../bottomSheet/demoBasicUsage/img/icons/print.svg | 1 + .../demoBasicUsage/img/icons/share-arrow.svg | 1 + .../demoBasicUsage/img/icons/twitter.svg | 2 + .../demoBasicUsage/img/icons/upload.svg | 1 + .../demos/chips/demoBasicUsage/style.scss | 39 + .../demos/chips/demoContactChips/style.scss | 47 + .../demos/gridList/demoBasicUsage/style.scss | 75 + .../demos/gridList/demoDynamicTiles/style.scss | 78 + .../demos/gridList/demoResponsiveUsage/style.scss | 0 .../img/icons/addShoppingCart.svg | 1 + .../demoLoadSvgIconsFromUrl/img/icons/android.svg | 1 + .../demoLoadSvgIconsFromUrl/img/icons/cake.svg | 1 + .../demos/icon/demoSvgIconSets/assets/cake.svg | 1 + .../icon/demoSvgIconSets/assets/core-icons.svg | 26 + .../icon/demoSvgIconSets/assets/social-icons.svg | 26 + .../img/icons/addShoppingCart.svg | 1 + .../icon/demoSvgIconsFromURL/img/icons/android.svg | 1 + .../icon/demoSvgIconsFromURL/img/icons/cake.svg | 1 + .../icon/demoUsingTemplateCache/assets/android.svg | 1 + .../icon/demoUsingTemplateCache/assets/cake.svg | 1 + .../demoUsingTemplateCache/assets/core-icons.svg | 26 + .../demos/input/demoIcons/icons/ic_email_24px.svg | 4 + .../demos/input/demoIcons/icons/ic_person_24px.svg | 4 + .../demos/input/demoIcons/icons/ic_phone_24px.svg | 4 + .../demos/input/demoIcons/icons/ic_place_24px.svg | 4 + .../demos/input/demoIcons/style.scss | 18 + .../demos/list/demoListControls/img/100-0.jpeg | Bin 0 -> 2331 bytes .../demos/list/demoListControls/img/100-1.jpeg | Bin 0 -> 2816 bytes .../demos/list/demoListControls/img/100-2.jpeg | Bin 0 -> 3389 bytes .../demos/tabs/demoDynamicHeight/style.scss | 13 + .../demos/tabs/demoDynamicTabs/style.scss | 44 + .../demos/tabs/demoStaticTabs/style.scss | 25 + .../toolbar/demoBasicUsage/img/icons/favorite.svg | 4 + .../toolbar/demoBasicUsage/img/icons/menu.svg | 4 + .../toolbar/demoBasicUsage/img/icons/more_vert.svg | 4 + .../bower_components/angular-material/index.js | 12 + .../autocomplete/autocomplete-default-theme.css | 24 + .../modules/closure/autocomplete/autocomplete.css | 220 + .../modules/closure/autocomplete/autocomplete.js | 794 + .../closure/backdrop/backdrop-default-theme.css | 9 + .../modules/closure/backdrop/backdrop.css | 59 + .../modules/closure/backdrop/backdrop.js | 38 + .../bottomSheet/bottomSheet-default-theme.css | 16 + .../modules/closure/bottomSheet/bottomSheet.css | 170 + .../modules/closure/bottomSheet/bottomSheet.js | 267 + .../closure/button/button-default-theme.css | 94 + .../modules/closure/button/button.css | 158 + .../modules/closure/button/button.js | 135 + .../modules/closure/card/card-default-theme.css | 12 + .../angular-material/modules/closure/card/card.css | 34 + .../angular-material/modules/closure/card/card.js | 84 + .../closure/checkbox/checkbox-default-theme.css | 47 + .../modules/closure/checkbox/checkbox.css | 125 + .../modules/closure/checkbox/checkbox.js | 166 + .../modules/closure/chips/chips-default-theme.css | 24 + .../modules/closure/chips/chips.css | 131 + .../modules/closure/chips/chips.js | 949 + .../closure/content/content-default-theme.css | 9 + .../modules/closure/content/content.css | 20 + .../modules/closure/content/content.js | 84 + .../angular-material/modules/closure/core/core.css | 2590 ++ .../angular-material/modules/closure/core/core.js | 3779 ++ .../modules/closure/core/default-theme.js | 4 + .../closure/dialog/dialog-default-theme.css | 12 + .../modules/closure/dialog/dialog.css | 111 + .../modules/closure/dialog/dialog.js | 713 + .../closure/divider/divider-default-theme.css | 9 + .../modules/closure/divider/divider.css | 14 + .../modules/closure/divider/divider.js | 45 + .../closure/gridList/gridList-default-theme.css | 6 + .../modules/closure/gridList/gridList.css | 68 + .../modules/closure/gridList/gridList.js | 762 + .../modules/closure/icon/icon-default-theme.css | 15 + .../angular-material/modules/closure/icon/icon.css | 20 + .../angular-material/modules/closure/icon/icon.js | 787 + .../modules/closure/input/input-default-theme.css | 46 + .../modules/closure/input/input.css | 162 + .../modules/closure/input/input.js | 371 + .../modules/closure/list/list-default-theme.css | 23 + .../angular-material/modules/closure/list/list.css | 147 + .../angular-material/modules/closure/list/list.js | 273 + .../angular-material/modules/closure/menu/menu.css | 6 + .../angular-material/modules/closure/menu/menu.js | 29 + .../progressCircular-default-theme.css | 36 + .../closure/progressCircular/progressCircular.css | 1306 + .../closure/progressCircular/progressCircular.js | 109 + .../progressLinear-default-theme.css | 27 + .../closure/progressLinear/progressLinear.css | 287 + .../closure/progressLinear/progressLinear.js | 126 + .../radioButton/radioButton-default-theme.css | 47 + .../modules/closure/radioButton/radioButton.css | 90 + .../modules/closure/radioButton/radioButton.js | 312 + .../closure/select/select-default-theme.css | 42 + .../modules/closure/select/select.css | 168 + .../modules/closure/select/select.js | 1056 + .../closure/sidenav/sidenav-default-theme.css | 9 + .../modules/closure/sidenav/sidenav.css | 99 + .../modules/closure/sidenav/sidenav.js | 424 + .../closure/slider/slider-default-theme.css | 56 + .../modules/closure/slider/slider.css | 220 + .../modules/closure/slider/slider.js | 403 + .../modules/closure/sticky/sticky.css | 21 + .../modules/closure/sticky/sticky.js | 315 + .../closure/subheader/subheader-default-theme.css | 16 + .../modules/closure/subheader/subheader.css | 63 + .../modules/closure/subheader/subheader.js | 93 + .../modules/closure/swipe/swipe.js | 72 + .../closure/switch/switch-default-theme.css | 33 + .../modules/closure/switch/switch.css | 110 + .../modules/closure/switch/switch.js | 169 + .../modules/closure/tabs/tabs-arrow.svg | 7 + .../modules/closure/tabs/tabs-default-theme.css | 82 + .../angular-material/modules/closure/tabs/tabs.css | 272 + .../angular-material/modules/closure/tabs/tabs.js | 867 + .../closure/textField/textField-default-theme.css | 30 + .../modules/closure/textField/textField.css | 111 + .../modules/closure/textField/textField.js | 145 + .../modules/closure/toast/toast-default-theme.css | 18 + .../modules/closure/toast/toast.css | 119 + .../modules/closure/toast/toast.js | 265 + .../closure/toolbar/toolbar-default-theme.css | 20 + .../modules/closure/toolbar/toolbar.css | 83 + .../modules/closure/toolbar/toolbar.js | 169 + .../closure/tooltip/tooltip-default-theme.css | 11 + .../modules/closure/tooltip/tooltip.css | 72 + .../modules/closure/tooltip/tooltip.js | 267 + .../modules/closure/whiteframe/whiteframe.css | 25 + .../modules/closure/whiteframe/whiteframe.js | 15 + .../modules/css/angular-material-layout.css | 2375 + .../js/autocomplete/autocomplete-default-theme.css | 24 + .../autocomplete-default-theme.min.css | 6 + .../modules/js/autocomplete/autocomplete.css | 220 + .../modules/js/autocomplete/autocomplete.js | 794 + .../modules/js/autocomplete/autocomplete.min.css | 6 + .../modules/js/autocomplete/autocomplete.min.js | 7 + .../modules/js/autocomplete/bower.json | 8 + .../modules/js/backdrop/backdrop-default-theme.css | 9 + .../js/backdrop/backdrop-default-theme.min.css | 6 + .../modules/js/backdrop/backdrop.css | 59 + .../modules/js/backdrop/backdrop.js | 39 + .../modules/js/backdrop/backdrop.min.css | 6 + .../modules/js/backdrop/backdrop.min.js | 7 + .../modules/js/backdrop/bower.json | 7 + .../js/bottomSheet/bottomSheet-default-theme.css | 16 + .../bottomSheet/bottomSheet-default-theme.min.css | 6 + .../modules/js/bottomSheet/bottomSheet.css | 170 + .../modules/js/bottomSheet/bottomSheet.js | 267 + .../modules/js/bottomSheet/bottomSheet.min.css | 6 + .../modules/js/bottomSheet/bottomSheet.min.js | 7 + .../modules/js/bottomSheet/bower.json | 8 + .../angular-material/modules/js/button/bower.json | 7 + .../modules/js/button/button-default-theme.css | 94 + .../modules/js/button/button-default-theme.min.css | 6 + .../angular-material/modules/js/button/button.css | 158 + .../angular-material/modules/js/button/button.js | 136 + .../modules/js/button/button.min.css | 6 + .../modules/js/button/button.min.js | 7 + .../angular-material/modules/js/card/bower.json | 7 + .../modules/js/card/card-default-theme.css | 12 + .../modules/js/card/card-default-theme.min.css | 6 + .../angular-material/modules/js/card/card.css | 34 + .../angular-material/modules/js/card/card.js | 85 + .../angular-material/modules/js/card/card.min.css | 6 + .../angular-material/modules/js/card/card.min.js | 7 + .../modules/js/checkbox/bower.json | 7 + .../modules/js/checkbox/checkbox-default-theme.css | 47 + .../js/checkbox/checkbox-default-theme.min.css | 6 + .../modules/js/checkbox/checkbox.css | 125 + .../modules/js/checkbox/checkbox.js | 167 + .../modules/js/checkbox/checkbox.min.css | 6 + .../modules/js/checkbox/checkbox.min.js | 7 + .../angular-material/modules/js/chips/bower.json | 8 + .../modules/js/chips/chips-default-theme.css | 24 + .../modules/js/chips/chips-default-theme.min.css | 6 + .../angular-material/modules/js/chips/chips.css | 131 + .../angular-material/modules/js/chips/chips.js | 949 + .../modules/js/chips/chips.min.css | 6 + .../angular-material/modules/js/chips/chips.min.js | 7 + .../angular-material/modules/js/content/bower.json | 7 + .../modules/js/content/content-default-theme.css | 9 + .../js/content/content-default-theme.min.css | 6 + .../modules/js/content/content.css | 20 + .../angular-material/modules/js/content/content.js | 85 + .../modules/js/content/content.min.css | 6 + .../modules/js/content/content.min.js | 7 + .../angular-material/modules/js/core/bower.json | 8 + .../angular-material/modules/js/core/core.css | 2590 ++ .../angular-material/modules/js/core/core.js | 3779 ++ .../angular-material/modules/js/core/core.min.css | 6 + .../angular-material/modules/js/core/core.min.js | 10 + .../modules/js/core/default-theme.js | 4 + .../angular-material/modules/js/dialog/bower.json | 8 + .../modules/js/dialog/dialog-default-theme.css | 12 + .../modules/js/dialog/dialog-default-theme.min.css | 6 + .../angular-material/modules/js/dialog/dialog.css | 111 + .../angular-material/modules/js/dialog/dialog.js | 713 + .../modules/js/dialog/dialog.min.css | 6 + .../modules/js/dialog/dialog.min.js | 7 + .../angular-material/modules/js/divider/bower.json | 7 + .../modules/js/divider/divider-default-theme.css | 9 + .../js/divider/divider-default-theme.min.css | 6 + .../modules/js/divider/divider.css | 14 + .../angular-material/modules/js/divider/divider.js | 46 + .../modules/js/divider/divider.min.css | 6 + .../modules/js/divider/divider.min.js | 7 + .../modules/js/gridList/bower.json | 7 + .../modules/js/gridList/gridList.css | 68 + .../modules/js/gridList/gridList.js | 763 + .../modules/js/gridList/gridList.min.css | 6 + .../modules/js/gridList/gridList.min.js | 7 + .../angular-material/modules/js/icon/bower.json | 7 + .../modules/js/icon/icon-default-theme.css | 15 + .../modules/js/icon/icon-default-theme.min.css | 6 + .../angular-material/modules/js/icon/icon.css | 20 + .../angular-material/modules/js/icon/icon.js | 788 + .../angular-material/modules/js/icon/icon.min.css | 6 + .../angular-material/modules/js/icon/icon.min.js | 7 + .../angular-material/modules/js/input/bower.json | 7 + .../modules/js/input/input-default-theme.css | 46 + .../modules/js/input/input-default-theme.min.css | 6 + .../angular-material/modules/js/input/input.css | 162 + .../angular-material/modules/js/input/input.js | 372 + .../modules/js/input/input.min.css | 6 + .../angular-material/modules/js/input/input.min.js | 7 + .../angular-material/modules/js/list/bower.json | 7 + .../modules/js/list/list-default-theme.css | 23 + .../modules/js/list/list-default-theme.min.css | 6 + .../angular-material/modules/js/list/list.css | 147 + .../angular-material/modules/js/list/list.js | 274 + .../angular-material/modules/js/list/list.min.css | 6 + .../angular-material/modules/js/list/list.min.js | 7 + .../angular-material/modules/js/menu/bower.json | 5 + .../angular-material/modules/js/menu/menu.css | 6 + .../angular-material/modules/js/menu/menu.js | 27 + .../angular-material/modules/js/menu/menu.min.css | 6 + .../angular-material/modules/js/menu/menu.min.js | 7 + .../modules/js/progressCircular/bower.json | 7 + .../progressCircular-default-theme.css | 36 + .../progressCircular-default-theme.min.css | 6 + .../js/progressCircular/progressCircular.css | 1306 + .../js/progressCircular/progressCircular.js | 110 + .../js/progressCircular/progressCircular.min.css | 6 + .../js/progressCircular/progressCircular.min.js | 7 + .../modules/js/progressLinear/bower.json | 7 + .../progressLinear-default-theme.css | 27 + .../progressLinear-default-theme.min.css | 6 + .../modules/js/progressLinear/progressLinear.css | 287 + .../modules/js/progressLinear/progressLinear.js | 127 + .../js/progressLinear/progressLinear.min.css | 6 + .../js/progressLinear/progressLinear.min.js | 7 + .../modules/js/radioButton/bower.json | 7 + .../js/radioButton/radioButton-default-theme.css | 47 + .../radioButton/radioButton-default-theme.min.css | 6 + .../modules/js/radioButton/radioButton.css | 90 + .../modules/js/radioButton/radioButton.js | 313 + .../modules/js/radioButton/radioButton.min.css | 6 + .../modules/js/radioButton/radioButton.min.js | 7 + .../angular-material/modules/js/select/bower.json | 8 + .../modules/js/select/select-default-theme.css | 42 + .../modules/js/select/select-default-theme.min.css | 6 + .../angular-material/modules/js/select/select.css | 168 + .../angular-material/modules/js/select/select.js | 1056 + .../modules/js/select/select.min.css | 6 + .../modules/js/select/select.min.js | 7 + .../angular-material/modules/js/sidenav/bower.json | 8 + .../modules/js/sidenav/sidenav-default-theme.css | 9 + .../js/sidenav/sidenav-default-theme.min.css | 6 + .../modules/js/sidenav/sidenav.css | 99 + .../angular-material/modules/js/sidenav/sidenav.js | 424 + .../modules/js/sidenav/sidenav.min.css | 6 + .../modules/js/sidenav/sidenav.min.js | 7 + .../angular-material/modules/js/slider/bower.json | 7 + .../modules/js/slider/slider-default-theme.css | 56 + .../modules/js/slider/slider-default-theme.min.css | 6 + .../angular-material/modules/js/slider/slider.css | 220 + .../angular-material/modules/js/slider/slider.js | 404 + .../modules/js/slider/slider.min.css | 6 + .../modules/js/slider/slider.min.js | 7 + .../angular-material/modules/js/sticky/bower.json | 8 + .../angular-material/modules/js/sticky/sticky.css | 21 + .../angular-material/modules/js/sticky/sticky.js | 315 + .../modules/js/sticky/sticky.min.css | 6 + .../modules/js/sticky/sticky.min.js | 7 + .../modules/js/subheader/bower.json | 8 + .../js/subheader/subheader-default-theme.css | 16 + .../js/subheader/subheader-default-theme.min.css | 6 + .../modules/js/subheader/subheader.css | 63 + .../modules/js/subheader/subheader.js | 93 + .../modules/js/subheader/subheader.min.css | 6 + .../modules/js/subheader/subheader.min.js | 7 + .../angular-material/modules/js/swipe/bower.json | 7 + .../angular-material/modules/js/swipe/swipe.js | 73 + .../angular-material/modules/js/swipe/swipe.min.js | 7 + .../angular-material/modules/js/switch/bower.json | 8 + .../modules/js/switch/switch-default-theme.css | 33 + .../modules/js/switch/switch-default-theme.min.css | 6 + .../angular-material/modules/js/switch/switch.css | 110 + .../angular-material/modules/js/switch/switch.js | 169 + .../modules/js/switch/switch.min.css | 6 + .../modules/js/switch/switch.min.js | 7 + .../angular-material/modules/js/tabs/bower.json | 8 + .../modules/js/tabs/tabs-default-theme.css | 82 + .../modules/js/tabs/tabs-default-theme.min.css | 6 + .../angular-material/modules/js/tabs/tabs.css | 272 + .../angular-material/modules/js/tabs/tabs.js | 867 + .../angular-material/modules/js/tabs/tabs.min.css | 6 + .../angular-material/modules/js/tabs/tabs.min.js | 7 + .../modules/js/textField/bower.json | 7 + .../js/textField/textField-default-theme.css | 30 + .../js/textField/textField-default-theme.min.css | 6 + .../modules/js/textField/textField.css | 111 + .../modules/js/textField/textField.js | 143 + .../modules/js/textField/textField.min.css | 6 + .../modules/js/textField/textField.min.js | 7 + .../angular-material/modules/js/toast/bower.json | 8 + .../modules/js/toast/toast-default-theme.css | 18 + .../modules/js/toast/toast-default-theme.min.css | 6 + .../angular-material/modules/js/toast/toast.css | 119 + .../angular-material/modules/js/toast/toast.js | 265 + .../modules/js/toast/toast.min.css | 6 + .../angular-material/modules/js/toast/toast.min.js | 7 + .../angular-material/modules/js/toolbar/bower.json | 8 + .../modules/js/toolbar/toolbar-default-theme.css | 20 + .../js/toolbar/toolbar-default-theme.min.css | 6 + .../modules/js/toolbar/toolbar.css | 83 + .../angular-material/modules/js/toolbar/toolbar.js | 169 + .../modules/js/toolbar/toolbar.min.css | 6 + .../modules/js/toolbar/toolbar.min.js | 7 + .../angular-material/modules/js/tooltip/bower.json | 7 + .../modules/js/tooltip/tooltip-default-theme.css | 11 + .../js/tooltip/tooltip-default-theme.min.css | 6 + .../modules/js/tooltip/tooltip.css | 72 + .../angular-material/modules/js/tooltip/tooltip.js | 268 + .../modules/js/tooltip/tooltip.min.css | 6 + .../modules/js/tooltip/tooltip.min.js | 7 + .../modules/js/whiteframe/bower.json | 5 + .../modules/js/whiteframe/whiteframe.css | 25 + .../modules/js/whiteframe/whiteframe.js | 16 + .../modules/js/whiteframe/whiteframe.min.css | 6 + .../modules/js/whiteframe/whiteframe.min.js | 7 + .../bower_components/angular-material/package.json | 48 + .../bower_components/angular-messages/.bower.json | 20 + .../bower_components/angular-messages/LICENSE.md | 21 + .../bower_components/angular-messages/README.md | 68 + .../angular-messages/angular-messages.js | 739 + .../angular-messages/angular-messages.min.js | 12 + .../angular-messages/angular-messages.min.js.map | 8 + .../bower_components/angular-messages/bower.json | 10 + .../bower_components/angular-messages/index.js | 2 + .../bower_components/angular-messages/package.json | 32 + .../bower_components/angular-mocks/.bower.json | 20 + .../bower_components/angular-mocks/LICENSE.md | 21 + .../bower_components/angular-mocks/README.md | 63 + .../angular-mocks/angular-mocks.js | 3170 ++ .../bower_components/angular-mocks/bower.json | 10 + .../angular-mocks/ngAnimateMock.js | 2 + .../bower_components/angular-mocks/ngMock.js | 2 + .../bower_components/angular-mocks/ngMockE2E.js | 2 + .../bower_components/angular-mocks/package.json | 34 + .../bower_components/angular-resource/.bower.json | 20 + .../bower_components/angular-resource/README.md | 68 + .../angular-resource/angular-resource.js | 768 + .../angular-resource/angular-resource.min.js | 15 + .../angular-resource/angular-resource.min.js.map | 8 + .../bower_components/angular-resource/bower.json | 10 + .../bower_components/angular-resource/index.js | 2 + .../bower_components/angular-resource/package.json | 26 + .../bower_components/angular-route/.bower.json | 20 + .../bower_components/angular-route/README.md | 68 + .../angular-route/angular-route.js | 1016 + .../angular-route/angular-route.min.js | 15 + .../angular-route/angular-route.min.js.map | 8 + .../bower_components/angular-route/bower.json | 10 + .../client/bower_components/angular-route/index.js | 2 + .../bower_components/angular-route/package.json | 26 + .../bower_components/angular-sanitize/.bower.json | 20 + .../bower_components/angular-sanitize/README.md | 68 + .../angular-sanitize/angular-sanitize.js | 717 + .../angular-sanitize/angular-sanitize.min.js | 15 + .../angular-sanitize/angular-sanitize.min.js.map | 8 + .../bower_components/angular-sanitize/bower.json | 10 + .../bower_components/angular-sanitize/index.js | 2 + .../bower_components/angular-sanitize/package.json | 26 + .../bower_components/angular-scenario/.bower.json | 20 + .../bower_components/angular-scenario/LICENSE.md | 21 + .../bower_components/angular-scenario/README.md | 61 + .../angular-scenario/angular-scenario.js | 44994 +++++++++++++++++++ .../bower_components/angular-scenario/bower.json | 10 + .../jstd-scenario-adapter-config.js | 6 + .../angular-scenario/jstd-scenario-adapter.js | 185 + .../bower_components/angular-scenario/package.json | 33 + .../angular-smart-table/.bower.json | 38 + .../angular-smart-table/.gitignore | 7 + .../angular-smart-table/.travis.yml | 12 + .../angular-smart-table/ISSUE_TEMPLATE.md | 15 + .../angular-smart-table/bower.json | 29 + .../angular-smart-table/changeLog.md | 175 + .../angular-smart-table/dist/smart-table.js | 534 + .../angular-smart-table/dist/smart-table.min.js | 6 + .../dist/smart-table.min.js.map | 1 + .../angular-smart-table/gulpFile.js | 60 + .../bower_components/angular-smart-table/index.js | 2 + .../angular-smart-table/package.json | 29 + .../bower_components/angular-smart-table/readme.md | 72 + .../angular-smart-table/src/bottom.txt | 1 + .../angular-smart-table/src/smart-table.module.js | 7 + .../angular-smart-table/src/stConfig.js | 26 + .../angular-smart-table/src/stPagination.js | 80 + .../angular-smart-table/src/stPipe.js | 36 + .../angular-smart-table/src/stSearch.js | 43 + .../angular-smart-table/src/stSelectRow.js | 26 + .../angular-smart-table/src/stSort.js | 82 + .../angular-smart-table/src/stTable.js | 219 + .../angular-smart-table/src/top.txt | 2 + .../bower_components/angular-ui-router/.bower.json | 34 + .../angular-ui-router/CHANGELOG.md | 316 + .../angular-ui-router/CONTRIBUTING.md | 65 + .../bower_components/angular-ui-router/LICENSE | 21 + .../bower_components/angular-ui-router/README.md | 252 + .../bower_components/angular-ui-router/bower.json | 24 + .../angular-ui-router/release/angular-ui-router.js | 4539 ++ .../release/angular-ui-router.min.js | 8 + .../angular-ui-router/src/common.js | 293 + .../angular-ui-router/src/resolve.js | 252 + .../angular-ui-router/src/state.js | 1470 + .../angular-ui-router/src/stateDirectives.js | 391 + .../angular-ui-router/src/stateFilters.js | 39 + .../angular-ui-router/src/templateFactory.js | 110 + .../angular-ui-router/src/urlMatcherFactory.js | 1081 + .../angular-ui-router/src/urlRouter.js | 431 + .../bower_components/angular-ui-router/src/view.js | 45 + .../angular-ui-router/src/viewDirective.js | 351 + .../angular-ui-router/src/viewScroll.js | 52 + .../bower_components/angular-uuid/.bower.json | 23 + .../client/bower_components/angular-uuid/LICENCE | 19 + .../bower_components/angular-uuid/bower.json | 13 + .../client/bower_components/angular-uuid/uuid.js | 21 + .../bower_components/angular-uuid/uuid.min.js | 7 + .../client/bower_components/angular/.bower.json | 18 + .../client/bower_components/angular/README.md | 64 + .../bower_components/angular/angular-csp.css | 21 + .../client/bower_components/angular/angular.js | 30428 +++++++++++++ .../client/bower_components/angular/angular.min.js | 307 + .../bower_components/angular/angular.min.js.gzip | Bin 0 -> 54462 bytes .../bower_components/angular/angular.min.js.map | 8 + .../client/bower_components/angular/bower.json | 9 + .../client/bower_components/angular/index.js | 2 + .../client/bower_components/angular/package.json | 25 + .../client/bower_components/es5-shim/.bower.json | 43 + .../client/bower_components/es5-shim/CHANGES | 404 + .../bower_components/es5-shim/CONTRIBUTORS.md | 27 + .../client/bower_components/es5-shim/LICENSE | 22 + .../client/bower_components/es5-shim/Makefile | 63 + .../client/bower_components/es5-shim/README.md | 190 + .../client/bower_components/es5-shim/bower.json | 33 + .../bower_components/es5-shim/component.json | 19 + .../client/bower_components/es5-shim/es5-sham.js | 563 + .../client/bower_components/es5-shim/es5-sham.map | 1 + .../bower_components/es5-shim/es5-sham.min.js | 7 + .../client/bower_components/es5-shim/es5-shim.js | 2065 + .../client/bower_components/es5-shim/es5-shim.map | 1 + .../bower_components/es5-shim/es5-shim.min.js | 7 + .../client/bower_components/es5-shim/package.json | 75 + .../client/bower_components/es5-shim/shims.json | 7 + .../client/bower_components/hammerjs/.bower.json | 27 + .../client/bower_components/hammerjs/CHANGELOG.md | 54 + .../bower_components/hammerjs/CONTRIBUTING.md | 41 + .../client/bower_components/hammerjs/LICENSE.md | 21 + .../client/bower_components/hammerjs/README.md | 51 + .../client/bower_components/hammerjs/bower.json | 16 + .../client/bower_components/hammerjs/changelog.js | 71 + .../client/bower_components/hammerjs/hammer.js | 2643 ++ .../client/bower_components/hammerjs/hammer.min.js | 7 + .../bower_components/hammerjs/hammer.min.js.map | 1 + .../bower_components/hammerjs/hammer.min.map | 1 + .../client/bower_components/jqTree/.bower.json | 28 + .../client/bower_components/jqTree/.editorconfig | 12 + .../client/bower_components/jqTree/.gitignore | 2 + .../client/bower_components/jqTree/.travis.yml | 12 + .../client/bower_components/jqTree/LICENSE | 202 + .../client/bower_components/jqTree/README.md | 63 + .../client/bower_components/jqTree/_config.yml | 18 + .../bower_components/jqTree/_entries/01_general.md | 7 + .../jqTree/_entries/02_introduction.md | 9 + .../jqTree/_entries/03_features.md | 15 + .../bower_components/jqTree/_entries/04_demo.html | 34 + .../jqTree/_entries/05_requirements.md | 6 + .../jqTree/_entries/06_downloads.md | 9 + .../jqTree/_entries/07_tutorial.md | 77 + .../jqTree/_entries/08_examples.md | 8 + .../jqTree/_entries/09_usecases.md | 17 + .../jqTree/_entries/10_changelog.md | 233 + .../bower_components/jqTree/_entries/11_options.md | 6 + .../jqTree/_entries/12_autoescape.md | 6 + .../jqTree/_entries/13_autoopen.md | 28 + .../jqTree/_entries/14_buttonleft.md | 12 + .../jqTree/_entries/15_closedicon.md | 12 + .../bower_components/jqTree/_entries/16_data.md | 46 + .../jqTree/_entries/17_datafilter.md | 17 + .../jqTree/_entries/18_data-url.md | 45 + .../jqTree/_entries/19_draganddrop.md | 18 + .../jqTree/_entries/20_keyboardsupport.md | 14 + .../jqTree/_entries/21_oncanmove.md | 22 + .../jqTree/_entries/22_oncanmoveto.md | 22 + .../jqTree/_entries/23_oncanselectnode.md | 26 + .../jqTree/_entries/24_oncreateli.md | 16 + .../jqTree/_entries/25_onismovehandle.md | 17 + .../jqTree/_entries/26_onloadfailed.md | 15 + .../jqTree/_entries/27_onloading.md | 22 + .../jqTree/_entries/28_openedicon.md | 12 + .../jqTree/_entries/29_openfolderdelay.md | 13 + .../bower_components/jqTree/_entries/30_rtl.md | 18 + .../jqTree/_entries/31_savestate.md | 42 + .../jqTree/_entries/32_selectable.md | 18 + .../bower_components/jqTree/_entries/33_slide.md | 12 + .../jqTree/_entries/34_usecontextmenu.md | 14 + .../jqTree/_entries/35_functions.md | 6 + .../jqTree/_entries/36_addnodeafter.md | 20 + .../jqTree/_entries/36a_addparentnode.md | 20 + .../jqTree/_entries/37_addnodebefore.md | 8 + .../jqTree/_entries/38_appendnode.md | 50 + .../jqTree/_entries/39_closenode.md | 23 + .../bower_components/jqTree/_entries/40_destroy.md | 12 + .../jqTree/_entries/41_getnodebycallback.md | 24 + .../jqTree/_entries/42_getnodebyid.md | 21 + .../jqTree/_entries/43_getnodebyhtmlelement.md | 29 + .../jqTree/_entries/44_getselectednode.md | 10 + .../jqTree/_entries/45_getstate.md | 19 + .../bower_components/jqTree/_entries/46_gettree.md | 12 + .../jqTree/_entries/47_loaddata.md | 46 + .../jqTree/_entries/48_loaddatafromurl.md | 55 + .../jqTree/_entries/49_movedown.md | 8 + .../jqTree/_entries/50_movenode.md | 15 + .../bower_components/jqTree/_entries/51_moveup.md | 8 + .../jqTree/_entries/52_opennode.md | 44 + .../bower_components/jqTree/_entries/53_reload.md | 22 + .../jqTree/_entries/54_removenode.md | 12 + .../jqTree/_entries/55_selectnode.md | 24 + .../jqTree/_entries/56_scrolltonode.md | 13 + .../jqTree/_entries/57_setoption.md | 12 + .../jqTree/_entries/58_setstate.md | 8 + .../bower_components/jqTree/_entries/59_toggle.md | 8 + .../bower_components/jqTree/_entries/60_tojson.md | 13 + .../jqTree/_entries/61_updatenode.md | 50 + .../bower_components/jqTree/_entries/62_events.md | 6 + .../jqTree/_entries/63_tree-click.md | 37 + .../jqTree/_entries/64_tree-close.md | 15 + .../jqTree/_entries/65_tree-contextmenu.md | 21 + .../jqTree/_entries/66_tree-dblclick.md | 19 + .../jqTree/_entries/67_tree-init.md | 15 + .../jqTree/_entries/68_tree-move.md | 73 + .../jqTree/_entries/69_tree-open.md | 15 + .../jqTree/_entries/70_tree-select.md | 28 + .../jqTree/_entries/71_multiple-selection.md | 9 + .../jqTree/_entries/72_add-to-selection.md | 11 + .../jqTree/_entries/73_get-selected-nodes.md | 10 + .../jqTree/_entries/74_is-node-selected.md | 11 + .../jqTree/_entries/75_remove-from-selection.md | 11 + .../jqTree/_entries/76_node-functions.md | 13 + .../jqTree/_entries/77_children.md | 12 + .../bower_components/jqTree/_entries/78_getdata.md | 17 + .../jqTree/_entries/79_getlevel.md | 13 + .../jqTree/_entries/80_getnextnode.md | 12 + .../jqTree/_entries/81_getnextsibling.md | 10 + .../jqTree/_entries/82_getpreviousnode.md | 12 + .../jqTree/_entries/83_getprevioussibling.md | 10 + .../bower_components/jqTree/_entries/84_parent.md | 10 + .../bower_components/jqTree/_entries/insert.py | 46 + .../bower_components/jqTree/_entries/renumber.py | 38 + .../jqTree/_examples/01_load_json_data.html | 47 + .../_examples/02_load_json_data_from_server.html | 31 + .../jqTree/_examples/03_drag_and_drop.html | 44 + .../jqTree/_examples/04_save_state.html | 52 + .../jqTree/_examples/05_load_on_demand.html | 70 + .../jqTree/_examples/06_autoescape.html | 47 + .../jqTree/_examples/07_autoscroll.html | 49 + .../jqTree/_examples/08_multiple_select.html | 72 + .../jqTree/_examples/09_custom_html.html | 68 + .../jqTree/_examples/10_icon_buttons.html | 28 + .../jqTree/_examples/11_right-to-left.html | 27 + .../jqTree/_examples/12_button_on_right.html | 27 + .../bower_components/jqTree/_layouts/base.html | 49 + .../jqTree/_layouts/frontpage.html | 20 + .../bower_components/jqTree/_layouts/page.html | 7 + .../client/bower_components/jqTree/bower.json | 18 + .../client/bower_components/jqTree/coffeelint.json | 122 + .../client/bower_components/jqTree/gulpfile.coffee | 57 + .../client/bower_components/jqTree/gulpfile.js | 2 + .../client/bower_components/jqTree/index.html | 48 + .../bower_components/jqTree/jqtree-circle.png | Bin 0 -> 239 bytes .../client/bower_components/jqTree/jqtree.css | 132 + .../bower_components/jqTree/jqtree.jquery.json | 23 + .../client/bower_components/jqTree/jqtree.scss | 197 + .../client/bower_components/jqTree/karma.conf.js | 59 + .../jqTree/lib/drag_and_drop_handler.js | 489 + .../jqTree/lib/elements_renderer.js | 220 + .../bower_components/jqTree/lib/key_handler.js | 120 + .../bower_components/jqTree/lib/mouse.widget.js | 191 + .../client/bower_components/jqTree/lib/node.js | 588 + .../bower_components/jqTree/lib/node_element.js | 226 + .../jqTree/lib/save_state_handler.js | 238 + .../bower_components/jqTree/lib/scroll_handler.js | 137 + .../jqTree/lib/select_node_handler.js | 111 + .../bower_components/jqTree/lib/simple.widget.js | 125 + .../bower_components/jqTree/lib/tree.jquery.js | 1101 + .../client/bower_components/jqTree/lib/util.js | 49 + .../client/bower_components/jqTree/lib/version.js | 1 + .../client/bower_components/jqTree/package.json | 51 + .../bower_components/jqTree/phantomjs/runner.js | 193 + .../bower_components/jqTree/phantomjs/server.js | 14 + .../client/bower_components/jqTree/screenshot.png | Bin 0 -> 31319 bytes .../client/bower_components/jqTree/sitemap.txt | 13 + .../jqTree/src/drag_and_drop_handler.coffee | 491 + .../jqTree/src/elements_renderer.coffee | 237 + .../client/bower_components/jqTree/src/header.txt | 17 + .../bower_components/jqTree/src/key_handler.coffee | 107 + .../jqTree/src/mouse.widget.coffee | 184 + .../client/bower_components/jqTree/src/node.coffee | 490 + .../jqTree/src/node_element.coffee | 170 + .../jqTree/src/save_state_handler.coffee | 208 + .../jqTree/src/scroll_handler.coffee | 114 + .../jqTree/src/select_node_handler.coffee | 84 + .../jqTree/src/simple.widget.coffee | 107 + .../bower_components/jqTree/src/tree.jquery.coffee | 918 + .../client/bower_components/jqTree/src/util.coffee | 48 + .../bower_components/jqTree/src/version.coffee | 1 + .../bower_components/jqTree/src_test/karma-test.js | 17 + .../bower_components/jqTree/src_test/test.js | 5 + .../jqTree/src_test/test_jqtree.js | 1390 + .../bower_components/jqTree/src_test/test_tree.js | 726 + .../bower_components/jqTree/src_test/test_util.js | 34 + .../jqTree/src_test/utils_for_test.js | 107 + .../bower_components/jqTree/static/bower.json | 16 + .../bower_components/blanket/dist/qunit/blanket.js | 5299 +++ .../bootstrap/dist/css/bootstrap-theme.min.css | 6 + .../bootstrap/dist/css/bootstrap.min.css | 6 + .../dist/fonts/glyphicons-halflings-regular.eot | Bin 0 -> 20127 bytes .../dist/fonts/glyphicons-halflings-regular.svg | 288 + .../dist/fonts/glyphicons-halflings-regular.ttf | Bin 0 -> 45404 bytes .../dist/fonts/glyphicons-halflings-regular.woff | Bin 0 -> 23424 bytes .../dist/fonts/glyphicons-halflings-regular.woff2 | Bin 0 -> 18028 bytes .../bootstrap/dist/js/bootstrap.min.js | 7 + .../fontawesome/css/font-awesome.min.css | 4 + .../fontawesome/fonts/FontAwesome.otf | Bin 0 -> 134808 bytes .../fontawesome/fonts/fontawesome-webfont.eot | Bin 0 -> 165742 bytes .../fontawesome/fonts/fontawesome-webfont.svg | 2671 ++ .../fontawesome/fonts/fontawesome-webfont.ttf | Bin 0 -> 165548 bytes .../fontawesome/fonts/fontawesome-webfont.woff | Bin 0 -> 98024 bytes .../fontawesome/fonts/fontawesome-webfont.woff2 | Bin 0 -> 77160 bytes .../bower_components/jquery-1/dist/jquery.js | 11008 +++++ .../bower_components/jquery-1/dist/jquery.min.js | 5 + .../bower_components/jquery-2/dist/jquery.js | 9814 ++++ .../bower_components/jquery-2/dist/jquery.min.js | 4 + .../jquery-cookie/jquery.cookie.js | 117 + .../jquery-mockjax/dist/jquery.mockjax.js | 1003 + .../static/bower_components/jquery/dist/jquery.js | 10220 +++++ .../bower_components/jquery/dist/jquery.min.js | 4 + .../static/bower_components/json3/lib/json3.js | 902 + .../bower_components/pygments/css/monokai.css | 65 + .../static/bower_components/qunit/qunit/qunit.css | 431 + .../static/bower_components/qunit/qunit/qunit.js | 4366 ++ .../jqTree/static/documentation.css | 171 + .../jqTree/static/documentation.js | 51 + .../bower_components/jqTree/static/example.css | 60 + .../bower_components/jqTree/static/example.scss | 86 + .../bower_components/jqTree/static/example_data.js | 128 + .../jqTree/static/examples/autoescape.js | 19 + .../jqTree/static/examples/autoscroll.js | 8 + .../jqTree/static/examples/button-on-right.js | 15 + .../jqTree/static/examples/custom_html.js | 32 + .../jqTree/static/examples/drag_and_drop.js | 15 + .../jqTree/static/examples/icon_buttons.js | 14 + .../jqTree/static/examples/load_json_data.js | 21 + .../static/examples/load_json_data_from_server.js | 11 + .../jqTree/static/examples/load_on_demand.js | 20 + .../jqTree/static/examples/multiple_select.js | 27 + .../jqTree/static/examples/right-to-left.js | 13 + .../jqTree/static/examples/save_state.js | 13 + .../bower_components/jqTree/static/spinner.gif | Bin 0 -> 673 bytes .../client/bower_components/jqTree/test/test.html | 24 + .../bower_components/jqTree/test/test_build.js | 3267 ++ .../jqTree/test/test_jquery_1.html | 16 + .../jqTree/test/test_jquery_2.html | 16 + .../client/bower_components/jqTree/tree.jquery.js | 3640 ++ .../bower_components/jquery-mousewheel/.bower.json | 27 + .../jquery-mousewheel/ChangeLog.md | 147 + .../bower_components/jquery-mousewheel/LICENSE.txt | 37 + .../bower_components/jquery-mousewheel/README.md | 76 + .../bower_components/jquery-mousewheel/bower.json | 17 + .../jquery-mousewheel/jquery.mousewheel.js | 221 + .../jquery-mousewheel/jquery.mousewheel.min.js | 8 + .../jquery.event.drag-new/.bower.json | 13 + .../event.drag/jquery.event.drag.js | 402 + .../event.drag/jquery.event.drag.live.js | 87 + .../event.drag/test/binding.js | 64 + .../event.drag/test/handlers.js | 160 + .../event.drag/test/index.html | 28 + .../event.drag/test/interaction.js | 198 + .../jquery.event.drag-new/event.drag/test/live.js | 41 + .../event.drag/test/method.js | 64 + .../event.drag/test/properties.js | 71 + .../event.drag/test/requirements.js | 24 + .../jquery.event.drag-new/event.drag/test/touch.js | 206 + .../event.drop/jquery.event.drop.js | 302 + .../event.drop/jquery.event.drop.live.js | 93 + .../event.drop/test/binding.js | 32 + .../event.drop/test/handlers.js | 130 + .../event.drop/test/index.html | 32 + .../event.drop/test/interaction.js | 134 + .../jquery.event.drag-new/event.drop/test/live.js | 60 + .../event.drop/test/method.js | 63 + .../event.drop/test/properties.js | 62 + .../event.drop/test/requirements.js | 37 + .../event.drop/test/tolerance.js | 189 + .../jquery.event.drag-new/event.drop/test/touch.js | 140 + .../event.keyend/jquery.event.keyend.js | 20 + .../event.linger/jquery.event.linger.js | 190 + .../event.linger/test/binding.js | 45 + .../event.linger/test/handlers.js | 111 + .../event.linger/test/index.html | 24 + .../event.linger/test/interaction.js | 231 + .../event.linger/test/method.js | 43 + .../event.linger/test/requirements.js | 23 + .../event.wheel/jquery.event.wheel.js | 50 + .../jquery.event.drag-new/fire/jquery.fire.js | 171 + .../jquery.event.drag-new/fire/test/custom.js | 61 + .../jquery.event.drag-new/fire/test/form.js | 50 + .../jquery.event.drag-new/fire/test/index.html | 26 + .../jquery.event.drag-new/fire/test/key.js | 52 + .../jquery.event.drag-new/fire/test/mouse.js | 54 + .../jquery.event.drag-new/fire/test/mutation.js | 52 + .../jquery.event.drag-new/fire/test/object.js | 50 + .../fire/test/requirements.js | 26 + .../jquery.event.drag-new/fire/test/touch.js | 61 + .../jquery.event.drag-new/jquery/jquery-1.4.2.js | 6240 +++ .../jquery.event.drag-new/jquery/jquery-1.4.4.js | 7179 +++ .../jquery.event.drag-new/jquery/jquery-1.5b1.js | 8068 ++++ .../jquery.event.drag-new/jquery/jquery-1.7.2.js | 9404 ++++ .../jquery.event.drag-new/jquery/jquery.js | 9404 ++++ .../jquery.event.drag-new/jquery/qunit.css | 118 + .../jquery.event.drag-new/jquery/qunit.js | 1069 + .../bower_components/jquery.newstape/.bower.json | 34 + .../bower_components/jquery.newstape/bower.json | 24 + .../jquery.newstape/dist/jquery.newstape.min.js | 1 + .../bower_components/jquery.newstape/gulpfile.js | 22 + .../jquery.newstape/jquery.newstape.js | 135 + .../bower_components/jquery.newstape/package.json | 8 + .../bower_components/jquery.newstape/readme.md | 99 + .../client/bower_components/jquery/.bower.json | 25 + .../client/bower_components/jquery/AUTHORS.txt | 278 + .../client/bower_components/jquery/LICENSE.txt | 36 + .../client/bower_components/jquery/README.md | 65 + .../client/bower_components/jquery/bower.json | 14 + .../client/bower_components/jquery/dist/jquery.js | 9814 ++++ .../bower_components/jquery/dist/jquery.min.js | 4 + .../bower_components/jquery/dist/jquery.min.map | 1 + .../jquery/external/sizzle/LICENSE.txt | 36 + .../jquery/external/sizzle/dist/sizzle.js | 2143 + .../jquery/external/sizzle/dist/sizzle.min.js | 3 + .../jquery/external/sizzle/dist/sizzle.min.map | 1 + .../client/bower_components/jquery/src/.jshintrc | 29 + .../client/bower_components/jquery/src/ajax.js | 845 + .../bower_components/jquery/src/ajax/jsonp.js | 100 + .../bower_components/jquery/src/ajax/load.js | 83 + .../bower_components/jquery/src/ajax/parseJSON.js | 13 + .../bower_components/jquery/src/ajax/parseXML.js | 27 + .../bower_components/jquery/src/ajax/script.js | 68 + .../jquery/src/ajax/var/location.js | 3 + .../bower_components/jquery/src/ajax/var/nonce.js | 5 + .../bower_components/jquery/src/ajax/var/rquery.js | 3 + .../client/bower_components/jquery/src/ajax/xhr.js | 167 + .../bower_components/jquery/src/attributes.js | 11 + .../bower_components/jquery/src/attributes/attr.js | 142 + .../jquery/src/attributes/classes.js | 177 + .../bower_components/jquery/src/attributes/prop.js | 125 + .../jquery/src/attributes/support.js | 36 + .../bower_components/jquery/src/attributes/val.js | 177 + .../bower_components/jquery/src/callbacks.js | 232 + .../client/bower_components/jquery/src/core.js | 494 + .../bower_components/jquery/src/core/access.js | 65 + .../bower_components/jquery/src/core/init.js | 134 + .../bower_components/jquery/src/core/parseHTML.js | 41 + .../bower_components/jquery/src/core/ready.js | 103 + .../jquery/src/core/var/rsingleTag.js | 5 + .../client/bower_components/jquery/src/css.js | 502 + .../jquery/src/css/addGetHookIf.js | 24 + .../bower_components/jquery/src/css/adjustCSS.js | 65 + .../bower_components/jquery/src/css/curCSS.js | 60 + .../jquery/src/css/defaultDisplay.js | 72 + .../jquery/src/css/hiddenVisibleSelectors.js | 18 + .../bower_components/jquery/src/css/showHide.js | 48 + .../bower_components/jquery/src/css/support.js | 121 + .../jquery/src/css/var/cssExpand.js | 3 + .../jquery/src/css/var/getStyles.js | 15 + .../jquery/src/css/var/isHidden.js | 16 + .../bower_components/jquery/src/css/var/rmargin.js | 3 + .../jquery/src/css/var/rnumnonpx.js | 5 + .../bower_components/jquery/src/css/var/swap.js | 24 + .../client/bower_components/jquery/src/data.js | 187 + .../bower_components/jquery/src/data/Data.js | 200 + .../jquery/src/data/var/acceptData.js | 18 + .../jquery/src/data/var/dataPriv.js | 5 + .../jquery/src/data/var/dataUser.js | 5 + .../client/bower_components/jquery/src/deferred.js | 158 + .../bower_components/jquery/src/deprecated.js | 32 + .../bower_components/jquery/src/dimensions.js | 54 + .../client/bower_components/jquery/src/effects.js | 629 + .../bower_components/jquery/src/effects/Tween.js | 121 + .../jquery/src/effects/animatedSelector.js | 13 + .../client/bower_components/jquery/src/event.js | 711 + .../bower_components/jquery/src/event/ajax.js | 20 + .../bower_components/jquery/src/event/alias.js | 27 + .../bower_components/jquery/src/event/focusin.js | 53 + .../bower_components/jquery/src/event/support.js | 9 + .../bower_components/jquery/src/event/trigger.js | 183 + .../bower_components/jquery/src/exports/amd.js | 24 + .../bower_components/jquery/src/exports/global.js | 26 + .../client/bower_components/jquery/src/intro.js | 44 + .../client/bower_components/jquery/src/jquery.js | 37 + .../bower_components/jquery/src/manipulation.js | 481 + .../jquery/src/manipulation/_evalUrl.js | 20 + .../jquery/src/manipulation/buildFragment.js | 102 + .../jquery/src/manipulation/getAll.js | 21 + .../jquery/src/manipulation/setGlobalEval.js | 20 + .../jquery/src/manipulation/support.js | 33 + .../jquery/src/manipulation/var/rcheckableType.js | 3 + .../jquery/src/manipulation/var/rscriptType.js | 3 + .../jquery/src/manipulation/var/rtagName.js | 3 + .../jquery/src/manipulation/wrapMap.js | 27 + .../client/bower_components/jquery/src/offset.js | 218 + .../client/bower_components/jquery/src/outro.js | 2 + .../client/bower_components/jquery/src/queue.js | 143 + .../bower_components/jquery/src/queue/delay.js | 22 + .../bower_components/jquery/src/selector-native.js | 211 + .../bower_components/jquery/src/selector-sizzle.js | 14 + .../client/bower_components/jquery/src/selector.js | 1 + .../bower_components/jquery/src/serialize.js | 125 + .../bower_components/jquery/src/traversing.js | 175 + .../jquery/src/traversing/findFilter.js | 100 + .../jquery/src/traversing/var/dir.js | 20 + .../jquery/src/traversing/var/rneedsContext.js | 6 + .../jquery/src/traversing/var/siblings.js | 15 + .../client/bower_components/jquery/src/var/arr.js | 3 + .../bower_components/jquery/src/var/class2type.js | 5 + .../bower_components/jquery/src/var/concat.js | 5 + .../bower_components/jquery/src/var/document.js | 3 + .../jquery/src/var/documentElement.js | 5 + .../bower_components/jquery/src/var/hasOwn.js | 5 + .../bower_components/jquery/src/var/indexOf.js | 5 + .../client/bower_components/jquery/src/var/pnum.js | 3 + .../client/bower_components/jquery/src/var/push.js | 5 + .../bower_components/jquery/src/var/rcssNum.js | 7 + .../bower_components/jquery/src/var/rnotwhite.js | 3 + .../bower_components/jquery/src/var/slice.js | 5 + .../bower_components/jquery/src/var/support.js | 5 + .../bower_components/jquery/src/var/toString.js | 5 + .../client/bower_components/jquery/src/wrap.js | 79 + .../client/bower_components/json3/.bower.json | 47 + .../client/bower_components/json3/CHANGELOG.md | 44 + .../client/bower_components/json3/CONTRIBUTING.md | 3 + .../client/bower_components/json3/LICENSE | 20 + .../client/bower_components/json3/README.md | 152 + .../client/bower_components/json3/bower.json | 38 + .../client/bower_components/json3/lib/json3.js | 902 + .../client/bower_components/json3/lib/json3.min.js | 17 + .../client/bower_components/lodash/.bower.json | 14 + .../client/bower_components/lodash/.editorconfig | 12 + .../client/bower_components/lodash/.gitattributes | 1 + .../lodash/.github/CONTRIBUTING.md | 78 + .../client/bower_components/lodash/.gitignore | 9 + .../client/bower_components/lodash/.jscsrc | 97 + .../lodash/.markdown-doctest-setup.js | 48 + .../client/bower_components/lodash/.travis.yml | 86 + .../client/bower_components/lodash/LICENSE | 47 + .../client/bower_components/lodash/README.md | 46 + .../bower_components/lodash/dist/lodash.core.js | 3830 ++ .../lodash/dist/lodash.core.min.js | 28 + .../bower_components/lodash/dist/lodash.fp.js | 879 + .../bower_components/lodash/dist/lodash.fp.min.js | 17 + .../client/bower_components/lodash/dist/lodash.js | 16404 +++++++ .../bower_components/lodash/dist/lodash.min.js | 127 + .../bower_components/lodash/dist/mapping.fp.js | 371 + .../client/bower_components/lodash/doc/README.md | 10897 +++++ .../bower_components/lodash/fp/_baseConvert.js | 466 + .../bower_components/lodash/fp/_convertBrowser.js | 18 + .../client/bower_components/lodash/fp/_mapping.js | 309 + .../bower_components/lodash/fp/placeholder.js | 6 + .../bower_components/lodash/lib/common/file.js | 71 + .../bower_components/lodash/lib/common/mapping.js | 9 + .../bower_components/lodash/lib/common/minify.js | 39 + .../lodash/lib/common/uglify.options.js | 23 + .../bower_components/lodash/lib/common/util.js | 27 + .../bower_components/lodash/lib/fp/build-dist.js | 55 + .../bower_components/lodash/lib/fp/build-doc.js | 65 + .../lodash/lib/fp/build-modules.js | 120 + .../lodash/lib/fp/template/doc/wiki.jst | 227 + .../lib/fp/template/modules/_falseOptions.jst | 7 + .../lodash/lib/fp/template/modules/_util.jst | 14 + .../lodash/lib/fp/template/modules/alias.jst | 1 + .../lodash/lib/fp/template/modules/category.jst | 2 + .../lodash/lib/fp/template/modules/convert.jst | 18 + .../lodash/lib/fp/template/modules/fp.jst | 2 + .../lodash/lib/fp/template/modules/module.jst | 5 + .../lodash/lib/fp/template/modules/thru.jst | 5 + .../bower_components/lodash/lib/main/build-dist.js | 30 + .../bower_components/lodash/lib/main/build-doc.js | 55 + .../lodash/lib/main/build-modules.js | 34 + .../client/bower_components/lodash/lodash.js | 16404 +++++++ .../client/bower_components/lodash/package.json | 55 + .../bower_components/lodash/perf/asset/perf-ui.js | 131 + .../client/bower_components/lodash/perf/index.html | 69 + .../client/bower_components/lodash/perf/perf.js | 1977 + .../bower_components/lodash/test/asset/test-ui.js | 170 + .../bower_components/lodash/test/asset/worker.js | 15 + .../bower_components/lodash/test/backbone.html | 170 + .../client/bower_components/lodash/test/fp.html | 41 + .../client/bower_components/lodash/test/index.html | 351 + .../client/bower_components/lodash/test/remove.js | 27 + .../bower_components/lodash/test/saucelabs.js | 914 + .../client/bower_components/lodash/test/test-fp.js | 2173 + .../client/bower_components/lodash/test/test.js | 26729 +++++++++++ .../bower_components/lodash/test/underscore.html | 484 + .../lodash/vendor/backbone/LICENSE | 22 + .../lodash/vendor/backbone/backbone.js | 1920 + .../lodash/vendor/backbone/test/collection.js | 1998 + .../lodash/vendor/backbone/test/events.js | 706 + .../lodash/vendor/backbone/test/model.js | 1418 + .../lodash/vendor/backbone/test/noconflict.js | 13 + .../lodash/vendor/backbone/test/router.js | 1062 + .../lodash/vendor/backbone/test/setup/dom-setup.js | 4 + .../vendor/backbone/test/setup/environment.js | 45 + .../lodash/vendor/backbone/test/sync.js | 239 + .../lodash/vendor/backbone/test/view.js | 495 + .../lodash/vendor/firebug-lite/license.txt | 30 + .../lodash/vendor/firebug-lite/skin/xp/blank.gif | Bin 0 -> 43 bytes .../vendor/firebug-lite/skin/xp/buttonBg.png | Bin 0 -> 167 bytes .../vendor/firebug-lite/skin/xp/buttonBgHover.png | Bin 0 -> 171 bytes .../vendor/firebug-lite/skin/xp/debugger.css | 331 + .../lodash/vendor/firebug-lite/skin/xp/detach.png | Bin 0 -> 655 bytes .../vendor/firebug-lite/skin/xp/detachHover.png | Bin 0 -> 586 bytes .../lodash/vendor/firebug-lite/skin/xp/disable.gif | Bin 0 -> 340 bytes .../lodash/vendor/firebug-lite/skin/xp/disable.png | Bin 0 -> 543 bytes .../vendor/firebug-lite/skin/xp/disableHover.gif | Bin 0 -> 344 bytes .../vendor/firebug-lite/skin/xp/disableHover.png | Bin 0 -> 512 bytes .../lodash/vendor/firebug-lite/skin/xp/down.png | Bin 0 -> 637 bytes .../vendor/firebug-lite/skin/xp/downActive.png | Bin 0 -> 543 bytes .../vendor/firebug-lite/skin/xp/downHover.png | Bin 0 -> 526 bytes .../vendor/firebug-lite/skin/xp/errorIcon-sm.png | Bin 0 -> 447 bytes .../vendor/firebug-lite/skin/xp/errorIcon.gif | Bin 0 -> 365 bytes .../vendor/firebug-lite/skin/xp/errorIcon.png | Bin 0 -> 457 bytes .../vendor/firebug-lite/skin/xp/firebug-1.3a2.css | 817 + .../vendor/firebug-lite/skin/xp/firebug.IE6.css | 20 + .../lodash/vendor/firebug-lite/skin/xp/firebug.css | 3147 ++ .../vendor/firebug-lite/skin/xp/firebug.html | 215 + .../lodash/vendor/firebug-lite/skin/xp/firebug.png | Bin 0 -> 1167 bytes .../lodash/vendor/firebug-lite/skin/xp/group.gif | Bin 0 -> 158 bytes .../lodash/vendor/firebug-lite/skin/xp/html.css | 272 + .../vendor/firebug-lite/skin/xp/infoIcon.gif | Bin 0 -> 359 bytes .../vendor/firebug-lite/skin/xp/infoIcon.png | Bin 0 -> 524 bytes .../vendor/firebug-lite/skin/xp/loading_16.gif | Bin 0 -> 1553 bytes .../lodash/vendor/firebug-lite/skin/xp/min.png | Bin 0 -> 552 bytes .../vendor/firebug-lite/skin/xp/minHover.png | Bin 0 -> 485 bytes .../lodash/vendor/firebug-lite/skin/xp/off.png | Bin 0 -> 742 bytes .../vendor/firebug-lite/skin/xp/offHover.png | Bin 0 -> 680 bytes .../firebug-lite/skin/xp/pixel_transparent.gif | Bin 0 -> 43 bytes .../vendor/firebug-lite/skin/xp/roundCorner.svg | 6 + .../lodash/vendor/firebug-lite/skin/xp/search.gif | Bin 0 -> 550 bytes .../lodash/vendor/firebug-lite/skin/xp/search.png | Bin 0 -> 685 bytes .../lodash/vendor/firebug-lite/skin/xp/shadow.gif | Bin 0 -> 4364 bytes .../lodash/vendor/firebug-lite/skin/xp/shadow2.gif | Bin 0 -> 3093 bytes .../vendor/firebug-lite/skin/xp/shadowAlpha.png | Bin 0 -> 3403 bytes .../lodash/vendor/firebug-lite/skin/xp/sprite.png | Bin 0 -> 40027 bytes .../vendor/firebug-lite/skin/xp/tabHoverLeft.png | Bin 0 -> 438 bytes .../vendor/firebug-lite/skin/xp/tabHoverMid.png | Bin 0 -> 261 bytes .../vendor/firebug-lite/skin/xp/tabHoverRight.png | Bin 0 -> 436 bytes .../lodash/vendor/firebug-lite/skin/xp/tabLeft.png | Bin 0 -> 449 bytes .../firebug-lite/skin/xp/tabMenuCheckbox.png | Bin 0 -> 220 bytes .../vendor/firebug-lite/skin/xp/tabMenuPin.png | Bin 0 -> 207 bytes .../vendor/firebug-lite/skin/xp/tabMenuRadio.png | Bin 0 -> 192 bytes .../vendor/firebug-lite/skin/xp/tabMenuTarget.png | Bin 0 -> 142 bytes .../firebug-lite/skin/xp/tabMenuTargetHover.png | Bin 0 -> 148 bytes .../lodash/vendor/firebug-lite/skin/xp/tabMid.png | Bin 0 -> 262 bytes .../vendor/firebug-lite/skin/xp/tabRight.png | Bin 0 -> 448 bytes .../firebug-lite/skin/xp/textEditorBorders.gif | Bin 0 -> 117 bytes .../firebug-lite/skin/xp/textEditorBorders.png | Bin 0 -> 3144 bytes .../firebug-lite/skin/xp/textEditorCorners.gif | Bin 0 -> 1821 bytes .../firebug-lite/skin/xp/textEditorCorners.png | Bin 0 -> 3960 bytes .../vendor/firebug-lite/skin/xp/titlebarMid.png | Bin 0 -> 273 bytes .../vendor/firebug-lite/skin/xp/toolbarMid.png | Bin 0 -> 242 bytes .../vendor/firebug-lite/skin/xp/tree_close.gif | Bin 0 -> 300 bytes .../vendor/firebug-lite/skin/xp/tree_open.gif | Bin 0 -> 202 bytes .../vendor/firebug-lite/skin/xp/twistyClosed.png | Bin 0 -> 334 bytes .../vendor/firebug-lite/skin/xp/twistyOpen.png | Bin 0 -> 309 bytes .../lodash/vendor/firebug-lite/skin/xp/up.png | Bin 0 -> 619 bytes .../vendor/firebug-lite/skin/xp/upActive.png | Bin 0 -> 551 bytes .../lodash/vendor/firebug-lite/skin/xp/upHover.png | Bin 0 -> 526 bytes .../vendor/firebug-lite/skin/xp/warningIcon.gif | Bin 0 -> 357 bytes .../vendor/firebug-lite/skin/xp/warningIcon.png | Bin 0 -> 516 bytes .../vendor/firebug-lite/src/firebug-lite-debug.js | 31176 +++++++++++++ .../lodash/vendor/json-js/json2.js | 519 + .../lodash/vendor/underscore/LICENSE | 23 + .../lodash/vendor/underscore/test/arrays.js | 555 + .../lodash/vendor/underscore/test/chaining.js | 99 + .../lodash/vendor/underscore/test/collections.js | 896 + .../vendor/underscore/test/cross-document.js | 141 + .../lodash/vendor/underscore/test/functions.js | 728 + .../lodash/vendor/underscore/test/objects.js | 1102 + .../lodash/vendor/underscore/test/utility.js | 420 + .../lodash/vendor/underscore/underscore-min.js | 6 + .../lodash/vendor/underscore/underscore.js | 1620 + .../client/bower_components/ng-dialog/.bower.json | 53 + .../client/bower_components/ng-dialog/CHANGELOG.md | 106 + .../bower_components/ng-dialog/CONTRIBUTING.md | 37 + .../client/bower_components/ng-dialog/README.md | 701 + .../client/bower_components/ng-dialog/bower.json | 43 + .../ng-dialog/css/ngDialog-custom-width.css | 3 + .../ng-dialog/css/ngDialog-theme-default.css | 191 + .../ng-dialog/css/ngDialog-theme-default.min.css | 1 + .../ng-dialog/css/ngDialog-theme-plain.css | 131 + .../ng-dialog/css/ngDialog-theme-plain.min.css | 1 + .../bower_components/ng-dialog/css/ngDialog.css | 114 + .../ng-dialog/css/ngDialog.min.css | 1 + .../bower_components/ng-dialog/js/ngDialog.js | 895 + .../bower_components/ng-dialog/js/ngDialog.min.js | 2 + .../bower_components/ng-dialog/karma.conf.js | 46 + .../client/bower_components/ng-dialog/package.json | 65 + .../bower_components/ng-dialog/protractor.conf.js | 75 + .../client/bower_components/ng-dialog/server.js | 9 + .../client/bower_components/ng-dialog/yarn.lock | 3281 ++ .../client/bower_components/ui-select/.bower.json | 41 + .../client/bower_components/ui-select/CHANGELOG.md | 180 + .../bower_components/ui-select/CONTRIBUTING.md | 161 + .../client/bower_components/ui-select/LICENSE | 20 + .../client/bower_components/ui-select/README.md | 57 + .../client/bower_components/ui-select/bower.json | 31 + .../bower_components/ui-select/composer.json | 29 + .../bower_components/ui-select/deploy-docs.sh | 31 + .../bower_components/ui-select/dist/select.css | 271 + .../bower_components/ui-select/dist/select.js | 2217 + .../bower_components/ui-select/dist/select.min.css | 7 + .../bower_components/ui-select/dist/select.min.js | 9 + .../bower_components/ui-select/docs/assets/app.js | 1 + .../bower_components/ui-select/docs/assets/demo.js | 461 + .../ui-select/docs/assets/docs.css | 339 + .../ui-select/docs/assets/plunkr.js | 110 + .../bower_components/ui-select/docs/index.html | 191 + .../ui-select/docs/partials/_footer.html | 2 + .../ui-select/docs/partials/_header.html | 64 + .../client/bower_components/ui-select/index.js | 2 + .../client/bower_components/ui-select/package.json | 61 + .../angular-att-gridster/angular-gridster.js | 2240 + .../angular-att-gridster/css/angular-gridster.css | 173 + .../styles/angular-gridster.css | 173 + .../angular-att-gridster/styles/ui-gridster.css | 116 + .../angular-att-gridster/ui-gridster-tpls-0.0.1.js | 86 + .../angular-att-gridster/ui-gridster-tpls.js | 164 + .../angular-att-gridster/ui-gridster.js | 131 + .../angular.att.abs-2.17.0/CHANGELOG.md | 1190 + .../angular.att.abs-2.17.0/README.md | 70 + .../angular.att.abs-2.17.0/att-abs-tpls.js | 20422 +++++++++ .../angular.att.abs-2.17.0/att-abs-tpls.min.js | 22 + .../angular.att.abs-2.17.0/styles/base.css | 1 + .../angular.att.abs-2.17.0/styles/btn.css | 1 + .../angular.att.abs-2.17.0/styles/demo.css | 2 + .../angular.att.abs-2.17.0/styles/dtpk.css | 9 + .../angular.att.abs-2.17.0/styles/frms.css | 1 + .../styles/ie/backgroundsize.min.htc | 12 + .../angular.att.abs-2.17.0/styles/images/down.png | Bin 0 -> 1059 bytes .../angular.att.abs-2.17.0/styles/images/up.png | Bin 0 -> 1064 bytes .../styles/images/upanddown.png | Bin 0 -> 1033 bytes .../styles/pages/iconography.css | 2 + .../angular.att.abs-2.17.0/styles/sldr.css | 1 + .../angular.att.abs-2.17.0/styles/style.css | 1 + .../angular.att.abs-2.17.0/styles/tbs.css | 1 + .../bower_components_external/bowser/bowser.js | 559 + .../bower_components_external/bowser/useragents.js | 1267 + .../ionicons-2.0.1/.gitignore | 4 + .../ionicons-2.0.1/LICENSE | 21 + .../ionicons-2.0.1/css/ionicons.css | 1480 + .../ionicons-2.0.1/css/ionicons.min.css | 11 + .../ionicons-2.0.1/fonts/ionicons.eot | Bin 0 -> 120724 bytes .../ionicons-2.0.1/fonts/ionicons.svg | 2230 + .../ionicons-2.0.1/fonts/ionicons.ttf | Bin 0 -> 188508 bytes .../ionicons-2.0.1/fonts/ionicons.woff | Bin 0 -> 67904 bytes .../ionicons-2.0.1/readme.md | 60 + ecomp-portal-FE/client/configurations/dev.json | 65 + .../client/configurations/devServer.json | 48 + ecomp-portal-FE/client/configurations/integ.json | 65 + ecomp-portal-FE/client/configurations/mock.json | 51 + ecomp-portal-FE/client/favicon.ico | Bin 0 -> 1150 bytes ecomp-portal-FE/client/index.html | 279 + .../kpi-dashboard/controllers/AAI-controller.js | 48 + .../kpi-dashboard/controllers/APPC-controller.js | 48 + .../kpi-dashboard/controllers/ASDC-controller.js | 45 + .../controllers/Closedloop-controller.js | 48 + .../kpi-dashboard/controllers/DCAE-controller.js | 375 + .../kpi-dashboard/controllers/DMaaP-controller.js | 48 + .../kpi-dashboard/controllers/ECOMP-controller.js | 25 + .../controllers/ECOMP_Portal-controller.js | 48 + .../controllers/InfrastructurePortal-controller.js | 48 + .../kpi-dashboard/controllers/MSO-controller.js | 48 + .../kpi-dashboard/controllers/Policy-controller.js | 48 + .../kpi-dashboard/controllers/kpi-whitelist.js | 22 + .../client/kpi-dashboard/views/AAI/AAI_KPI.html | 33 + .../kpi-dashboard/views/AAI/AAI_Metrics.html | 33 + .../views/AAI/AAI_UserDefinedKPI.html | 33 + .../client/kpi-dashboard/views/APPC/APPC_KPI.html | 33 + .../kpi-dashboard/views/APPC/APPC_Metrics.html | 33 + .../views/APPC/APPC_UserDefinedKPI.html | 33 + .../client/kpi-dashboard/views/ASDC/ASDC_KPI.html | 33 + .../kpi-dashboard/views/ASDC/ASDC_Metrics.html | 33 + .../views/ASDC/ASDC_UserDefinedKPI.html | 33 + .../views/Closedloop/Closedloop_KPI.html | 33 + .../views/Closedloop/Closedloop_Metrics.html | 33 + .../Closedloop/Closedloop_UserDefinedKPI.html | 33 + .../client/kpi-dashboard/views/DCAE/DCAE_KPI.html | 204 + .../kpi-dashboard/views/DCAE/DCAE_Metrics.html | 41 + .../views/DCAE/DCAE_UserDefinedKPI.html | 33 + .../views/DCAE_DASH/dcae_locations.csv | 10 + .../views/DCAE_DASH/dcae_locations.html | 431 + .../views/DCAE_DASH/dcae_locations1.csv | 7 + .../kpi-dashboard/views/DCAE_DASH/lib/d3.v3.min.js | 24 + .../kpi-dashboard/views/DCAE_DASH/lib/leaflet.css | 497 + .../kpi-dashboard/views/DCAE_DASH/lib/leaflet.js | 28 + .../views/DCAE_DASH/static/visjs/legend_v2.js | 85 + .../kpi-dashboard/views/DMaaP/DMaaP_KPI.html | 33 + .../kpi-dashboard/views/DMaaP/DMaaP_Metrics.html | 33 + .../views/DMaaP/DMaaP_UserDefinedKPI.html | 33 + .../client/kpi-dashboard/views/ECOMP.html | 30 + .../views/ECOMP_Portal/ECOMP_Portal_KPI.html | 33 + .../views/ECOMP_Portal/ECOMP_Portal_Metrics.html | 33 + .../ECOMP_Portal/ECOMP_Portal_UserDefinedKPI.html | 33 + .../InfrastructurePortal_KPI.html | 33 + .../InfrastructurePortal_Metrics.html | 33 + .../InfrastructurePortal_UserDefinedKPI.html | 33 + .../client/kpi-dashboard/views/MSO/MSO_KPI.html | 33 + .../kpi-dashboard/views/MSO/MSO_Metrics.html | 33 + .../views/MSO/MSO_UserDefinedKPI.html | 33 + .../kpi-dashboard/views/Policy/Policy_KPI.html | 33 + .../kpi-dashboard/views/Policy/Policy_Metrics.html | 33 + .../views/Policy/Policy_UserDefinedKPI.html | 33 + .../client/kpi-dashboard/views/sec-level-tabs.html | 29 + .../client/kpi-dashboard/views/top-level-tabs.html | 44 + .../client/kpi-dashboard/views/userapi-edit.html | 71 + .../client/kpi-dashboard/views/userstory-edit.html | 102 + ecomp-portal-FE/client/robots.txt | 3 + .../client/utils/test-utils/test-utils.js | 51 + ecomp-portal-FE/e2e/main/anchoring.spec.js | 27 + ecomp-portal-FE/e2e/main/main.po.js | 15 + ecomp-portal-FE/e2e/main/main.spec.js | 16 + ecomp-portal-FE/karma.conf.js | 131 + .../mock/ecomp/data/admins/account-admins.json | 140 + .../mock/ecomp/data/admins/admin-app-roles.json | 25 + .../mock/ecomp/data/applications/admin-apps.json | 30 + .../data/applications/applications-onboarding.json | 128 + .../ecomp/data/applications/available-apps.json | 25 + .../mock/ecomp/data/applications/user-apps.json | 62 + .../mock/ecomp/data/functional-menu/app-roles.json | 34 + .../mock/ecomp/data/functional-menu/dev.json | 22 + .../mock/ecomp/data/functional-menu/favorites.json | 27 + .../data/functional-menu/menu-item-details.json | 12 + .../ecomp/data/functional-menu/menu-items.json | 674 + .../data/functional-menu/menu-items_small.json | 674 + ecomp-portal-FE/mock/ecomp/data/ping/ping.json | 4 + .../mock/ecomp/data/user-profile/user-profile.json | 6 + .../ecomp/data/user-profile/user-static-info.json | 7 + .../mock/ecomp/data/users/account-users.json | 40 + .../mock/ecomp/data/users/query-users-results.json | 38 + .../mock/ecomp/data/users/user-apps-roles.json | 59 + .../mock/ecomp/data/widgets/widgets.json | 39 + ecomp-portal-FE/mock/ecomp/router.js | 144 + ecomp-portal-FE/mock/server.js | 16 + ecomp-portal-FE/package.json | 90 + ecomp-portal-FE/pom.xml | 7 + ecomp-portal-FE/protractor.conf.js | 51 + ecomp-portal-FE/server/.jshintrc | 14 + ecomp-portal-FE/server/.jshintrc-spec | 11 + ecomp-portal-FE/server/app.js | 19 + ecomp-portal-FE/server/components/errors/index.js | 20 + ecomp-portal-FE/server/config/environment/index.js | 25 + ecomp-portal-FE/server/config/express.js | 40 + ecomp-portal-FE/server/routes.js | 17 + ecomp-portal-FE/server/views/404.html | 157 + 1462 files changed, 534823 insertions(+) create mode 100644 ecomp-portal-FE/.bowerrc create mode 100644 ecomp-portal-FE/.gitignore create mode 100644 ecomp-portal-FE/Gruntfile.js create mode 100644 ecomp-portal-FE/README.md create mode 100644 ecomp-portal-FE/bower.json create mode 100644 ecomp-portal-FE/build-ecomportal-fe.sh create mode 100644 ecomp-portal-FE/client/.htaccess create mode 100644 ecomp-portal-FE/client/.jshintrc create mode 100644 ecomp-portal-FE/client/app/app.js create mode 100644 ecomp-portal-FE/client/app/app.less create mode 100644 ecomp-portal-FE/client/app/configurations.js create mode 100644 ecomp-portal-FE/client/app/directives/auto-focus/auto-focus.directive.js create mode 100644 ecomp-portal-FE/client/app/directives/image-upload/image-upload.directive.js create mode 100644 ecomp-portal-FE/client/app/directives/left-menu/left-menu.directive.js create mode 100644 ecomp-portal-FE/client/app/directives/left-menu/left-menu.less create mode 100644 ecomp-portal-FE/client/app/directives/left-menu/left-menu.tpl.html create mode 100644 ecomp-portal-FE/client/app/directives/multiple-select/multiple-select.directive.js create mode 100644 ecomp-portal-FE/client/app/directives/multiple-select/multiple-select.less create mode 100644 ecomp-portal-FE/client/app/directives/multiple-select/multiple-select.tpl.html create mode 100644 ecomp-portal-FE/client/app/directives/right-click-menu/right-click-menu.directive.js create mode 100644 ecomp-portal-FE/client/app/directives/right-click/ng-right-click-directive.js create mode 100644 ecomp-portal-FE/client/app/directives/right-menu/right-menu.directive.js create mode 100644 ecomp-portal-FE/client/app/directives/right-menu/right-menu.less create mode 100644 ecomp-portal-FE/client/app/directives/right-menu/right-menu.tpl.html create mode 100644 ecomp-portal-FE/client/app/directives/scroll-top/scroll-top.directive.js create mode 100644 ecomp-portal-FE/client/app/directives/search-users/search-users.controller.js create mode 100644 ecomp-portal-FE/client/app/directives/search-users/search-users.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/directives/search-users/search-users.directive.js create mode 100644 ecomp-portal-FE/client/app/directives/search-users/search-users.less create mode 100644 ecomp-portal-FE/client/app/directives/search-users/search-users.tpl.html create mode 100644 ecomp-portal-FE/client/app/filters/elipsis/elipsis.filter.js create mode 100644 ecomp-portal-FE/client/app/filters/elipsis/elipsis.filter.spec.js create mode 100644 ecomp-portal-FE/client/app/filters/trusted-url/trusted-url.filter.js create mode 100644 ecomp-portal-FE/client/app/router.js create mode 100644 ecomp-portal-FE/client/app/services/admins/admins.service.js create mode 100644 ecomp-portal-FE/client/app/services/applications/applications.service.js create mode 100644 ecomp-portal-FE/client/app/services/catalog/catalog.service.js create mode 100644 ecomp-portal-FE/client/app/services/confirm-box/confirm-box.service.js create mode 100644 ecomp-portal-FE/client/app/services/contact-us/contact-us.service.js create mode 100644 ecomp-portal-FE/client/app/services/dashboard/dashboard.service.js create mode 100644 ecomp-portal-FE/client/app/services/error-messages/error-messages.service.js create mode 100644 ecomp-portal-FE/client/app/services/functionalMenu/functionalMenu.service.js create mode 100644 ecomp-portal-FE/client/app/services/global-constants/global-constants.js create mode 100644 ecomp-portal-FE/client/app/services/kpi-dashboard/kpi-dashboard.service.js create mode 100644 ecomp-portal-FE/client/app/services/manifest/manifest.service.js create mode 100644 ecomp-portal-FE/client/app/services/menus/menus.service.js create mode 100644 ecomp-portal-FE/client/app/services/portal-admins/portal-admins.service.js create mode 100644 ecomp-portal-FE/client/app/services/support/getAccess/get-access.service.js create mode 100644 ecomp-portal-FE/client/app/services/support/session/session.service.js create mode 100644 ecomp-portal-FE/client/app/services/userProfile/userProfile.service.js create mode 100644 ecomp-portal-FE/client/app/services/userbar/userbar.update.service.js create mode 100644 ecomp-portal-FE/client/app/services/users/users.service.js create mode 100644 ecomp-portal-FE/client/app/services/utils/utils.service.js create mode 100644 ecomp-portal-FE/client/app/services/widgets/widgets.service.js create mode 100644 ecomp-portal-FE/client/app/styles/att-abs.less create mode 100644 ecomp-portal-FE/client/app/styles/buttons.less create mode 100644 ecomp-portal-FE/client/app/styles/ecomp-general.less create mode 100644 ecomp-portal-FE/client/app/styles/fonts.less create mode 100644 ecomp-portal-FE/client/app/styles/form.less create mode 100644 ecomp-portal-FE/client/app/styles/kpi-dashboard.less create mode 100644 ecomp-portal-FE/client/app/styles/mixins.less create mode 100644 ecomp-portal-FE/client/app/styles/ng-dialog.less create mode 100644 ecomp-portal-FE/client/app/styles/reset.less create mode 100644 ecomp-portal-FE/client/app/styles/select2.less create mode 100644 ecomp-portal-FE/client/app/styles/spinner.less create mode 100644 ecomp-portal-FE/client/app/styles/sprites.less create mode 100644 ecomp-portal-FE/client/app/styles/variables.less create mode 100644 ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.controller.js create mode 100644 ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.modal.html create mode 100644 ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.modal.less create mode 100644 ecomp-portal-FE/client/app/views/admins/admins.controller.js create mode 100644 ecomp-portal-FE/client/app/views/admins/admins.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/admins/admins.less create mode 100644 ecomp-portal-FE/client/app/views/admins/admins.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.controller.js create mode 100644 ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.modal.html create mode 100644 ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.modal.less create mode 100644 ecomp-portal-FE/client/app/views/applications/applications.controller.js create mode 100644 ecomp-portal-FE/client/app/views/applications/applications.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/applications/applications.less create mode 100644 ecomp-portal-FE/client/app/views/applications/applications.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/catalog/catalog.controller.js create mode 100644 ecomp-portal-FE/client/app/views/catalog/catalog.less create mode 100644 ecomp-portal-FE/client/app/views/catalog/catalog.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/catalog/catalogconfirmation.controller.js create mode 100644 ecomp-portal-FE/client/app/views/catalog/information-box.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/confirmation-box/admin-confirmation-box.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.controller.js create mode 100644 ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.less create mode 100644 ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/confirmation-box/dragdrop-confirmation-box.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/confirmation-box/information-box.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/dashboard/dashboard-widget-manage.html create mode 100644 ecomp-portal-FE/client/app/views/dashboard/dashboard-widget.controller.js create mode 100644 ecomp-portal-FE/client/app/views/dashboard/dashboard-widget.controller.less create mode 100644 ecomp-portal-FE/client/app/views/dashboard/dashboard.controller.js create mode 100644 ecomp-portal-FE/client/app/views/dashboard/dashboard.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/dashboard/dashboard.less create mode 100644 ecomp-portal-FE/client/app/views/dashboard/dashboard.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/dashboard/newsticker.controller.js create mode 100644 ecomp-portal-FE/client/app/views/errors/error.404.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/errors/error.controller.js create mode 100644 ecomp-portal-FE/client/app/views/errors/error.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/footer/footer.controller.js create mode 100644 ecomp-portal-FE/client/app/views/footer/footer.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/footer/footer.less create mode 100644 ecomp-portal-FE/client/app/views/footer/footer.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.controller.js create mode 100644 ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.delete.modal.html create mode 100644 ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.modal.html create mode 100644 ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/modal-details.modal.less create mode 100644 ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.controller.js create mode 100644 ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.less create mode 100644 ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/functionalMenu/jqTreeContextMenu.js create mode 100644 ecomp-portal-FE/client/app/views/header/header.controller.js create mode 100644 ecomp-portal-FE/client/app/views/header/header.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/header/header.less create mode 100644 ecomp-portal-FE/client/app/views/header/header.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/header/user-edit/edit-user.controller.js create mode 100644 ecomp-portal-FE/client/app/views/header/user-edit/edit-user.less create mode 100644 ecomp-portal-FE/client/app/views/header/user-edit/edit-user.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/home/applications-home/applications-home.controller.js create mode 100644 ecomp-portal-FE/client/app/views/home/applications-home/applications-home.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/home/applications-home/applications-home.less create mode 100644 ecomp-portal-FE/client/app/views/home/applications-home/applications-home.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.controller.js create mode 100644 ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.less create mode 100644 ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.js create mode 100644 ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html create mode 100644 ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.less create mode 100644 ecomp-portal-FE/client/app/views/portal-admin/portal-admin-controller.js create mode 100644 ecomp-portal-FE/client/app/views/portal-admin/portal-admin.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/portal-admin/portal-admins.less create mode 100644 ecomp-portal-FE/client/app/views/search/search.controller.js create mode 100644 ecomp-portal-FE/client/app/views/search/search.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/search/search.less create mode 100644 ecomp-portal-FE/client/app/views/search/search.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/sidebar/sidebar.controller.js create mode 100644 ecomp-portal-FE/client/app/views/sidebar/sidebar.less create mode 100644 ecomp-portal-FE/client/app/views/sidebar/sidebar.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.js create mode 100644 ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.less create mode 100644 ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.html create mode 100644 ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.js create mode 100644 ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/support/contact-us/contact-us.less create mode 100644 ecomp-portal-FE/client/app/views/support/contact-us/contact-us.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.js create mode 100644 ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/support/get-access/get-access.less create mode 100644 ecomp-portal-FE/client/app/views/support/get-access/get-access.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/tabs/tabframe.html create mode 100644 ecomp-portal-FE/client/app/views/tabs/tabs.controller.js create mode 100644 ecomp-portal-FE/client/app/views/tabs/tabs.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/tabs/tabs.less create mode 100644 ecomp-portal-FE/client/app/views/tabs/tabs.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/userbar/userbar.controller.js create mode 100644 ecomp-portal-FE/client/app/views/userbar/userbar.less create mode 100644 ecomp-portal-FE/client/app/views/userbar/userbar.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.js create mode 100644 ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.html create mode 100644 ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.less create mode 100644 ecomp-portal-FE/client/app/views/users/users.controller.js create mode 100644 ecomp-portal-FE/client/app/views/users/users.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/users/users.less create mode 100644 ecomp-portal-FE/client/app/views/users/users.tpl.html create mode 100644 ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.js create mode 100644 ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.html create mode 100644 ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.less create mode 100644 ecomp-portal-FE/client/app/views/widgets/widgets.controller.js create mode 100644 ecomp-portal-FE/client/app/views/widgets/widgets.controller.spec.js create mode 100644 ecomp-portal-FE/client/app/views/widgets/widgets.less create mode 100644 ecomp-portal-FE/client/app/views/widgets/widgets.tpl.html create mode 100644 ecomp-portal-FE/client/assets/images/cloud.png create mode 100644 ecomp-portal-FE/client/assets/images/ecomp_logo.png create mode 100644 ecomp-portal-FE/client/assets/images/grips.png create mode 100644 ecomp-portal-FE/client/assets/images/photo.png create mode 100644 ecomp-portal-FE/client/assets/images/spinner.gif create mode 100644 ecomp-portal-FE/client/assets/images/sprite.png create mode 100644 ecomp-portal-FE/client/bower_components/angular-animate/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-animate/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-animate/angular-animate.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-animate/angular-animate.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-animate/angular-animate.min.js.map create mode 100644 ecomp-portal-FE/client/bower_components/angular-animate/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-animate/index.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-animate/package.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-aria/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-aria/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-aria/angular-aria.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-aria/angular-aria.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-aria/angular-aria.min.js.map create mode 100644 ecomp-portal-FE/client/bower_components/angular-aria/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-aria/index.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-aria/package.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-bootstrap/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-bootstrap/.gitignore create mode 100644 ecomp-portal-FE/client/bower_components/angular-bootstrap/.npmignore create mode 100644 ecomp-portal-FE/client/bower_components/angular-bootstrap/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-bootstrap/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-bootstrap/index.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-bootstrap/package.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap-csp.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap-tpls.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/CHANGELOG.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/LICENSE create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/TRANSITION.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/browserify/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/browserify/app.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/browserify/index.html create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/app.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/index.html create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/main.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/require.config.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack/app.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack/index.html create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack/webpack.config.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6/app.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6/index.html create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6/webpack.config.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6_2/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6_2/app.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6_2/index.html create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6_2/webpack.config.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/circle.yml create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/dist/angular-cache.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/dist/angular-cache.js.map create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/dist/angular-cache.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/dist/angular-cache.min.map create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/rollup.config.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cache/scripts/postbuild.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cookies/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-cookies/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-cookies/angular-cookies.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cookies/angular-cookies.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cookies/angular-cookies.min.js.map create mode 100644 ecomp-portal-FE/client/bower_components/angular-cookies/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-cookies/index.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-cookies/package.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-gestures/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-gestures/LICENSE.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-gestures/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-gestures/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-gestures/gestures.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-gestures/gestures.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/.gitignore create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/CHANGELOG.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/LICENSE create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/angular-material-mocks.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/angular-material.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/angular-material.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/angular-material.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/angular-material.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/bottomSheet/demoBasicUsage/img/icons/copy.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/bottomSheet/demoBasicUsage/img/icons/copy2.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/bottomSheet/demoBasicUsage/img/icons/facebook.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/bottomSheet/demoBasicUsage/img/icons/hangout.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/bottomSheet/demoBasicUsage/img/icons/mail.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/bottomSheet/demoBasicUsage/img/icons/message.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/bottomSheet/demoBasicUsage/img/icons/print.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/bottomSheet/demoBasicUsage/img/icons/share-arrow.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/bottomSheet/demoBasicUsage/img/icons/twitter.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/bottomSheet/demoBasicUsage/img/icons/upload.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/chips/demoBasicUsage/style.scss create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/chips/demoContactChips/style.scss create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/gridList/demoBasicUsage/style.scss create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/gridList/demoDynamicTiles/style.scss create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/gridList/demoResponsiveUsage/style.scss create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/icon/demoLoadSvgIconsFromUrl/img/icons/addShoppingCart.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/icon/demoLoadSvgIconsFromUrl/img/icons/android.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/icon/demoLoadSvgIconsFromUrl/img/icons/cake.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/icon/demoSvgIconSets/assets/cake.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/icon/demoSvgIconSets/assets/core-icons.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/icon/demoSvgIconSets/assets/social-icons.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/icon/demoSvgIconsFromURL/img/icons/addShoppingCart.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/icon/demoSvgIconsFromURL/img/icons/android.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/icon/demoSvgIconsFromURL/img/icons/cake.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/icon/demoUsingTemplateCache/assets/android.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/icon/demoUsingTemplateCache/assets/cake.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/icon/demoUsingTemplateCache/assets/core-icons.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/input/demoIcons/icons/ic_email_24px.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/input/demoIcons/icons/ic_person_24px.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/input/demoIcons/icons/ic_phone_24px.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/input/demoIcons/icons/ic_place_24px.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/input/demoIcons/style.scss create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/list/demoListControls/img/100-0.jpeg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/list/demoListControls/img/100-1.jpeg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/list/demoListControls/img/100-2.jpeg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/tabs/demoDynamicHeight/style.scss create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/tabs/demoDynamicTabs/style.scss create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/tabs/demoStaticTabs/style.scss create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/toolbar/demoBasicUsage/img/icons/favorite.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/toolbar/demoBasicUsage/img/icons/menu.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/demos/toolbar/demoBasicUsage/img/icons/more_vert.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/index.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/autocomplete/autocomplete-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/autocomplete/autocomplete.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/autocomplete/autocomplete.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/backdrop/backdrop-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/backdrop/backdrop.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/backdrop/backdrop.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/bottomSheet/bottomSheet-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/bottomSheet/bottomSheet.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/bottomSheet/bottomSheet.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/button/button-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/button/button.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/button/button.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/card/card-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/card/card.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/card/card.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/checkbox/checkbox-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/checkbox/checkbox.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/checkbox/checkbox.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/chips/chips-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/chips/chips.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/chips/chips.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/content/content-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/content/content.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/content/content.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/core/core.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/core/core.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/core/default-theme.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/dialog/dialog-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/dialog/dialog.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/dialog/dialog.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/divider/divider-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/divider/divider.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/divider/divider.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/gridList/gridList-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/gridList/gridList.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/gridList/gridList.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/icon/icon-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/icon/icon.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/icon/icon.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/input/input-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/input/input.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/input/input.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/list/list-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/list/list.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/list/list.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/menu/menu.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/menu/menu.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/progressCircular/progressCircular-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/progressCircular/progressCircular.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/progressCircular/progressCircular.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/progressLinear/progressLinear-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/progressLinear/progressLinear.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/progressLinear/progressLinear.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/radioButton/radioButton-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/radioButton/radioButton.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/radioButton/radioButton.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/select/select-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/select/select.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/select/select.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/sidenav/sidenav-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/sidenav/sidenav.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/sidenav/sidenav.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/slider/slider-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/slider/slider.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/slider/slider.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/sticky/sticky.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/sticky/sticky.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/subheader/subheader-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/subheader/subheader.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/subheader/subheader.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/swipe/swipe.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/switch/switch-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/switch/switch.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/switch/switch.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/tabs/tabs-arrow.svg create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/tabs/tabs-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/tabs/tabs.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/tabs/tabs.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/textField/textField-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/textField/textField.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/textField/textField.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/toast/toast-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/toast/toast.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/toast/toast.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/toolbar/toolbar-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/toolbar/toolbar.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/toolbar/toolbar.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/tooltip/tooltip-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/tooltip/tooltip.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/tooltip/tooltip.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/whiteframe/whiteframe.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/closure/whiteframe/whiteframe.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/css/angular-material-layout.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/autocomplete/autocomplete-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/autocomplete/autocomplete-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/autocomplete/autocomplete.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/autocomplete/autocomplete.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/autocomplete/autocomplete.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/autocomplete/autocomplete.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/autocomplete/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/backdrop/backdrop-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/backdrop/backdrop-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/backdrop/backdrop.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/backdrop/backdrop.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/backdrop/backdrop.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/backdrop/backdrop.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/backdrop/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/bottomSheet/bottomSheet-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/bottomSheet/bottomSheet-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/bottomSheet/bottomSheet.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/bottomSheet/bottomSheet.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/bottomSheet/bottomSheet.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/bottomSheet/bottomSheet.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/bottomSheet/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/button/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/button/button-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/button/button-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/button/button.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/button/button.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/button/button.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/button/button.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/card/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/card/card-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/card/card-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/card/card.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/card/card.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/card/card.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/card/card.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/checkbox/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/checkbox/checkbox-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/checkbox/checkbox-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/checkbox/checkbox.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/checkbox/checkbox.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/checkbox/checkbox.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/checkbox/checkbox.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/chips/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/chips/chips-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/chips/chips-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/chips/chips.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/chips/chips.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/chips/chips.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/chips/chips.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/content/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/content/content-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/content/content-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/content/content.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/content/content.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/content/content.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/content/content.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/core/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/core/core.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/core/core.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/core/core.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/core/core.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/core/default-theme.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/dialog/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/dialog/dialog-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/dialog/dialog-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/dialog/dialog.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/dialog/dialog.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/dialog/dialog.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/dialog/dialog.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/divider/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/divider/divider-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/divider/divider-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/divider/divider.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/divider/divider.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/divider/divider.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/divider/divider.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/gridList/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/gridList/gridList.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/gridList/gridList.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/gridList/gridList.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/gridList/gridList.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/icon/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/icon/icon-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/icon/icon-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/icon/icon.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/icon/icon.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/icon/icon.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/icon/icon.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/input/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/input/input-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/input/input-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/input/input.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/input/input.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/input/input.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/input/input.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/list/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/list/list-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/list/list-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/list/list.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/list/list.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/list/list.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/list/list.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/menu/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/menu/menu.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/menu/menu.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/menu/menu.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/menu/menu.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/progressCircular/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/progressCircular/progressCircular-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/progressCircular/progressCircular-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/progressCircular/progressCircular.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/progressCircular/progressCircular.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/progressCircular/progressCircular.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/progressCircular/progressCircular.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/progressLinear/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/progressLinear/progressLinear-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/progressLinear/progressLinear-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/progressLinear/progressLinear.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/progressLinear/progressLinear.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/progressLinear/progressLinear.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/progressLinear/progressLinear.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/radioButton/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/radioButton/radioButton-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/radioButton/radioButton-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/radioButton/radioButton.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/radioButton/radioButton.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/radioButton/radioButton.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/radioButton/radioButton.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/select/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/select/select-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/select/select-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/select/select.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/select/select.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/select/select.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/select/select.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/sidenav/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/sidenav/sidenav-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/sidenav/sidenav-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/sidenav/sidenav.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/sidenav/sidenav.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/sidenav/sidenav.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/sidenav/sidenav.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/slider/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/slider/slider-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/slider/slider-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/slider/slider.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/slider/slider.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/slider/slider.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/slider/slider.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/sticky/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/sticky/sticky.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/sticky/sticky.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/sticky/sticky.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/sticky/sticky.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/subheader/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/subheader/subheader-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/subheader/subheader-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/subheader/subheader.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/subheader/subheader.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/subheader/subheader.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/subheader/subheader.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/swipe/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/swipe/swipe.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/swipe/swipe.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/switch/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/switch/switch-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/switch/switch-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/switch/switch.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/switch/switch.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/switch/switch.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/switch/switch.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/tabs/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/tabs/tabs-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/tabs/tabs-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/tabs/tabs.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/tabs/tabs.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/tabs/tabs.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/tabs/tabs.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/textField/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/textField/textField-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/textField/textField-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/textField/textField.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/textField/textField.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/textField/textField.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/textField/textField.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/toast/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/toast/toast-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/toast/toast-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/toast/toast.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/toast/toast.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/toast/toast.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/toast/toast.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/toolbar/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/toolbar/toolbar-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/toolbar/toolbar-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/toolbar/toolbar.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/toolbar/toolbar.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/toolbar/toolbar.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/toolbar/toolbar.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/tooltip/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/tooltip/tooltip-default-theme.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/tooltip/tooltip-default-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/tooltip/tooltip.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/tooltip/tooltip.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/tooltip/tooltip.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/tooltip/tooltip.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/whiteframe/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/whiteframe/whiteframe.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/whiteframe/whiteframe.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/whiteframe/whiteframe.min.css create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/modules/js/whiteframe/whiteframe.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-material/package.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-messages/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-messages/LICENSE.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-messages/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-messages/angular-messages.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-messages/angular-messages.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-messages/angular-messages.min.js.map create mode 100644 ecomp-portal-FE/client/bower_components/angular-messages/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-messages/index.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-messages/package.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-mocks/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-mocks/LICENSE.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-mocks/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-mocks/angular-mocks.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-mocks/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-mocks/ngAnimateMock.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-mocks/ngMock.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-mocks/ngMockE2E.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-mocks/package.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-resource/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-resource/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-resource/angular-resource.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-resource/angular-resource.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-resource/angular-resource.min.js.map create mode 100644 ecomp-portal-FE/client/bower_components/angular-resource/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-resource/index.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-resource/package.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-route/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-route/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-route/angular-route.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-route/angular-route.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-route/angular-route.min.js.map create mode 100644 ecomp-portal-FE/client/bower_components/angular-route/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-route/index.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-route/package.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-sanitize/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-sanitize/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-sanitize/angular-sanitize.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-sanitize/angular-sanitize.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-sanitize/angular-sanitize.min.js.map create mode 100644 ecomp-portal-FE/client/bower_components/angular-sanitize/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-sanitize/index.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-sanitize/package.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-scenario/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-scenario/LICENSE.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-scenario/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-scenario/angular-scenario.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-scenario/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-scenario/jstd-scenario-adapter-config.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-scenario/jstd-scenario-adapter.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-scenario/package.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/.gitignore create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/.travis.yml create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/ISSUE_TEMPLATE.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/changeLog.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/dist/smart-table.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/dist/smart-table.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/dist/smart-table.min.js.map create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/gulpFile.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/index.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/package.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/readme.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/src/bottom.txt create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/src/smart-table.module.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/src/stConfig.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/src/stPagination.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/src/stPipe.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/src/stSearch.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/src/stSelectRow.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/src/stSort.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/src/stTable.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-smart-table/src/top.txt create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/CHANGELOG.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/CONTRIBUTING.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/LICENSE create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/release/angular-ui-router.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/release/angular-ui-router.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/src/common.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/src/resolve.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/src/state.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/src/stateDirectives.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/src/stateFilters.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/src/templateFactory.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/src/urlMatcherFactory.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/src/urlRouter.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/src/view.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/src/viewDirective.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-ui-router/src/viewScroll.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-uuid/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-uuid/LICENCE create mode 100644 ecomp-portal-FE/client/bower_components/angular-uuid/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular-uuid/uuid.js create mode 100644 ecomp-portal-FE/client/bower_components/angular-uuid/uuid.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular/README.md create mode 100644 ecomp-portal-FE/client/bower_components/angular/angular-csp.css create mode 100644 ecomp-portal-FE/client/bower_components/angular/angular.js create mode 100644 ecomp-portal-FE/client/bower_components/angular/angular.min.js create mode 100644 ecomp-portal-FE/client/bower_components/angular/angular.min.js.gzip create mode 100644 ecomp-portal-FE/client/bower_components/angular/angular.min.js.map create mode 100644 ecomp-portal-FE/client/bower_components/angular/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/angular/index.js create mode 100644 ecomp-portal-FE/client/bower_components/angular/package.json create mode 100644 ecomp-portal-FE/client/bower_components/es5-shim/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/es5-shim/CHANGES create mode 100644 ecomp-portal-FE/client/bower_components/es5-shim/CONTRIBUTORS.md create mode 100644 ecomp-portal-FE/client/bower_components/es5-shim/LICENSE create mode 100644 ecomp-portal-FE/client/bower_components/es5-shim/Makefile create mode 100644 ecomp-portal-FE/client/bower_components/es5-shim/README.md create mode 100644 ecomp-portal-FE/client/bower_components/es5-shim/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/es5-shim/component.json create mode 100644 ecomp-portal-FE/client/bower_components/es5-shim/es5-sham.js create mode 100644 ecomp-portal-FE/client/bower_components/es5-shim/es5-sham.map create mode 100644 ecomp-portal-FE/client/bower_components/es5-shim/es5-sham.min.js create mode 100644 ecomp-portal-FE/client/bower_components/es5-shim/es5-shim.js create mode 100644 ecomp-portal-FE/client/bower_components/es5-shim/es5-shim.map create mode 100644 ecomp-portal-FE/client/bower_components/es5-shim/es5-shim.min.js create mode 100644 ecomp-portal-FE/client/bower_components/es5-shim/package.json create mode 100644 ecomp-portal-FE/client/bower_components/es5-shim/shims.json create mode 100644 ecomp-portal-FE/client/bower_components/hammerjs/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/hammerjs/CHANGELOG.md create mode 100644 ecomp-portal-FE/client/bower_components/hammerjs/CONTRIBUTING.md create mode 100644 ecomp-portal-FE/client/bower_components/hammerjs/LICENSE.md create mode 100644 ecomp-portal-FE/client/bower_components/hammerjs/README.md create mode 100644 ecomp-portal-FE/client/bower_components/hammerjs/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/hammerjs/changelog.js create mode 100644 ecomp-portal-FE/client/bower_components/hammerjs/hammer.js create mode 100644 ecomp-portal-FE/client/bower_components/hammerjs/hammer.min.js create mode 100644 ecomp-portal-FE/client/bower_components/hammerjs/hammer.min.js.map create mode 100644 ecomp-portal-FE/client/bower_components/hammerjs/hammer.min.map create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/.editorconfig create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/.gitignore create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/.travis.yml create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/LICENSE create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/README.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_config.yml create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/01_general.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/02_introduction.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/03_features.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/04_demo.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/05_requirements.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/06_downloads.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/07_tutorial.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/08_examples.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/09_usecases.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/10_changelog.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/11_options.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/12_autoescape.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/13_autoopen.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/14_buttonleft.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/15_closedicon.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/16_data.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/17_datafilter.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/18_data-url.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/19_draganddrop.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/20_keyboardsupport.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/21_oncanmove.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/22_oncanmoveto.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/23_oncanselectnode.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/24_oncreateli.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/25_onismovehandle.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/26_onloadfailed.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/27_onloading.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/28_openedicon.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/29_openfolderdelay.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/30_rtl.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/31_savestate.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/32_selectable.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/33_slide.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/34_usecontextmenu.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/35_functions.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/36_addnodeafter.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/36a_addparentnode.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/37_addnodebefore.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/38_appendnode.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/39_closenode.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/40_destroy.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/41_getnodebycallback.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/42_getnodebyid.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/43_getnodebyhtmlelement.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/44_getselectednode.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/45_getstate.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/46_gettree.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/47_loaddata.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/48_loaddatafromurl.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/49_movedown.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/50_movenode.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/51_moveup.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/52_opennode.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/53_reload.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/54_removenode.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/55_selectnode.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/56_scrolltonode.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/57_setoption.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/58_setstate.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/59_toggle.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/60_tojson.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/61_updatenode.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/62_events.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/63_tree-click.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/64_tree-close.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/65_tree-contextmenu.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/66_tree-dblclick.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/67_tree-init.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/68_tree-move.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/69_tree-open.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/70_tree-select.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/71_multiple-selection.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/72_add-to-selection.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/73_get-selected-nodes.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/74_is-node-selected.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/75_remove-from-selection.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/76_node-functions.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/77_children.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/78_getdata.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/79_getlevel.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/80_getnextnode.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/81_getnextsibling.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/82_getpreviousnode.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/83_getprevioussibling.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/84_parent.md create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/insert.py create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_entries/renumber.py create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_examples/01_load_json_data.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_examples/02_load_json_data_from_server.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_examples/03_drag_and_drop.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_examples/04_save_state.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_examples/05_load_on_demand.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_examples/06_autoescape.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_examples/07_autoscroll.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_examples/08_multiple_select.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_examples/09_custom_html.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_examples/10_icon_buttons.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_examples/11_right-to-left.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_examples/12_button_on_right.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_layouts/base.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_layouts/frontpage.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/_layouts/page.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/coffeelint.json create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/gulpfile.coffee create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/gulpfile.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/index.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/jqtree-circle.png create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/jqtree.css create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/jqtree.jquery.json create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/jqtree.scss create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/karma.conf.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/lib/drag_and_drop_handler.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/lib/elements_renderer.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/lib/key_handler.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/lib/mouse.widget.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/lib/node.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/lib/node_element.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/lib/save_state_handler.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/lib/scroll_handler.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/lib/select_node_handler.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/lib/simple.widget.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/lib/tree.jquery.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/lib/util.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/lib/version.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/package.json create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/phantomjs/runner.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/phantomjs/server.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/screenshot.png create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/sitemap.txt create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src/drag_and_drop_handler.coffee create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src/elements_renderer.coffee create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src/header.txt create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src/key_handler.coffee create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src/mouse.widget.coffee create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src/node.coffee create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src/node_element.coffee create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src/save_state_handler.coffee create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src/scroll_handler.coffee create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src/select_node_handler.coffee create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src/simple.widget.coffee create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src/tree.jquery.coffee create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src/util.coffee create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src/version.coffee create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src_test/karma-test.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src_test/test.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src_test/test_jqtree.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src_test/test_tree.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src_test/test_util.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/src_test/utils_for_test.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/blanket/dist/qunit/blanket.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/bootstrap/dist/css/bootstrap-theme.min.css create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/bootstrap/dist/css/bootstrap.min.css create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.eot create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.svg create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.woff create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/bootstrap/dist/js/bootstrap.min.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/fontawesome/css/font-awesome.min.css create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/fontawesome/fonts/FontAwesome.otf create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/fontawesome/fonts/fontawesome-webfont.eot create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/fontawesome/fonts/fontawesome-webfont.svg create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/fontawesome/fonts/fontawesome-webfont.ttf create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/fontawesome/fonts/fontawesome-webfont.woff create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/fontawesome/fonts/fontawesome-webfont.woff2 create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/jquery-1/dist/jquery.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/jquery-1/dist/jquery.min.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/jquery-2/dist/jquery.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/jquery-2/dist/jquery.min.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/jquery-cookie/jquery.cookie.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/jquery-mockjax/dist/jquery.mockjax.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/jquery/dist/jquery.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/jquery/dist/jquery.min.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/json3/lib/json3.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/pygments/css/monokai.css create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/qunit/qunit/qunit.css create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/bower_components/qunit/qunit/qunit.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/documentation.css create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/documentation.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/example.css create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/example.scss create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/example_data.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/examples/autoescape.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/examples/autoscroll.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/examples/button-on-right.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/examples/custom_html.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/examples/drag_and_drop.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/examples/icon_buttons.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/examples/load_json_data.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/examples/load_json_data_from_server.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/examples/load_on_demand.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/examples/multiple_select.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/examples/right-to-left.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/examples/save_state.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/static/spinner.gif create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/test/test.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/test/test_build.js create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/test/test_jquery_1.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/test/test_jquery_2.html create mode 100644 ecomp-portal-FE/client/bower_components/jqTree/tree.jquery.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery-mousewheel/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/jquery-mousewheel/ChangeLog.md create mode 100644 ecomp-portal-FE/client/bower_components/jquery-mousewheel/LICENSE.txt create mode 100644 ecomp-portal-FE/client/bower_components/jquery-mousewheel/README.md create mode 100644 ecomp-portal-FE/client/bower_components/jquery-mousewheel/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/jquery-mousewheel/jquery.mousewheel.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery-mousewheel/jquery.mousewheel.min.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drag/jquery.event.drag.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drag/jquery.event.drag.live.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drag/test/binding.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drag/test/handlers.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drag/test/index.html create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drag/test/interaction.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drag/test/live.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drag/test/method.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drag/test/properties.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drag/test/requirements.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drag/test/touch.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drop/jquery.event.drop.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drop/jquery.event.drop.live.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drop/test/binding.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drop/test/handlers.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drop/test/index.html create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drop/test/interaction.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drop/test/live.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drop/test/method.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drop/test/properties.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drop/test/requirements.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drop/test/tolerance.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.drop/test/touch.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.keyend/jquery.event.keyend.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.linger/jquery.event.linger.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.linger/test/binding.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.linger/test/handlers.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.linger/test/index.html create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.linger/test/interaction.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.linger/test/method.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.linger/test/requirements.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/event.wheel/jquery.event.wheel.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/fire/jquery.fire.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/fire/test/custom.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/fire/test/form.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/fire/test/index.html create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/fire/test/key.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/fire/test/mouse.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/fire/test/mutation.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/fire/test/object.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/fire/test/requirements.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/fire/test/touch.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/jquery/jquery-1.4.2.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/jquery/jquery-1.4.4.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/jquery/jquery-1.5b1.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/jquery/jquery-1.7.2.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/jquery/jquery.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/jquery/qunit.css create mode 100644 ecomp-portal-FE/client/bower_components/jquery.event.drag-new/jquery/qunit.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.newstape/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/jquery.newstape/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/jquery.newstape/dist/jquery.newstape.min.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.newstape/gulpfile.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.newstape/jquery.newstape.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery.newstape/package.json create mode 100644 ecomp-portal-FE/client/bower_components/jquery.newstape/readme.md create mode 100644 ecomp-portal-FE/client/bower_components/jquery/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/jquery/AUTHORS.txt create mode 100644 ecomp-portal-FE/client/bower_components/jquery/LICENSE.txt create mode 100644 ecomp-portal-FE/client/bower_components/jquery/README.md create mode 100644 ecomp-portal-FE/client/bower_components/jquery/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/jquery/dist/jquery.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/dist/jquery.min.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/dist/jquery.min.map create mode 100644 ecomp-portal-FE/client/bower_components/jquery/external/sizzle/LICENSE.txt create mode 100644 ecomp-portal-FE/client/bower_components/jquery/external/sizzle/dist/sizzle.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/external/sizzle/dist/sizzle.min.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/external/sizzle/dist/sizzle.min.map create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/.jshintrc create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/ajax.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/ajax/jsonp.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/ajax/load.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/ajax/parseJSON.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/ajax/parseXML.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/ajax/script.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/ajax/var/location.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/ajax/var/nonce.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/ajax/var/rquery.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/ajax/xhr.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/attributes.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/attributes/attr.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/attributes/classes.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/attributes/prop.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/attributes/support.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/attributes/val.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/callbacks.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/core.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/core/access.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/core/init.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/core/parseHTML.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/core/ready.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/core/var/rsingleTag.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/css.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/css/addGetHookIf.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/css/adjustCSS.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/css/curCSS.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/css/defaultDisplay.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/css/hiddenVisibleSelectors.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/css/showHide.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/css/support.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/css/var/cssExpand.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/css/var/getStyles.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/css/var/isHidden.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/css/var/rmargin.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/css/var/rnumnonpx.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/css/var/swap.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/data.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/data/Data.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/data/var/acceptData.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/data/var/dataPriv.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/data/var/dataUser.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/deferred.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/deprecated.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/dimensions.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/effects.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/effects/Tween.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/effects/animatedSelector.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/event.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/event/ajax.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/event/alias.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/event/focusin.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/event/support.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/event/trigger.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/exports/amd.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/exports/global.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/intro.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/jquery.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/manipulation.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/manipulation/_evalUrl.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/manipulation/buildFragment.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/manipulation/getAll.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/manipulation/setGlobalEval.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/manipulation/support.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/manipulation/var/rcheckableType.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/manipulation/var/rscriptType.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/manipulation/var/rtagName.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/manipulation/wrapMap.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/offset.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/outro.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/queue.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/queue/delay.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/selector-native.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/selector-sizzle.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/selector.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/serialize.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/traversing.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/traversing/findFilter.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/traversing/var/dir.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/traversing/var/rneedsContext.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/traversing/var/siblings.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/var/arr.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/var/class2type.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/var/concat.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/var/document.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/var/documentElement.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/var/hasOwn.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/var/indexOf.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/var/pnum.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/var/push.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/var/rcssNum.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/var/rnotwhite.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/var/slice.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/var/support.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/var/toString.js create mode 100644 ecomp-portal-FE/client/bower_components/jquery/src/wrap.js create mode 100644 ecomp-portal-FE/client/bower_components/json3/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/json3/CHANGELOG.md create mode 100644 ecomp-portal-FE/client/bower_components/json3/CONTRIBUTING.md create mode 100644 ecomp-portal-FE/client/bower_components/json3/LICENSE create mode 100644 ecomp-portal-FE/client/bower_components/json3/README.md create mode 100644 ecomp-portal-FE/client/bower_components/json3/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/json3/lib/json3.js create mode 100644 ecomp-portal-FE/client/bower_components/json3/lib/json3.min.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/lodash/.editorconfig create mode 100644 ecomp-portal-FE/client/bower_components/lodash/.gitattributes create mode 100644 ecomp-portal-FE/client/bower_components/lodash/.github/CONTRIBUTING.md create mode 100644 ecomp-portal-FE/client/bower_components/lodash/.gitignore create mode 100644 ecomp-portal-FE/client/bower_components/lodash/.jscsrc create mode 100644 ecomp-portal-FE/client/bower_components/lodash/.markdown-doctest-setup.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/.travis.yml create mode 100644 ecomp-portal-FE/client/bower_components/lodash/LICENSE create mode 100644 ecomp-portal-FE/client/bower_components/lodash/README.md create mode 100644 ecomp-portal-FE/client/bower_components/lodash/dist/lodash.core.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/dist/lodash.core.min.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/dist/lodash.fp.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/dist/lodash.fp.min.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/dist/lodash.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/dist/lodash.min.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/dist/mapping.fp.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/doc/README.md create mode 100644 ecomp-portal-FE/client/bower_components/lodash/fp/_baseConvert.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/fp/_convertBrowser.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/fp/_mapping.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/fp/placeholder.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/common/file.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/common/mapping.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/common/minify.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/common/uglify.options.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/common/util.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/fp/build-dist.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/fp/build-doc.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/fp/build-modules.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/fp/template/doc/wiki.jst create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/fp/template/modules/_falseOptions.jst create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/fp/template/modules/_util.jst create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/fp/template/modules/alias.jst create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/fp/template/modules/category.jst create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/fp/template/modules/convert.jst create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/fp/template/modules/fp.jst create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/fp/template/modules/module.jst create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/fp/template/modules/thru.jst create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/main/build-dist.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/main/build-doc.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lib/main/build-modules.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/lodash.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/package.json create mode 100644 ecomp-portal-FE/client/bower_components/lodash/perf/asset/perf-ui.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/perf/index.html create mode 100644 ecomp-portal-FE/client/bower_components/lodash/perf/perf.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/test/asset/test-ui.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/test/asset/worker.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/test/backbone.html create mode 100644 ecomp-portal-FE/client/bower_components/lodash/test/fp.html create mode 100644 ecomp-portal-FE/client/bower_components/lodash/test/index.html create mode 100644 ecomp-portal-FE/client/bower_components/lodash/test/remove.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/test/saucelabs.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/test/test-fp.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/test/test.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/test/underscore.html create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/backbone/LICENSE create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/backbone/backbone.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/backbone/test/collection.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/backbone/test/events.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/backbone/test/model.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/backbone/test/noconflict.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/backbone/test/router.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/backbone/test/setup/dom-setup.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/backbone/test/setup/environment.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/backbone/test/sync.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/backbone/test/view.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/license.txt create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/blank.gif create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/buttonBg.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/buttonBgHover.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/debugger.css create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/detach.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/detachHover.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/disable.gif create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/disable.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/disableHover.gif create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/disableHover.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/down.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/downActive.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/downHover.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/errorIcon-sm.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/errorIcon.gif create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/errorIcon.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/firebug-1.3a2.css create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/firebug.IE6.css create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/firebug.css create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/firebug.html create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/firebug.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/group.gif create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/html.css create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/infoIcon.gif create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/infoIcon.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/loading_16.gif create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/min.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/minHover.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/off.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/offHover.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/pixel_transparent.gif create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/roundCorner.svg create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/search.gif create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/search.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/shadow.gif create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/shadow2.gif create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/shadowAlpha.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/sprite.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/tabHoverLeft.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/tabHoverMid.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/tabHoverRight.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/tabLeft.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/tabMenuCheckbox.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/tabMenuPin.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/tabMenuRadio.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/tabMenuTarget.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/tabMenuTargetHover.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/tabMid.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/tabRight.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/textEditorBorders.gif create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/textEditorBorders.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/textEditorCorners.gif create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/textEditorCorners.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/titlebarMid.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/toolbarMid.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/tree_close.gif create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/tree_open.gif create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/twistyClosed.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/twistyOpen.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/up.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/upActive.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/upHover.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/warningIcon.gif create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/skin/xp/warningIcon.png create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/firebug-lite/src/firebug-lite-debug.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/json-js/json2.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/underscore/LICENSE create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/underscore/test/arrays.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/underscore/test/chaining.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/underscore/test/collections.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/underscore/test/cross-document.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/underscore/test/functions.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/underscore/test/objects.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/underscore/test/utility.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/underscore/underscore-min.js create mode 100644 ecomp-portal-FE/client/bower_components/lodash/vendor/underscore/underscore.js create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/CHANGELOG.md create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/CONTRIBUTING.md create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/README.md create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/css/ngDialog-custom-width.css create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/css/ngDialog-theme-default.css create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/css/ngDialog-theme-default.min.css create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/css/ngDialog-theme-plain.css create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/css/ngDialog-theme-plain.min.css create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/css/ngDialog.css create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/css/ngDialog.min.css create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/js/ngDialog.js create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/js/ngDialog.min.js create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/karma.conf.js create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/package.json create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/protractor.conf.js create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/server.js create mode 100644 ecomp-portal-FE/client/bower_components/ng-dialog/yarn.lock create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/.bower.json create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/CHANGELOG.md create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/CONTRIBUTING.md create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/LICENSE create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/README.md create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/bower.json create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/composer.json create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/deploy-docs.sh create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/dist/select.css create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/dist/select.js create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/dist/select.min.css create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/dist/select.min.js create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/docs/assets/app.js create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/docs/assets/demo.js create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/docs/assets/docs.css create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/docs/assets/plunkr.js create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/docs/index.html create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/docs/partials/_footer.html create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/docs/partials/_header.html create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/index.js create mode 100644 ecomp-portal-FE/client/bower_components/ui-select/package.json create mode 100644 ecomp-portal-FE/client/bower_components_external/angular-att-gridster/angular-gridster.js create mode 100644 ecomp-portal-FE/client/bower_components_external/angular-att-gridster/css/angular-gridster.css create mode 100644 ecomp-portal-FE/client/bower_components_external/angular-att-gridster/styles/angular-gridster.css create mode 100644 ecomp-portal-FE/client/bower_components_external/angular-att-gridster/styles/ui-gridster.css create mode 100644 ecomp-portal-FE/client/bower_components_external/angular-att-gridster/ui-gridster-tpls-0.0.1.js create mode 100644 ecomp-portal-FE/client/bower_components_external/angular-att-gridster/ui-gridster-tpls.js create mode 100644 ecomp-portal-FE/client/bower_components_external/angular-att-gridster/ui-gridster.js create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/CHANGELOG.md create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/README.md create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/att-abs-tpls.js create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/att-abs-tpls.min.js create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/styles/base.css create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/styles/btn.css create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/styles/demo.css create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/styles/dtpk.css create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/styles/frms.css create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/styles/ie/backgroundsize.min.htc create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/styles/images/down.png create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/styles/images/up.png create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/styles/images/upanddown.png create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/styles/pages/iconography.css create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/styles/sldr.css create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/styles/style.css create mode 100644 ecomp-portal-FE/client/bower_components_external/angular.att.abs-2.17.0/styles/tbs.css create mode 100644 ecomp-portal-FE/client/bower_components_external/bowser/bowser.js create mode 100644 ecomp-portal-FE/client/bower_components_external/bowser/useragents.js create mode 100644 ecomp-portal-FE/client/bower_components_external/ionicons-2.0.1/.gitignore create mode 100644 ecomp-portal-FE/client/bower_components_external/ionicons-2.0.1/LICENSE create mode 100644 ecomp-portal-FE/client/bower_components_external/ionicons-2.0.1/css/ionicons.css create mode 100644 ecomp-portal-FE/client/bower_components_external/ionicons-2.0.1/css/ionicons.min.css create mode 100644 ecomp-portal-FE/client/bower_components_external/ionicons-2.0.1/fonts/ionicons.eot create mode 100644 ecomp-portal-FE/client/bower_components_external/ionicons-2.0.1/fonts/ionicons.svg create mode 100644 ecomp-portal-FE/client/bower_components_external/ionicons-2.0.1/fonts/ionicons.ttf create mode 100644 ecomp-portal-FE/client/bower_components_external/ionicons-2.0.1/fonts/ionicons.woff create mode 100644 ecomp-portal-FE/client/bower_components_external/ionicons-2.0.1/readme.md create mode 100644 ecomp-portal-FE/client/configurations/dev.json create mode 100644 ecomp-portal-FE/client/configurations/devServer.json create mode 100644 ecomp-portal-FE/client/configurations/integ.json create mode 100644 ecomp-portal-FE/client/configurations/mock.json create mode 100644 ecomp-portal-FE/client/favicon.ico create mode 100644 ecomp-portal-FE/client/index.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/controllers/AAI-controller.js create mode 100644 ecomp-portal-FE/client/kpi-dashboard/controllers/APPC-controller.js create mode 100644 ecomp-portal-FE/client/kpi-dashboard/controllers/ASDC-controller.js create mode 100644 ecomp-portal-FE/client/kpi-dashboard/controllers/Closedloop-controller.js create mode 100644 ecomp-portal-FE/client/kpi-dashboard/controllers/DCAE-controller.js create mode 100644 ecomp-portal-FE/client/kpi-dashboard/controllers/DMaaP-controller.js create mode 100644 ecomp-portal-FE/client/kpi-dashboard/controllers/ECOMP-controller.js create mode 100644 ecomp-portal-FE/client/kpi-dashboard/controllers/ECOMP_Portal-controller.js create mode 100644 ecomp-portal-FE/client/kpi-dashboard/controllers/InfrastructurePortal-controller.js create mode 100644 ecomp-portal-FE/client/kpi-dashboard/controllers/MSO-controller.js create mode 100644 ecomp-portal-FE/client/kpi-dashboard/controllers/Policy-controller.js create mode 100644 ecomp-portal-FE/client/kpi-dashboard/controllers/kpi-whitelist.js create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/AAI/AAI_KPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/AAI/AAI_Metrics.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/AAI/AAI_UserDefinedKPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/APPC/APPC_KPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/APPC/APPC_Metrics.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/APPC/APPC_UserDefinedKPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/ASDC/ASDC_KPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/ASDC/ASDC_Metrics.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/ASDC/ASDC_UserDefinedKPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/Closedloop/Closedloop_KPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/Closedloop/Closedloop_Metrics.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/Closedloop/Closedloop_UserDefinedKPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/DCAE/DCAE_KPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/DCAE/DCAE_Metrics.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/DCAE/DCAE_UserDefinedKPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/DCAE_DASH/dcae_locations.csv create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/DCAE_DASH/dcae_locations.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/DCAE_DASH/dcae_locations1.csv create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/DCAE_DASH/lib/d3.v3.min.js create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/DCAE_DASH/lib/leaflet.css create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/DCAE_DASH/lib/leaflet.js create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/DCAE_DASH/static/visjs/legend_v2.js create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/DMaaP/DMaaP_KPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/DMaaP/DMaaP_Metrics.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/DMaaP/DMaaP_UserDefinedKPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/ECOMP.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/ECOMP_Portal/ECOMP_Portal_KPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/ECOMP_Portal/ECOMP_Portal_Metrics.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/ECOMP_Portal/ECOMP_Portal_UserDefinedKPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/InfrastructurePortal/InfrastructurePortal_KPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/InfrastructurePortal/InfrastructurePortal_Metrics.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/InfrastructurePortal/InfrastructurePortal_UserDefinedKPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/MSO/MSO_KPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/MSO/MSO_Metrics.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/MSO/MSO_UserDefinedKPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/Policy/Policy_KPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/Policy/Policy_Metrics.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/Policy/Policy_UserDefinedKPI.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/sec-level-tabs.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/top-level-tabs.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/userapi-edit.html create mode 100644 ecomp-portal-FE/client/kpi-dashboard/views/userstory-edit.html create mode 100644 ecomp-portal-FE/client/robots.txt create mode 100644 ecomp-portal-FE/client/utils/test-utils/test-utils.js create mode 100644 ecomp-portal-FE/e2e/main/anchoring.spec.js create mode 100644 ecomp-portal-FE/e2e/main/main.po.js create mode 100644 ecomp-portal-FE/e2e/main/main.spec.js create mode 100644 ecomp-portal-FE/karma.conf.js create mode 100644 ecomp-portal-FE/mock/ecomp/data/admins/account-admins.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/admins/admin-app-roles.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/applications/admin-apps.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/applications/applications-onboarding.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/applications/available-apps.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/applications/user-apps.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/functional-menu/app-roles.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/functional-menu/dev.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/functional-menu/favorites.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/functional-menu/menu-item-details.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/functional-menu/menu-items.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/functional-menu/menu-items_small.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/ping/ping.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/user-profile/user-profile.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/user-profile/user-static-info.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/users/account-users.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/users/query-users-results.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/users/user-apps-roles.json create mode 100644 ecomp-portal-FE/mock/ecomp/data/widgets/widgets.json create mode 100644 ecomp-portal-FE/mock/ecomp/router.js create mode 100644 ecomp-portal-FE/mock/server.js create mode 100644 ecomp-portal-FE/package.json create mode 100644 ecomp-portal-FE/pom.xml create mode 100644 ecomp-portal-FE/protractor.conf.js create mode 100644 ecomp-portal-FE/server/.jshintrc create mode 100644 ecomp-portal-FE/server/.jshintrc-spec create mode 100644 ecomp-portal-FE/server/app.js create mode 100644 ecomp-portal-FE/server/components/errors/index.js create mode 100644 ecomp-portal-FE/server/config/environment/index.js create mode 100644 ecomp-portal-FE/server/config/express.js create mode 100644 ecomp-portal-FE/server/routes.js create mode 100644 ecomp-portal-FE/server/views/404.html (limited to 'ecomp-portal-FE') diff --git a/ecomp-portal-FE/.bowerrc b/ecomp-portal-FE/.bowerrc new file mode 100644 index 00000000..dd348af0 --- /dev/null +++ b/ecomp-portal-FE/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "client/bower_components/" +} \ No newline at end of file diff --git a/ecomp-portal-FE/.gitignore b/ecomp-portal-FE/.gitignore new file mode 100644 index 00000000..4e0eb558 --- /dev/null +++ b/ecomp-portal-FE/.gitignore @@ -0,0 +1,6 @@ +/target/ +.classpath +.project +.settings/ +/bin/ +/bin/**/* \ No newline at end of file diff --git a/ecomp-portal-FE/Gruntfile.js b/ecomp-portal-FE/Gruntfile.js new file mode 100644 index 00000000..eccdc9aa --- /dev/null +++ b/ecomp-portal-FE/Gruntfile.js @@ -0,0 +1,779 @@ +// Generated on 2015-09-27 using generator-angular-fullstack 2.1.1 +'use strict'; + +module.exports = function (grunt) { + var localConfig; + try { + localConfig = require('./server/config/local.env'); + } catch (e) { + localConfig = {}; + } + + // Load grunt tasks automatically, when needed + require('jit-grunt')(grunt, { + express: 'grunt-express-server', + useminPrepare: 'grunt-usemin', + ngtemplates: 'grunt-angular-templates', + cdnify: 'grunt-google-cdn', + protractor: 'grunt-protractor-runner', + buildcontrol: 'grunt-build-control', + ngconstant: 'grunt-ng-constant' + }); + + // Time how long tasks take. Can help when optimizing build times + require('time-grunt')(grunt); + + grunt.loadNpmTasks('grunt-exec'); + + // Define the configuration for all the tasks + grunt.initConfig({ + + // Project settings + pkg: grunt.file.readJSON('package.json'), + yeoman: { + // configurable paths + client: require('./bower.json').appPath || 'client', + dist: 'dist' + }, + express: { + options: { + port: process.env.PORT || 9000 + }, + dev: { + options: { + script: 'server/app.js', + args:[ + grunt.option('min') ? '' : '--unmin', + grunt.option('server-conf') ? '--server-conf=' + grunt.option('server-conf') : '--server-conf=development' + ] + } + }, + dev_debug: { + options: { + script: 'server/app.js', + debug: true, + args:[ + grunt.option('min') ? '' : '--unmin', + grunt.option('server-conf') ? '--server-conf=' + grunt.option('server-conf') : '--server-conf=development' + ] + } + }, + mock: { + options: { + script: 'mock/server.js' + } + }, + prod: { + options: { + script: 'dist/server/app.js' + } + } + }, + open: { + server: { + url: 'http://localhost:<%= express.options.port %>/ecompportal/applicationsHome' + } + }, + watch: { + injectJS: { + files: [ + '<%= yeoman.client %>/{app,components}/**/*.js', + '!<%= yeoman.client %>/{app,components}/**/*.spec.js', + '!<%= yeoman.client %>/{app,components}/**/*.mock.js', + '!<%= yeoman.client %>/app/app.js'], + tasks: ['injector:scripts'] + }, + injectCss: { + files: [ + '<%= yeoman.client %>/{app,components}/**/*.css' + ], + tasks: ['injector:css'] + }, + mochaTest: { + files: ['server/**/*.spec.js'], + tasks: ['env:test', 'mochaTest'] + }, + jsTest: { + files: [ + '<%= yeoman.client %>/{app,components}/**/*.spec.js', + '<%= yeoman.client %>/{app,components}/**/*.mock.js' + ], + tasks: ['newer:jshint:all', 'karma'] + }, + injectLess: { + files: [ + '<%= yeoman.client %>/{app,components}/**/*.less'], + tasks: ['injector:less'] + }, + less: { + files: [ + '<%= yeoman.client %>/{app,components}/**/*.less'], + tasks: ['less', 'autoprefixer'] + }, + babel: { + files: [ + '<%= yeoman.client %>/{app,components}/**/*.js', + '!<%= yeoman.client %>/{app,components}/**/*.spec.js' + ], + tasks: ['babel'] + }, + gruntfile: { + files: ['Gruntfile.js'] + }, + livereload: { + files: [ + '{.tmp,<%= yeoman.client %>}/{app,components}/**/*.css', + '{.tmp,<%= yeoman.client %>}/{app,components}/**/*.html', + + '.tmp/{app,components}/**/*.js', + + '!{.tmp,<%= yeoman.client %>}{app,components}/**/*.spec.js', + '!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.mock.js', + '<%= yeoman.client %>/assets/images/{,*//*}*.{png,jpg,jpeg,gif,webp,svg}' + ], + options: { + livereload: true + } + }, + express: { + files: [ + 'server/**/*.{js,json}' + ], + tasks: ['express:dev', 'wait'], + options: { + livereload: true, + spawn: false //Without this option specified express won't be reloaded + } + }, + mock:{ + files:[ + 'mock/**/*' + ], + tasks:['express:mock'], + options:{ + spawn: false + } + }, + clientConf: { + files: [ + 'client/configurations/*.json' + ], + tasks: ['ngconstant'] + } + }, + + // Make sure code styles are up to par and there are no obvious mistakes + jshint: { + options: { + jshintrc: '<%= yeoman.client %>/.jshintrc', + reporter: require('jshint-stylish') + }, + server: { + options: { + jshintrc: 'server/.jshintrc' + }, + src: [ + 'server/**/*.js', + '!server/**/*.spec.js' + ] + }, + serverTest: { + options: { + jshintrc: 'server/.jshintrc-spec' + }, + src: ['server/**/*.spec.js'] + }, + all: [ + '<%= yeoman.client %>/{app,components}/**/*.js', + '!<%= yeoman.client %>/{app,components}/**/*.spec.js', + '!<%= yeoman.client %>/{app,components}/**/*.mock.js' + ], + test: { + src: [ + '<%= yeoman.client %>/{app,components}/**/*.spec.js', + '<%= yeoman.client %>/{app,components}/**/*.mock.js' + ] + } + }, + + // Empties folders to start fresh + clean: { + dist: { + files: [{ + dot: true, + src: [ + '.tmp', + '<%= yeoman.dist %>/*', + '!<%= yeoman.dist %>/.git*', + '!<%= yeoman.dist %>/.openshift', + '!<%= yeoman.dist %>/Procfile' + ] + }] + }, + server: '.tmp' + }, + + // Add vendor prefixed styles + autoprefixer: { + options: { + browsers: ['last 1 version'] + }, + dist: { + files: [{ + expand: true, + cwd: '.tmp/', + src: '{,*/}*.css', + dest: '.tmp/' + }] + } + }, + + // // Debugging with node inspector + // 'node-inspector': { + // custom: { + // options: { + // 'web-host': 'localhost' + // } + // } + // }, + + // Use nodemon to run server in debug mode with an initial breakpoint + nodemon: { + debug: { + script: 'server/app.js', + options: { + nodeArgs: ['--debug-brk'], + env: { + PORT: process.env.PORT || 9000 + }, + callback: function (nodemon) { + nodemon.on('log', function (event) { + console.log(event.colour); + }); + + // opens browser on initial server start + nodemon.on('config:update', function () { + setTimeout(function () { + require('open')('http://localhost:8080/debug?port=5858'); + }, 500); + }); + } + } + } + }, + + // Automatically inject Bower components into the app + wiredep: { + target: { + src: '<%= yeoman.client %>/index.html', + ignorePath: '<%= yeoman.client %>/', + exclude: [/bootstrap-sass-official/, /bootstrap.js/, '/json3/', '/es5-shim/', /bootstrap.css/, /font-awesome.css/] + } + }, + + // Renames files for browser caching purposes + rev: { + dist: { + files: { + src: [ + '<%= yeoman.dist %>/public/{,*/}*.js', + '<%= yeoman.dist %>/public/{,*/}*.css', + '<%= yeoman.dist %>/public/assets/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', + '<%= yeoman.dist %>/public/assets/fonts/*', + '!<%= yeoman.dist %>/public/assets/images/tmp/*' + ] + } + } + }, + + // Reads HTML for usemin blocks to enable smart builds that automatically + // concat, minify and revision files. Creates configurations in memory so + // additional tasks can operate on them + useminPrepare: { + html: ['<%= yeoman.client %>/index.html'], + options: { + dest: '<%= yeoman.dist %>/public' + } + }, + + // Performs rewrites based on rev and the useminPrepare configuration + usemin: { + html: ['<%= yeoman.dist %>/public/{,*/}*.html'], + css: ['<%= yeoman.dist %>/public/{,*/}*.css'], + js: ['<%= yeoman.dist %>/public/{,*/}*.js'], + options: { + assetsDirs: [ + '<%= yeoman.dist %>/public', + '<%= yeoman.dist %>/public/assets' + ], + // This is so we update image references in our ng-templates + patterns: { + js: [ + [/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the JS to reference our revved images'] + ] + } + } + }, + + // The following *-min tasks produce minified files in the dist folder + imagemin: { + dist: { + files: [{ + expand: true, + cwd: '<%= yeoman.client %>/assets/images', + src: '{,*/}*.{png,jpg,jpeg,gif}', + dest: '<%= yeoman.dist %>/public/assets/images' + }] + } + }, + + svgmin: { + dist: { + files: [{ + expand: true, + cwd: '<%= yeoman.client %>/assets/images', + src: '{,*/}*.svg', + dest: '<%= yeoman.dist %>/public/assets/images' + }] + } + }, + + // Allow the use of non-minsafe AngularJS files. Automatically makes it + // minsafe compatible so Uglify does not destroy the ng references + ngAnnotate: { + dist: { + files: [{ + expand: true, + cwd: '.tmp/concat', + src: '**/*.js', + dest: '.tmp/concat' + }] + } + }, + + // Package all the html partials into a single javascript payload + ngtemplates: { + app: { + options: { + prefix: '' + } + }, + options: { + // This should be the name of your apps angular module + module: 'ecompApp', + htmlmin: { + // collapseBooleanAttributes: true, // breaks "att-table-header sortable=false" + collapseWhitespace: true, + removeAttributeQuotes: true, + removeEmptyAttributes: true, + removeRedundantAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true + }, + usemin: 'app/app.js' + }, + main: { + cwd: '<%= yeoman.client %>', + src: ['{app,components}/**/*.html'], + dest: '.tmp/templates.js' + }, + tmp: { + cwd: '.tmp', + src: ['{app,components}/**/*.html'], + dest: '.tmp/tmp-templates.js' + } + }, + + // Replace Google CDN references + cdnify: { + dist: { + html: ['<%= yeoman.dist %>/public/*.html'] + } + }, + + // Copies remaining files to places other tasks can use + copy: { + dist: { + files: [{ + expand: true, + dot: true, + cwd: '<%= yeoman.client %>', + dest: '<%= yeoman.dist %>/public', + src: [ + '*.{ico,png,txt}', + '.htaccess', + 'bower_components/**/*', + 'bower_components_external/**/*', + 'assets/images/**/*', + 'assets/fonts/**/*', + 'kpi-dashboard/**/*', + 'index.html' + ] + }, { + expand: true, + cwd: '.tmp/images', + dest: '<%= yeoman.dist %>/public/assets/images', + src: ['generated/*'] + }, { + expand: true, + dest: '<%= yeoman.dist %>', + src: [ + 'package.json', + 'server/**/*' + ] + }] + }, + styles: { + expand: true, + cwd: '<%= yeoman.client %>', + dest: '.tmp/', + src: ['{app,components}/**/*.css'] + } + }, + + buildcontrol: { + options: { + dir: 'dist', + commit: true, + push: true, + connectCommits: false, + message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%' + }, + heroku: { + options: { + remote: 'heroku', + branch: 'master' + } + }, + openshift: { + options: { + remote: 'openshift', + branch: 'master' + } + } + }, + + // Run some tasks in parallel to speed up the build process + concurrent: { + server: [ + 'babel', + 'less', + ], + test: [ + 'babel', + 'less', + ], + debug: { + tasks: [ + 'nodemon' + //'node-inspector' + ], + options: { + logConcurrentOutput: true + } + }, + dist: [ + 'babel', + 'less', + //'imagemin', + 'svgmin' + ] + }, + + // Test settings + karma: { + unit: { + configFile: 'karma.conf.js', + singleRun: true + } + }, + + mochaTest: { + options: { + reporter: 'spec' + }, + src: ['server/**/*.spec.js'] + }, + + protractor: { + options: { + configFile: 'protractor.conf.js' + }, + chrome: { + options: { + args: { + browser: 'chrome' + } + } + } + }, + + env: { + test: { + NODE_ENV: 'test' + }, + prod: { + NODE_ENV: 'production' + }, + all: localConfig + }, + + // Compiles ES6 to JavaScript using Babel + babel: { + options: { + sourceMap: true + }, + server: { + files: [{ + expand: true, + cwd: 'client', + src: [ + '{app,components}/**/*.js', + '!{app,components}/**/*.spec.js' + ], + dest: '.tmp' + }] + } + }, + + // Compiles Less to CSS + less: { + options: { + paths: [ + '<%= yeoman.client %>/bower_components', + '<%= yeoman.client %>/app', + '<%= yeoman.client %>/components' + ] + }, + server: { + files: { + '.tmp/app/app.css': '<%= yeoman.client %>/app/app.less' + } + }, + }, + + injector: { + options: {}, + // Inject application script files into index.html (doesn't include bower) + scripts: { + options: { + transform: function (filePath) { + filePath = filePath.replace('/client/', ''); + filePath = filePath.replace('/.tmp/', ''); + return ''; + }, + starttag: '', + endtag: '' + }, + files: { + '<%= yeoman.client %>/index.html': [ + [ + + '.tmp/{app,components}/**/*.js', + + '!{.tmp,<%= yeoman.client %>}/app/app.js', + '!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.spec.js', + '!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.mock.js' + ] + ] + } + }, + + // Inject component less into app.less + less: { + options: { + transform: function (filePath) { + filePath = filePath.replace('/client/app/', ''); + filePath = filePath.replace('/client/components/', ''); + return '@import \'' + filePath + '\';'; + }, + starttag: '// injector', + endtag: '// endinjector' + }, + files: { + '<%= yeoman.client %>/app/app.less': [ + '<%= yeoman.client %>/{app,components}/**/*.less', + '!<%= yeoman.client %>/app/app.less' + ] + } + }, + + // Inject component css into index.html + css: { + options: { + transform: function (filePath) { + filePath = filePath.replace('/client/', ''); + filePath = filePath.replace('/.tmp/', ''); + return ''; + }, + starttag: '', + endtag: '' + }, + files: { + '<%= yeoman.client %>/index.html': [ + '<%= yeoman.client %>/{app,components}/**/*.css' + ] + } + } + }, + + //Project configuration task + ngconstant: { + main: { + options: { + dest: 'client/app/configurations.js', + name: 'ecomp.conf' + }, + constants: function () { + if (!grunt.option('env')) { + grunt.fail.warn('env parameter missing, please use --env=, ex: grunt serve --env=mock'); + } + + return { + conf: grunt.file.readJSON('client/configurations/' + grunt.option('env') + '.json') + } + } + } + }, + exec: { + delete: 'sshpass -p Aa123456 ssh root@remotehost rm -rf /var/lib/tomcat/webapps/ecompportal/public/', + copy: 'sshpass -p Aa123456 scp -r dist/public/ root@remotehost:/var/lib/tomcat/webapps/ecompportal/' + } + }); + + //Express server task + + // Used for delaying livereload until after server has restarted + grunt.registerTask('wait', function () { + grunt.log.ok('Waiting for server reload...'); + + var done = this.async(); + + setTimeout(function () { + grunt.log.writeln('Done waiting!'); + done(); + }, 1500); + }); + + grunt.registerTask('express-keepalive', 'Keep grunt running', function () { + this.async(); + }); + + grunt.registerTask('serve', function (target) { + + if (target === 'dist') { + return grunt.task.run(['build', 'env:all', 'env:prod', 'express:prod', 'express:mock', 'wait', 'open', 'express-keepalive']); + } + + if (target === 'debug') { + return grunt.task.run([ + 'clean:server', + 'env:all', + 'injector:less', + 'concurrent:server', + 'express:mock', + 'injector', + 'wiredep', + 'autoprefixer', + 'concurrent:debug' + ]); + } + + var isDebug = grunt.option('dev_debug'); + + grunt.task.run([ + 'clean:server', + 'env:all', + 'ngconstant', + 'injector:less', + 'concurrent:server', + 'injector', + 'wiredep', + 'autoprefixer', + isDebug ? 'express:dev_debug' : 'express:dev', + 'express:mock', + 'wait', + 'open', + 'watch' + ]); + }); + + grunt.registerTask('server', function () { + grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.'); + grunt.task.run(['serve']); + }); + + grunt.registerTask('test', function (target) { + if (target === 'server') { + return grunt.task.run([ + 'env:all', + 'env:test', + 'mochaTest' + ]); + } + + else if (target === 'client') { + return grunt.task.run([ + 'clean:server', + 'env:all', + 'injector:less', + 'concurrent:test', + 'injector', + 'ngtemplates:main', + 'autoprefixer', + 'karma' + ]); + } + + else if (target === 'e2e') { + return grunt.task.run([ + 'clean:server', + 'env:all', + 'env:test', + 'injector:less', + 'concurrent:test', + 'injector', + 'wiredep', + 'autoprefixer', + 'express:dev', + 'protractor' + ]); + } + + else grunt.task.run([ + 'test:server', + 'test:client' + ]); + }); + + grunt.registerTask('build', [ + 'clean:dist', + 'injector:less', + 'ngconstant', + 'concurrent:dist', + 'injector', + 'wiredep', + 'useminPrepare', + 'autoprefixer', + 'ngtemplates', + 'concat', + 'ngAnnotate', + 'copy:dist', + //'cdnify', + 'cssmin', + 'uglify', + 'rev', + 'usemin' + ]); + + grunt.registerTask('default', [ + 'newer:jshint', + 'test', + 'build' + ]); + + grunt.registerTask('deploy', [ + 'build', + 'exec' + ]); +}; diff --git a/ecomp-portal-FE/README.md b/ecomp-portal-FE/README.md new file mode 100644 index 00000000..962f7b09 --- /dev/null +++ b/ecomp-portal-FE/README.md @@ -0,0 +1,51 @@ +# readme file +## Initial installation +#### Prerequisites + +1. [node.js](https://nodejs.org/en/) - ssp project is running on node.js 4.1.1 please install from the link +2. bower - install with npm: `npm install -g bower` _NOTE: mac/linux users should run the following command with `sudo`_ +3. [git](https://git-scm.com/) - install git command line tool _NOTE: please select to add git command line into the PATH variable for it to be accessible from any folder_ +4. grunt - install grunt with npm: `npm install -g grunt-cli` _NOTE: mac/linux users should run the following command with `sudo`_ + + +#### First run +_NOTE: some npm modules inside the project does not support yet the new node.js 4.1.1, npm install command will probably throw an error. even so, everything should work, so no need to worry + +1. run `npm install` to install all the server side dependencies +2. run `bower install` to install all the client side dependencies + +## Running the project + +this project it supported by grunt task manager, below are the different run options +ssp project can be run with different client configurations per environment, the environment is set byt the option `--env=` +the following evironments are available: + +mock - mock will activate the mock server and will return static data examples from predefined json files. + +### Dev time + +1. to start the project in dev mode under mock please run: `grunt serve --env=mock` +2. for any other client configuration change the `--env` option accordingly to env options available + + +### Tests + +There are 3 options available for tests: + +1. `grunt test` will run all re server and the client tests all together +2. `grunt test:client` will run only the client tests +3. `grunt test:server` will run only the server tests + +### Build time + +Execute below commands + +npm cache clean +npm install +npm install -g bower +bower install +grunt build --env=integ + +COPY ${FE_DIR}/dist/public into ${BE_DIR}/public + +deploy BE project onto tomcat and goto login page http://portal.openecomp.org:8989/ECOMPPORTAL/login.htm diff --git a/ecomp-portal-FE/bower.json b/ecomp-portal-FE/bower.json new file mode 100644 index 00000000..68a039f4 --- /dev/null +++ b/ecomp-portal-FE/bower.json @@ -0,0 +1,36 @@ +{ + "name": "ecomp", + "version": "0.0.0", + "dependencies": { + "angular": "1.5.0", + "angular-animate": "1.5.0", + "angular-route": "1.5.0", + "angular-aria": "1.5.0", + "angular-messages": "~1.5.0", + "angular-material": "0.9.8", + "json3": "~3.3.1", + "es5-shim": "~4.5.8", + "angular-resource": "1.5.0", + "angular-cookies": "1.5.0", + "angular-sanitize": "1.5.0", + "lodash": "~4.13.1", + "angular-ui-router": "~0.2.18", + "jquery": "~2.2.4", + "ui-select": "angular-ui-select#~0.17.1", + "angular-smart-table": "~2.1.8", + "angular-gestures": "latest", + "angular-cache": "~4.6.0", + "ng-dialog": "~0.6.1", + "jqTree": "jqtree#^1.3.3", + "angular-uuid": "^0.1.0", + "angular-bootstrap": "^2.0.0", + "jquery.newstape": "1.0.0" + }, + "devDependencies": { + "angular-mocks": "~1.5.0", + "angular-scenario": "~1.5.0" + }, + "resolutions": { + "angular": "1.5.0" + } +} diff --git a/ecomp-portal-FE/build-ecomportal-fe.sh b/ecomp-portal-FE/build-ecomportal-fe.sh new file mode 100644 index 00000000..8fc199d5 --- /dev/null +++ b/ecomp-portal-FE/build-ecomportal-fe.sh @@ -0,0 +1,181 @@ +#!/bin/sh + +# Script name : build_ecompportal_fe.sh +# Script purpose : To have an easy way to build the front-end part of the eComp portal +# Pre requisites : +# 1. Your home directory must reside at /home +# 2. Your server must have a 'Node' installation +#---------------------------------------------------------------------------------------- + + +################################################ +### Functions +################################################ +function log_message() { +msgType=$1 +message=$2 + +if [ ${msgType} == "I" ]; then +printf "\033[32m %s \n\033[0m" "INF - ${message}" +elif [ ${msgType} == "E" ]; then +printf "\033[31m %s \n\033[0m" "ERR - ${message}" +else +echo "${msgType} - ${message}"; +fi +} + + + +function exit_with_error() { + +log_message "E" "" +log_message "E" "$1" +log_message "E" "" + +exit 1 +} + + + +################################################ +### Hard coded information. +################################################ +NVM_DIR="/home/${USER}/.nvm" +NODE_VERSION=v0.12.4 +SCRIPT_USAGE="USAGE: $0 [ dev | ci | integ | qa ]" + + + +################################################ +### Verify arguments +################################################ +log_message "I" "Checking command line arguments." +if [ $# == 1 ]; then +if [ $1 == "ci" -o $1 == "integ" -o $1 == "dev" -o $1 == "qa" ]; then +BUILD_BY_ENV=$1 +else +exit_with_error "The environment '$1' is invalid." +fi +else +log_message "E" "" +log_message "E" "$SCRIPT_USAGE" +log_message "E" "" +exit 1 +fi +log_message "I" "OK." +log_message "I" "" + + + +################################################ +### Set the node environment. +################################################ +log_message "I" "Set the node environment." +if [ -s "$NVM_DIR/nvm.sh" ]; then +# This loads nvm +. "$NVM_DIR/nvm.sh" +if [ $? != 0 ]; then +exit_with_error "Cannot load the NODE env." +fi +else +exit_with_error "The nvm.sh script does not exist." +fi +log_message "I" "OK." +log_message "I" "" + + + +################################################ +### Set the node version manager version. +################################################ +log_message "I" "Set the node version manager version." +nvm use v0.12.4 +TOOLS_ROOT_FOLDER=${NVM_DIR}/versions/node/${NODE_VERSION}/bin +log_message "I" "OK." +log_message "I" "" + + + +################################################ +### Set the proxy servers. +################################################ +log_message "I" "Set the proxy servers." +log_message "I" "OK." +log_message "I" "" + + + +################################################ +### Install bower, if neeeded. +################################################ +log_message "I" "Install bower, if neeeded." +if [ ! -e ${TOOLS_ROOT_FOLDER}/bower ]; then +npm install -g bower +if [ $? != 0 ]; then +exit_with_error "Cannot install bower." +fi +fi +log_message "I" "OK." +log_message "I" "" + + + +################################################ +### Install grunt, if neeeded. +################################################ +log_message "I" "Install grunt, if neeeded." +if [ ! -e ${TOOLS_ROOT_FOLDER}/grunt ]; then +npm config set ca "" +npm install -g grunt-cli + +if [ $? != 0 ]; then +exit_with_error "Cannot install grunt." +fi +fi +log_message "I" "OK." +log_message "I" "" + + + +################################################ +### Run the Node package manager (NPM). +################################################ +log_message "I" "Run the Node package manager (npm install)." +npm install +if [ $? != 0 ]; then +exit_with_error "Cannot run 'npm install'." +fi +log_message "I" "OK." +log_message "I" "" + + + +################################################ +### Install the Bower components. +################################################ +log_message "I" "Install the Bower components." +bower install +if [ $? != 0 ]; then +exit_with_error "Cannot run 'npm install'." +fi +log_message "I" "OK." +log_message "I" "" + + + +################################################ +### Build the application. +################################################ +log_message "I" "Build the application." +grunt build --env=${BUILD_BY_ENV} +if [ $? != 0 ]; then +exit_with_error "Cannot run 'grunt build --env=${BUILD_BY_ENV}'." +fi +log_message "I" "OK." +log_message "I" "" + + + +log_message "I" "" +log_message "I" "Done." +log_message "I" "" \ No newline at end of file diff --git a/ecomp-portal-FE/client/.htaccess b/ecomp-portal-FE/client/.htaccess new file mode 100644 index 00000000..cb84cb91 --- /dev/null +++ b/ecomp-portal-FE/client/.htaccess @@ -0,0 +1,543 @@ +# Apache Configuration File + +# (!) Using `.htaccess` files slows down Apache, therefore, if you have access +# to the main server config file (usually called `httpd.conf`), you should add +# this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html. + +# ############################################################################## +# # CROSS-ORIGIN RESOURCE SHARING (CORS) # +# ############################################################################## + +# ------------------------------------------------------------------------------ +# | Cross-domain AJAX requests | +# ------------------------------------------------------------------------------ + +# Enable cross-origin AJAX requests. +# http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity +# http://enable-cors.org/ + +# +# Header set Access-Control-Allow-Origin "*" +# + +# ------------------------------------------------------------------------------ +# | CORS-enabled images | +# ------------------------------------------------------------------------------ + +# Send the CORS header for images when browsers request it. +# https://developer.mozilla.org/en/CORS_Enabled_Image +# http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html +# http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/ + + + + + SetEnvIf Origin ":" IS_CORS + Header set Access-Control-Allow-Origin "*" env=IS_CORS + + + + +# ------------------------------------------------------------------------------ +# | Web fonts access | +# ------------------------------------------------------------------------------ + +# Allow access from all domains for web fonts + + + + Header set Access-Control-Allow-Origin "*" + + + + +# ############################################################################## +# # ERRORS # +# ############################################################################## + +# ------------------------------------------------------------------------------ +# | 404 error prevention for non-existing redirected folders | +# ------------------------------------------------------------------------------ + +# Prevent Apache from returning a 404 error for a rewrite if a directory +# with the same name does not exist. +# http://httpd.apache.org/docs/current/content-negotiation.html#multiviews +# http://www.webmasterworld.com/apache/3808792.htm + +Options -MultiViews + +# ------------------------------------------------------------------------------ +# | Custom error messages / pages | +# ------------------------------------------------------------------------------ + +# You can customize what Apache returns to the client in case of an error (see +# http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.: + +ErrorDocument 404 /404.html + + +# ############################################################################## +# # INTERNET EXPLORER # +# ############################################################################## + +# ------------------------------------------------------------------------------ +# | Better website experience | +# ------------------------------------------------------------------------------ + +# Force IE to render pages in the highest available mode in the various +# cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf. + + + Header set X-UA-Compatible "IE=edge" + # `mod_headers` can't match based on the content-type, however, we only + # want to send this header for HTML pages and not for the other resources + + Header unset X-UA-Compatible + + + +# ------------------------------------------------------------------------------ +# | Cookie setting from iframes | +# ------------------------------------------------------------------------------ + +# Allow cookies to be set from iframes in IE. + +# +# Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" +# + +# ------------------------------------------------------------------------------ +# | Screen flicker | +# ------------------------------------------------------------------------------ + +# Stop screen flicker in IE on CSS rollovers (this only works in +# combination with the `ExpiresByType` directives for images from below). + +# BrowserMatch "MSIE" brokenvary=1 +# BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1 +# BrowserMatch "Opera" !brokenvary +# SetEnvIf brokenvary 1 force-no-vary + + +# ############################################################################## +# # MIME TYPES AND ENCODING # +# ############################################################################## + +# ------------------------------------------------------------------------------ +# | Proper MIME types for all files | +# ------------------------------------------------------------------------------ + + + + # Audio + AddType audio/mp4 m4a f4a f4b + AddType audio/ogg oga ogg + + # JavaScript + # Normalize to standard type (it's sniffed in IE anyways): + # http://tools.ietf.org/html/rfc4329#section-7.2 + AddType application/javascript js jsonp + AddType application/json json + + # Video + AddType video/mp4 mp4 m4v f4v f4p + AddType video/ogg ogv + AddType video/webm webm + AddType video/x-flv flv + + # Web fonts + AddType application/font-woff woff + AddType application/vnd.ms-fontobject eot + + # Browsers usually ignore the font MIME types and sniff the content, + # however, Chrome shows a warning if other MIME types are used for the + # following fonts. + AddType application/x-font-ttf ttc ttf + AddType font/opentype otf + + # Make SVGZ fonts work on iPad: + # https://twitter.com/FontSquirrel/status/14855840545 + AddType image/svg+xml svg svgz + AddEncoding gzip svgz + + # Other + AddType application/octet-stream safariextz + AddType application/x-chrome-extension crx + AddType application/x-opera-extension oex + AddType application/x-shockwave-flash swf + AddType application/x-web-app-manifest+json webapp + AddType application/x-xpinstall xpi + AddType application/xml atom rdf rss xml + AddType image/webp webp + AddType image/x-icon ico + AddType text/cache-manifest appcache manifest + AddType text/vtt vtt + AddType text/x-component htc + AddType text/x-vcard vcf + + + +# ------------------------------------------------------------------------------ +# | UTF-8 encoding | +# ------------------------------------------------------------------------------ + +# Use UTF-8 encoding for anything served as `text/html` or `text/plain`. +AddDefaultCharset utf-8 + +# Force UTF-8 for certain file formats. + + AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml + + + +# ############################################################################## +# # URL REWRITES # +# ############################################################################## + +# ------------------------------------------------------------------------------ +# | Rewrite engine | +# ------------------------------------------------------------------------------ + +# Turning on the rewrite engine and enabling the `FollowSymLinks` option is +# necessary for the following directives to work. + +# If your web host doesn't allow the `FollowSymlinks` option, you may need to +# comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the +# performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks + +# Also, some cloud hosting services require `RewriteBase` to be set: +# http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site + + + Options +FollowSymlinks + # Options +SymLinksIfOwnerMatch + RewriteEngine On + # RewriteBase / + + +# ------------------------------------------------------------------------------ +# | Suppressing / Forcing the "www." at the beginning of URLs | +# ------------------------------------------------------------------------------ + +# The same content should never be available under two different URLs especially +# not with and without "www." at the beginning. This can cause SEO problems +# (duplicate content), therefore, you should choose one of the alternatives and +# redirect the other one. + +# By default option 1 (no "www.") is activated: +# http://no-www.org/faq.php?q=class_b + +# If you'd prefer to use option 2, just comment out all the lines from option 1 +# and uncomment the ones from option 2. + +# IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME! + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Option 1: rewrite www.example.com → example.com + + + RewriteCond %{HTTPS} !=on + RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] + RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Option 2: rewrite example.com → www.example.com + +# Be aware that the following might not be a good idea if you use "real" +# subdomains for certain parts of your website. + +# +# RewriteCond %{HTTPS} !=on +# RewriteCond %{HTTP_HOST} !^www\..+$ [NC] +# RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] +# + + +# ############################################################################## +# # SECURITY # +# ############################################################################## + +# ------------------------------------------------------------------------------ +# | Content Security Policy (CSP) | +# ------------------------------------------------------------------------------ + +# You can mitigate the risk of cross-site scripting and other content-injection +# attacks by setting a Content Security Policy which whitelists trusted sources +# of content for your site. + +# The example header below allows ONLY scripts that are loaded from the current +# site's origin (no inline scripts, no CDN, etc). This almost certainly won't +# work as-is for your site! + +# To get all the details you'll need to craft a reasonable policy for your site, +# read: http://html5rocks.com/en/tutorials/security/content-security-policy (or +# see the specification: http://w3.org/TR/CSP). + +# +# Header set Content-Security-Policy "script-src 'self'; object-src 'self'" +# +# Header unset Content-Security-Policy +# +# + +# ------------------------------------------------------------------------------ +# | File access | +# ------------------------------------------------------------------------------ + +# Block access to directories without a default document. +# Usually you should leave this uncommented because you shouldn't allow anyone +# to surf through every directory on your server (which may includes rather +# private places like the CMS's directories). + + + Options -Indexes + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Block access to hidden files and directories. +# This includes directories used by version control systems such as Git and SVN. + + + RewriteCond %{SCRIPT_FILENAME} -d [OR] + RewriteCond %{SCRIPT_FILENAME} -f + RewriteRule "(^|/)\." - [F] + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Block access to backup and source files. +# These files may be left by some text editors and can pose a great security +# danger when anyone has access to them. + + + Order allow,deny + Deny from all + Satisfy All + + +# ------------------------------------------------------------------------------ +# | Secure Sockets Layer (SSL) | +# ------------------------------------------------------------------------------ + +# Rewrite secure requests properly to prevent SSL certificate warnings, e.g.: +# prevent `https://www.example.com` when your certificate only allows +# `https://secure.example.com`. + +# +# RewriteCond %{SERVER_PORT} !^443 +# RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L] +# + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Force client-side SSL redirection. + +# If a user types "example.com" in his browser, the above rule will redirect him +# to the secure version of the site. That still leaves a window of opportunity +# (the initial HTTP connection) for an attacker to downgrade or redirect the +# request. The following header ensures that browser will ONLY connect to your +# server via HTTPS, regardless of what the users type in the address bar. +# http://www.html5rocks.com/en/tutorials/security/transport-layer-security/ + +# +# Header set Strict-Transport-Security max-age=16070400; +# + +# ------------------------------------------------------------------------------ +# | Server software information | +# ------------------------------------------------------------------------------ + +# Avoid displaying the exact Apache version number, the description of the +# generic OS-type and the information about Apache's compiled-in modules. + +# ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`! + +# ServerTokens Prod + + +# ############################################################################## +# # WEB PERFORMANCE # +# ############################################################################## + +# ------------------------------------------------------------------------------ +# | Compression | +# ------------------------------------------------------------------------------ + + + + # Force compression for mangled headers. + # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping + + + SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding + RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding + + + + # Compress all output labeled with one of the following MIME-types + # (for Apache versions below 2.3.7, you don't need to enable `mod_filter` + # and can remove the `` and `` lines + # as `AddOutputFilterByType` is still in the core directives). + + AddOutputFilterByType DEFLATE application/atom+xml \ + application/javascript \ + application/json \ + application/rss+xml \ + application/vnd.ms-fontobject \ + application/x-font-ttf \ + application/x-web-app-manifest+json \ + application/xhtml+xml \ + application/xml \ + font/opentype \ + image/svg+xml \ + image/x-icon \ + text/css \ + text/html \ + text/plain \ + text/x-component \ + text/xml + + + + +# ------------------------------------------------------------------------------ +# | Content transformations | +# ------------------------------------------------------------------------------ + +# Prevent some of the mobile network providers from modifying the content of +# your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5. + +# +# Header set Cache-Control "no-transform" +# + +# ------------------------------------------------------------------------------ +# | ETag removal | +# ------------------------------------------------------------------------------ + +# Since we're sending far-future expires headers (see below), ETags can +# be removed: http://developer.yahoo.com/performance/rules.html#etags. + +# `FileETag None` is not enough for every server. + + Header unset ETag + + +FileETag None + +# ------------------------------------------------------------------------------ +# | Expires headers (for better cache control) | +# ------------------------------------------------------------------------------ + +# The following expires headers are set pretty far in the future. If you don't +# control versioning with filename-based cache busting, consider lowering the +# cache time for resources like CSS and JS to something like 1 week. + + + + ExpiresActive on + ExpiresDefault "access plus 1 month" + + # CSS + ExpiresByType text/css "access plus 1 year" + + # Data interchange + ExpiresByType application/json "access plus 0 seconds" + ExpiresByType application/xml "access plus 0 seconds" + ExpiresByType text/xml "access plus 0 seconds" + + # Favicon (cannot be renamed!) + ExpiresByType image/x-icon "access plus 1 week" + + # HTML components (HTCs) + ExpiresByType text/x-component "access plus 1 month" + + # HTML + ExpiresByType text/html "access plus 0 seconds" + + # JavaScript + ExpiresByType application/javascript "access plus 1 year" + + # Manifest files + ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds" + ExpiresByType text/cache-manifest "access plus 0 seconds" + + # Media + ExpiresByType audio/ogg "access plus 1 month" + ExpiresByType image/gif "access plus 1 month" + ExpiresByType image/jpeg "access plus 1 month" + ExpiresByType image/png "access plus 1 month" + ExpiresByType video/mp4 "access plus 1 month" + ExpiresByType video/ogg "access plus 1 month" + ExpiresByType video/webm "access plus 1 month" + + # Web feeds + ExpiresByType application/atom+xml "access plus 1 hour" + ExpiresByType application/rss+xml "access plus 1 hour" + + # Web fonts + ExpiresByType application/font-woff "access plus 1 month" + ExpiresByType application/vnd.ms-fontobject "access plus 1 month" + ExpiresByType application/x-font-ttf "access plus 1 month" + ExpiresByType font/opentype "access plus 1 month" + ExpiresByType image/svg+xml "access plus 1 month" + + + +# ------------------------------------------------------------------------------ +# | Filename-based cache busting | +# ------------------------------------------------------------------------------ + +# If you're not using a build process to manage your filename version revving, +# you might want to consider enabling the following directives to route all +# requests such as `/css/style.12345.css` to `/css/style.css`. + +# To understand why this is important and a better idea than `*.css?v231`, read: +# http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring + +# +# RewriteCond %{REQUEST_FILENAME} !-f +# RewriteCond %{REQUEST_FILENAME} !-d +# RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L] +# + +# ------------------------------------------------------------------------------ +# | File concatenation | +# ------------------------------------------------------------------------------ + +# Allow concatenation from within specific CSS and JS files, e.g.: +# Inside of `script.combined.js` you could have +# +# +# and they would be included into this single file. + +# +# +# Options +Includes +# AddOutputFilterByType INCLUDES application/javascript application/json +# SetOutputFilter INCLUDES +# +# +# Options +Includes +# AddOutputFilterByType INCLUDES text/css +# SetOutputFilter INCLUDES +# +# + +# ------------------------------------------------------------------------------ +# | Persistent connections | +# ------------------------------------------------------------------------------ + +# Allow multiple requests to be sent over the same TCP connection: +# http://httpd.apache.org/docs/current/en/mod/core.html#keepalive. + +# Enable if you serve a lot of static content but, be aware of the +# possible disadvantages! + +# +# Header set Connection Keep-Alive +# diff --git a/ecomp-portal-FE/client/.jshintrc b/ecomp-portal-FE/client/.jshintrc new file mode 100644 index 00000000..8c5c42c0 --- /dev/null +++ b/ecomp-portal-FE/client/.jshintrc @@ -0,0 +1,36 @@ +{ + "node": true, + "browser": true, + "esnext": true, + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "immed": true, + "latedef": true, + "newcap": true, + "noarg": true, + "quotmark": "single", + "undef": true, + "unused": true, + "strict": true, + "trailing": true, + "smarttabs": true, + "globals": { + "jQuery": true, + "angular": true, + "console": true, + "$": true, + "_": true, + "moment": true, + "describe": true, + "beforeEach": true, + "module": true, + "inject": true, + "it": true, + "expect": true, + "browser": true, + "element": true, + "by": true + } +} diff --git a/ecomp-portal-FE/client/app/app.js b/ecomp-portal-FE/client/app/app.js new file mode 100644 index 00000000..ea06d9aa --- /dev/null +++ b/ecomp-portal-FE/client/app/app.js @@ -0,0 +1,120 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +var app = angular.module('ecompApp', [ + 'ngCookies', + 'ngResource', + 'ngSanitize', + 'ui.router', + 'ecomp.conf', + 'ngMessages', + 'ui.select', + 'angular-cache', + 'ngDialog', + 'att.abs', + 'att.gridster', + 'uuid', + 'ui.bootstrap', + 'ngMaterial' + ]) + .config(($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider, ngDialogProvider) => { + $urlRouterProvider.otherwise('/error404'); + $locationProvider.html5Mode(true); + + //initialize get if not there + if (!$httpProvider.defaults.headers.get) { + $httpProvider.defaults.headers.get = {}; + } + + //withCredentials flag on the XHR object - add cookie to XHR requests + $httpProvider.defaults.withCredentials = true; + $httpProvider.defaults.useXDoain = true; + $httpProvider.defaults.timeout = 30000; + + //default configuration for ngDialog modal + ngDialogProvider.setDefaults({ + className: 'ngdialog-theme-default', + showClose: true, + closeByDocument: false, + closeByEscape: false + }); + + + var myHostName; + myHostName = location.host; + + if (!(myHostName.includes("localhost"))) { + $httpProvider.defaults.headers.get['If-Modified-Since'] = 'Mon, 26 Jul 1997 05:00:00 GMT'; + $httpProvider.defaults.headers.get['Cache-Control'] = 'no-cache'; + $httpProvider.defaults.headers.get['Pragma'] = 'no-cache'; + } + + + //interceptor here: + var interceptor = function ($q, $injector, $log) { + return { + 'responseError': function (rejection) { + $log.error('Interceptor rejection: ' + JSON.stringify(rejection)); + var $state = $injector.get('$state'); + switch (rejection.status) { + case 401: + var globalLoginUrl = rejection.headers()['global-login-url']; + if (globalLoginUrl) { + window.location = globalLoginUrl + + (globalLoginUrl.indexOf('?') === -1 ? '?' : '') + + '&retUrl=' + encodeURI(window.location); + return; + } + break; + case 403: + // $state.go('root.applicationsHome'); + //handle forbidden error + break; + // case 404: + // $state.go('/login.htm'); + // //handle forbidden error + // break; + default: + //handle internal server error + } + return $q.reject(rejection); + } + }; + }; + $httpProvider.interceptors.push(interceptor); + + }).run(($http, CacheFactory) => { + //default configuration for cache factory + $http.defaults.cache = CacheFactory('defaultCache', { + maxAge: 15 * 60 * 1000, // Items added to this cache expire after 15 minutes + cacheFlushInterval: 60 * 60 * 1000, // This cache will clear itself every hour + deleteOnExpire: 'aggressive' // Items will be deleted from this cache when they expire + }); + }); + + +angular.module( 'ecompApp' ).config( [ +'$compileProvider', +function( $compileProvider ) +{ + $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension):/); +} +]); diff --git a/ecomp-portal-FE/client/app/app.less b/ecomp-portal-FE/client/app/app.less new file mode 100644 index 00000000..4c8ac018 --- /dev/null +++ b/ecomp-portal-FE/client/app/app.less @@ -0,0 +1,78 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +// injector +@import 'directives/left-menu/left-menu.less'; +@import 'directives/multiple-select/multiple-select.less'; +@import 'directives/right-menu/right-menu.less'; +@import 'directives/search-users/search-users.less'; +@import 'styles/att-abs.less'; +@import 'styles/buttons.less'; +@import 'styles/ecomp-general.less'; +@import 'styles/fonts.less'; +@import 'styles/form.less'; +@import 'styles/kpi-dashboard.less'; +@import 'styles/mixins.less'; +@import 'styles/ng-dialog.less'; +@import 'styles/reset.less'; +@import 'styles/select2.less'; +@import 'styles/spinner.less'; +@import 'styles/sprites.less'; +@import 'styles/variables.less'; +@import 'views/admins/add-admin-dialogs/new-admin.modal.less'; +@import 'views/admins/admins.less'; +@import 'views/applications/application-details-dialog/application-details.modal.less'; +@import 'views/applications/applications.less'; +@import 'views/catalog/catalog.less'; +@import 'views/confirmation-box/confirmation-box.less'; +@import 'views/dashboard/dashboard-widget.controller.less'; +@import 'views/dashboard/dashboard.less'; +@import 'views/footer/footer.less'; +@import 'views/functionalMenu/functionalMenu-dialog/modal-details.modal.less'; +@import 'views/functionalMenu/functionalMenu.less'; +@import 'views/header/header.less'; +@import 'views/home/applications-home/applications-home.less'; +@import 'views/home/widgets-home/widgets-home.less'; +@import 'views/portal-admin/new-portal-admin/new-portal-admin.modal.less'; +@import 'views/portal-admin/portal-admins.less'; +@import 'views/search/search.less'; +@import 'views/sidebar/sidebar.less'; +@import 'views/support/contact-us/contact-us-manage/contact-us-manage.controller.less'; +@import 'views/support/contact-us/contact-us.less'; +@import 'views/support/get-access/get-access.less'; +@import 'views/tabs/tabs.less'; +@import 'views/userbar/userbar.less'; +@import 'views/users/new-user-dialogs/new-user.modal.less'; +@import 'views/users/users.less'; +@import 'views/widgets/widget-details-dialog/widget-details.modal.less'; +@import 'views/widgets/widgets.less'; +// endinjector + +html { height: 100%; overflow-y: hidden;} +* { box-sizing: border-box; } +body { + font: normal 12px arial; +} + +/* Responsive: Portrait tablets and up */ +@media screen and (min-width: 768px) { + .container { + //max-width: 730px; + } +} diff --git a/ecomp-portal-FE/client/app/configurations.js b/ecomp-portal-FE/client/app/configurations.js new file mode 100644 index 00000000..0c417914 --- /dev/null +++ b/ecomp-portal-FE/client/app/configurations.js @@ -0,0 +1,5 @@ +angular.module('ecomp.conf', []) + +.constant('conf', {api:{userApps:'http://www.ecomp.att.com:8080/ecompportal/portalApi/userApps',persUserApps:'http://www.ecomp.att.com:8080/ecompportal/portalApi/persUserApps',appCatalog:'http://www.ecomp.att.com:8080/ecompportal/portalApi/appCatalog',accountAdmins:'http://www.ecomp.att.com:8080/ecompportal/portalApi/accountAdmins',availableApps:'http://www.ecomp.att.com:8080/ecompportal/portalApi/availableApps',allAvailableApps:'http://www.ecomp.att.com:8080/ecompportal/portalApi/allAvailableApps',userProfile:'http://www.ecomp.att.com:8080/ecompportal/portalApi/userProfile',currentUserProfile:'http://www.ecomp.att.com:8080/ecompportal/portalApi/currentUserProfile',queryUsers:'http://www.ecomp.att.com:8080/ecompportal/portalApi/queryUsers',adminAppsRoles:'http://www.ecomp.att.com:8080/ecompportal/portalApi/adminAppsRoles',adminApps:'http://www.ecomp.att.com:8080/ecompportal/portalApi/adminApps',appsForSuperAdminAndAccountAdmin:'http://www.ecomp.att.com:8080/ecompportal/portalApi/appsForSuperAdminAndAccountAdmin',accountUsers:'http://www.ecomp.att.com:8080/ecompportal/portalApi/app/:appId/users',saveNewUser:'http://www.ecomp.att.com:8080/ecompportal/portalApi/saveNewUser',userAppRoles:'http://www.ecomp.att.com:8080/ecompportal/portalApi/userAppRoles',updateRemoteUserProfile:'http://www.ecomp.att.com:8080/ecompportal/portalApi/updateRemoteUserProfile',onboardingApps:'http://www.ecomp.att.com:8080/ecompportal/portalApi/onboardingApps',widgets:'http://www.ecomp.att.com:8080/ecompportal/portalApi/widgets',widgetsValidation:'http://www.ecomp.att.com:8080/ecompportal/portalApi/widgets/validation',functionalMenuForAuthUser:'http://www.ecomp.att.com:8080/ecompportal/portalApi/functionalMenuForAuthUser',functionalMenuForEditing:'http://www.ecomp.att.com:8080/ecompportal/portalApi/functionalMenuForEditing',functionalMenu:'http://www.ecomp.att.com:8080/ecompportal/portalApi/functionalMenu',functionalMenuItemDetails:'http://www.ecomp.att.com:8080/ecompportal/portalApi/functionalMenuItemDetails/:menuId',appRoles:'http://www.ecomp.att.com:8080/ecompportal/portalApi/appRoles/:appId',functionalMenuItem:'http://www.ecomp.att.com:8080/ecompportal/portalApi/functionalMenuItem',regenerateFunctionalMenuAncestors:'http://www.ecomp.att.com:8080/ecompportal/portalApi/regenerateFunctionalMenuAncestors',listOfApp:'http://www.ecomp.att.com:8080/ecompportal/portalApi/getAppList',setFavoriteItem:'http://www.ecomp.att.com:8080/ecompportal/portalApi/setFavoriteItem',getFavoriteItems:'http://www.ecomp.att.com:8080/ecompportal/portalApi/getFavoriteItems',removeFavoriteItem:'http://www.ecomp.att.com:8080/ecompportal/portalApi/removeFavoriteItem/:menuId',ping:'http://www.ecomp.att.com:8080/ecompportal/portalApi/ping',functionalMenuStaticInfo:'http://www.ecomp.att.com:8080/ecompportal/portalApi/functionalMenuStaticInfo',resetFunctionalMenuStaticInfo:'http://www.ecomp.att.com:8080/ecompportal/portalApi/resetFunctionalMenuStaticInfo',portalAdmins:'http://www.ecomp.att.com:8080/ecompportal/portalApi/portalAdmins',portalAdmin:'http://www.ecomp.att.com:8080/ecompportal/portalApi/portalAdmin',getKpiDashUserStoriesStats:'http://www.ecomp.att.com:8080/ecompportal/portalApi/get_user_stories_stats',getKpiDashUserApiStats:'http://www.ecomp.att.com:8080/ecompportal/portalApi/get_user_api_stats',getKpiDashLocStats:'http://www.ecomp.att.com:8080/ecompportal/portalApi/get_loc_stats',getKpiDashLocStatsCat:'http://www.ecomp.att.com:8080/ecompportal/portalApi/get_loc_stats_cat',getKpiDashServiceSupported:'http://www.ecomp.att.com:8080/ecompportal/portalApi/get_kpi_service_supported',getKpiDashPublishedDelivered:'http://www.ecomp.att.com:8080/ecompportal/portalApi/execute_get_published_delivered',getKpiDashFeedStats:'http://www.ecomp.att.com:8080/ecompportal/portalApi/execute_get_feed_stats',getKpiDashUserApis:'http://www.ecomp.att.com:8080/ecompportal/portalApi/get_user_api_stats',getKpiDashGeoMapUrl:'http://www.ecomp.att.com:8080/ecompportal/portalApi/get_geo_map_url',getKpiDashRCloudAUrl:'http://www.ecomp.att.com:8080/ecompportal/portalApi/get_rcloud_a_url',getKpiDashGeoMapApiUrl:'http://www.ecomp.att.com:8080/ecompportal/portalApi/get_geo_map_api_url',getManifest:'http://www.ecomp.att.com:8080/ecompportal/portalApi/manifest',getActiveUser:'http://www.ecomp.att.com:8080/ecompportal/portalApi/dashboard/activeUsers',getSearchAllByStringResults:'http://www.ecomp.att.com:8080/ecompportal/portalApi/dashboard/search',commonWidget:'http://www.ecomp.att.com:8080/ecompportal/portalApi/dashboard/widgetData',deleteCommonWidget:'http://www.ecomp.att.com:8080/ecompportal/portalApi/dashboard/deleteData',getContactUS:'http://www.ecomp.att.com:8080/ecompportal/portalApi/contactus/list',getAppsAndContacts:'http://www.ecomp.att.com:8080/ecompportal/portalApi/contactus/allapps',saveContactUS:'http://www.ecomp.att.com:8080/ecompportal/portalApi/contactus/save',deleteContactUS:'http://www.ecomp.att.com:8080/ecompportal/portalApi/contactus/delete',getContactUSPortalDetails:'http://www.ecomp.att.com:8080/ecompportal/portalApi/contactus/feedback',getAppCategoryFunctions:'http://www.ecomp.att.com:8080/ecompportal/portalApi/contactus/functions',onlineUserUpdateRate:'http://www.ecomp.att.com:8080/ecompportal/portalApi/dashboard/onlineUserUpdateRate',getWidthThresholdLeftMenu:'http://www.ecomp.att.com:8080/ecompportal/portalApi/dashboard/windowWidthThresholdLeftMenu',getWidthThresholdRightMenu:'http://www.ecomp.att.com:8080/ecompportal/portalApi/dashboard/windowWidthThresholdRightMenu'},cookieDomain:'att.com'}) + +; \ No newline at end of file diff --git a/ecomp-portal-FE/client/app/directives/auto-focus/auto-focus.directive.js b/ecomp-portal-FE/client/app/directives/auto-focus/auto-focus.directive.js new file mode 100644 index 00000000..73964115 --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/auto-focus/auto-focus.directive.js @@ -0,0 +1,46 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + + + +//angular.module('ecompApp') +// .directive('autoFocus', function () { +// return { +// restrict: 'A', +// scope: {}, +// link: function (scope, element) { +// element[0].focus(); +// } +// }; +// }); + +/* istanbul ignore next */ +(function(){ + class AutoFocusDirective{ + constructor(){ + this.restrict = 'A'; + this.link = this._link.bind(this); + } + _link(scope, element){ + element[0].focus(); + } + } + angular.module('ecompApp').directive('autoFocus', () => new AutoFocusDirective()); +})(); diff --git a/ecomp-portal-FE/client/app/directives/image-upload/image-upload.directive.js b/ecomp-portal-FE/client/app/directives/image-upload/image-upload.directive.js new file mode 100644 index 00000000..90c88c50 --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/image-upload/image-upload.directive.js @@ -0,0 +1,222 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; + +angular.module('ecompApp').directive('imageUpload', function factory($q) { + var imageMimeRgx = /^image\/[a-zA-Z0-9]*$/; + + var URL = window.URL || window.webkitURL; + + var getResizeArea = function () { + var resizeAreaId = 'fileupload-resize-area'; + + var resizeArea = document.getElementById(resizeAreaId); + + if (!resizeArea) { + resizeArea = document.createElement('canvas'); + resizeArea.id = resizeAreaId; + resizeArea.style.visibility = 'hidden'; + document.body.appendChild(resizeArea); + } + + return resizeArea; + }; + + var resizeImage = function (origImage, options) { + var maxHeight = options.resizeMaxHeight || 300; + var maxWidth = options.resizeMaxWidth || 250; + var quality = options.resizeQuality || 0.7; + var type = options.resizeType || 'image/jpg'; + + var canvas = getResizeArea(); + + var height = origImage.height; + var width = origImage.width; + + //image redraw starting points + var x0, y0; + + // calculate the width and height, constraining the proportions + if (width > height) { + if (width > maxWidth) { + height = Math.round(height *= maxWidth / width); + width = maxWidth; + + x0 = 0; + y0 = Math.round((maxHeight - height)/2); + }else{ + maxHeight = height; + maxWidth = width; + x0 = 0; + y0 = 0; + } + } else { + if (height > maxHeight) { + width = Math.round(width *= maxHeight / height); + height = maxHeight; + + x0 = Math.round((maxWidth - width)/2); + y0 = 0; + }else{ + maxHeight = height; + maxWidth = width; + x0 = 0; + y0 = 0; + } + } + + canvas.width = maxWidth; + canvas.height = maxHeight; + + //draw image on canvas + var ctx = canvas.getContext("2d"); + + //set background color + if(options.backgroundColor){ + ctx.fillStyle = options.backgroundColor; + ctx.fillRect(0,0,maxWidth,maxHeight); + } + + + ctx.drawImage(origImage, x0, y0, width, height); + + // get the data from canvas as 70% jpg (or specified type). + return canvas.toDataURL(type, quality); + }; + + var createImage = function(url, callback) { + var image = new Image(); + image.onload = function() { + callback(image); + }; + image.src = url; + }; + + var fileToDataURL = function (file) { + var deferred = $q.defer(); + var reader = new FileReader(); + reader.onload = function (e) { + deferred.resolve(e.target.result); + }; + reader.readAsDataURL(file); + return deferred.promise; + }; + + return { + restrict: 'A', + require: '^form', + scope: { + image: '=imageUpload', + resizeMaxHeight: '@?imageUploadResizeMaxHeight', + resizeMaxWidth: '@?imageUploadResizeMaxWidth', + resizeQuality: '@?imageUploadResizeQuality', + resizeType: '@?imageUploadResizeType', + imageApi: '=?imageUploadApi', + backgroundColor: '@?imageUploadBackgroundColor' + }, + compile: function compile(tElement, tAttrs, transclude) { + return function postLink(scope, iElement, iAttrs, formCtrl) { + var doResizing = function(imageResult, callback) { + createImage(imageResult.url, function(image) { + var dataURL = resizeImage(image, scope); + imageResult.resized = { + dataURL: dataURL, + type: dataURL.match(/:(.+\/.+);/)[1] + }; + callback(imageResult); + }); + }; + + var applyScope = function(imageResult) { + scope.$apply(function() { + //console.log(imageResult); + if(iAttrs.multiple) + scope.image.push(imageResult); + else + scope.image = imageResult; + }); + }; + + iElement.bind('change', function (evt) { + //when multiple always return an array of images + if(iAttrs.multiple) + scope.image = []; + + var files = evt.target.files; + for(var i = 0; i < files.length; i++) { + setInputValidity(files[i]); + + //create a result object for each file in files + var imageResult = { + file: files[i], + url: URL.createObjectURL(files[i]) + }; + + fileToDataURL(files[i]).then(function (dataURL) { + imageResult.dataURL = dataURL; + }); + + if(scope.resizeMaxHeight || scope.resizeMaxWidth) { //resize image + doResizing(imageResult, function(imageResult) { + applyScope(imageResult); + }); + } + else { //no resizing + applyScope(imageResult); + } + } + }); + + //API for otter actions + scope.imageApi = scope.imageApi || {}; + scope.imageApi.clearFile = () => { + iElement[0].value = ""; + setInputValidity(); + }; + + + let setInputValidity = file => { + //if form validation supported + + if(formCtrl && iAttrs.name && formCtrl[iAttrs.name]){ + formCtrl[iAttrs.name].$setDirty(); + if(file && file.type && !imageMimeRgx.test(file.type)){ + //set form invalid + formCtrl[iAttrs.name].$setValidity('mimeType', false); + applyScope(); + return; + } + if(file && file.size && file.size > 1000000){ + //set form invalid + formCtrl[iAttrs.name].$setValidity('imageSize', false); + applyScope(); + return; + } + //set valid + formCtrl[iAttrs.name].$setValidity('mimeType', true); + formCtrl[iAttrs.name].$setValidity('imageSize', true); + } + + } + } + } + } +}); diff --git a/ecomp-portal-FE/client/app/directives/left-menu/left-menu.directive.js b/ecomp-portal-FE/client/app/directives/left-menu/left-menu.directive.js new file mode 100644 index 00000000..363485fe --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/left-menu/left-menu.directive.js @@ -0,0 +1,96 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +(function () { + class LeftMenu { + constructor($rootScope, $log, userbarUpdateService) { + this.templateUrl = 'app/directives/left-menu/left-menu.tpl.html'; + this.restrict = 'AE'; + this.$rootScope = $rootScope; + this.$log = $log; + this.userbarUpdateService = userbarUpdateService; + this.link = this._link.bind(this); + this.scope = { + sidebarModel: '=' + } + } + + _link(scope) { + let init = () => { + scope.isOpen = true; + }; + + init(); + + scope.refreshOnlineUsers = () => { + this.userbarUpdateService.setRefreshCount(this.userbarUpdateService.maxCount); + }; + + scope.toggleSidebar = () => { + scope.isOpen = !scope.isOpen; + if(scope.isOpen) + setContentPos(1); + else + setContentPos(0); + + }; + + scope.isBrowserInternetExplorer = false; + scope.browserName = bowser.name; + + if (bowser.msie || bowser.msedge) { + scope.isBrowserInternetExplorer = true; + } else { + scope.isBrowserInternetExplorer = false; + } + + let log = this.$log; + + this.userbarUpdateService.getWidthThresholdLeftMenu().then(function (res) { + if (res == null || res.response == null) { + log.error('userbarUpdateService: failed to get window width threshold for collapsing left menu; please make sure "window_width_threshold_left_menu" is specified in system.properties file.'); + } else { + var leftMenuCollapseWidthThreshold = parseInt(res.response.windowWidth); + if ($(window).width() { + scope.isOpen = true; + }); + } + } + angular.module('ecompApp').directive('leftMenu', ($rootScope,$log,userbarUpdateService) => new LeftMenu($rootScope,$log,userbarUpdateService)); +})(); + +function setContentPos(open) { + if(open==1){ + $("#contentId" ).css( "padding-left", "210px" ); + }else{ + $("#contentId" ).css( "padding-left", "50px" ); + } + +} diff --git a/ecomp-portal-FE/client/app/directives/left-menu/left-menu.less b/ecomp-portal-FE/client/app/directives/left-menu/left-menu.less new file mode 100644 index 00000000..276fb409 --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/left-menu/left-menu.less @@ -0,0 +1,133 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +@sidebar-width: 200px; +@transition-duration: 0.25s; +@overlayer-opacity: 0.65; + + +.close-button { + .a24r; + font-size: 3em; + line-height: 18px; + position: absolute; + cursor: pointer; + vertical-align: middle; + top: @second-level-top; + left: 0; + -webkit-font-smoothing: antialiased; + height: 53px; + z-index: 101; +} +.ecomp-sidebar-container { + position: absolute; + display: block; + left: 0; + z-index: 100; + transition: left @transition-duration; + margin-top: -15px; + + .ecomp-sidebar-main { + position: absolute; + margin-top: 65px; + width: @sidebar-width; + height: 100vh; + .bg_u; + box-shadow: 0 4px 5px rgba(0, 0, 0, .2); + + padding-right: 10px; + padding-left: 10px; + + .accordion-container{ + margin-top: 45px; + overflow-y:auto; + overflow-x:hidden; + } + .att-accordion-font{ + font-size: .875rem; + color: #666; + display: inline-block; + font-family: arial; + + } + + .att-accordion-active{ + color: #199DDF !important; + } + + .sub-item{ + .att-accordion-font; + cursor: pointer; + height: 37px; + line-height: 37px; + padding-left: 20px; + padding-bottom: 10px; + vertical-align: middle; + width: 100%; + } + .sub-item:hover{ + .att-accordion-active; + } + + .parent-item{ + .att-accordion-font; + border-bottom: 1px solid #bbb; + cursor: pointer; + height: 37px; + line-height: 37px; + padding-bottom: 10px; + vertical-align: middle; + width: 100%; + } + .parent-item:hover{ + .att-accordion-active; + } + + } +} + +.open-sidebar { + left: 0; +} + +.close-sidebar { + left: -@sidebar-width; +} + +.content-overlayed { + position: fixed; + top: 110px; + right: 0; + bottom: 0; + left: 0; + background: none repeat scroll 0 0 rgb(242, 242, 242); + z-index: 9999; +} +.fade-animation{ + opacity: @overlayer-opacity; + transition: opacity @transition-duration ease-in-out; +} +.fade-animation.ng-hide { + opacity:0; + transition: opacity @transition-duration ease-in-out; +} + +#contentId{ + padding-left:210px +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/app/directives/left-menu/left-menu.tpl.html b/ecomp-portal-FE/client/app/directives/left-menu/left-menu.tpl.html new file mode 100644 index 00000000..0059d6a4 --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/left-menu/left-menu.tpl.html @@ -0,0 +1,57 @@ + +
+ +
+
+
+
+ +
+
+ +   {{parent.name}} +
+ + +
+   {{child.name}} +
+
+ +
+
+ +
+
+
diff --git a/ecomp-portal-FE/client/app/directives/multiple-select/multiple-select.directive.js b/ecomp-portal-FE/client/app/directives/multiple-select/multiple-select.directive.js new file mode 100644 index 00000000..e8f6ed94 --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/multiple-select/multiple-select.directive.js @@ -0,0 +1,111 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +angular.module('ecompApp') + .directive('multipleSelect', function ($window) { + return { + restrict: 'E', + templateUrl: 'app/directives/multiple-select/multiple-select.tpl.html', + scope: { + onChange: '&', + nameAttr: '@', + valueAttr: '@', + ngModel: '=', + placeholder: '@', + uniqueData: '@?', + onDropdownClose: '&?' + }, + link: function(scope, elm, attrs){ + scope.isExpanded = false; + + scope.isDisabled = !scope.ngModel || !scope.ngModel.length; + scope.$watch('ngModel', function(newVal){ + scope.isDisabled = !newVal || !newVal.length; + }); + + + let startListening = () => { + console.log('listening on $window!'); + angular.element($window).on('click', function () { + stopListening(); + }); + + angular.element('multiple-select').on('click', function(e) { + if($(e.target).closest('multiple-select')[0].attributes['unique-data'].value === attrs.uniqueData){ + console.log('ignored that..:', attrs.uniqueData); + e.stopPropagation(); + }else{ + console.log('shouldnt ignore, close expanded!:', attrs.uniqueData); + scope.isExpanded = false; + scope.$applyAsync(); + } + }); + }; + + let stopListening = function() { + if(scope.onDropdownClose){ + scope.onDropdownClose(); + } + scope.isExpanded = false; + scope.$applyAsync(); + console.log('stop listening on $window and multiple-element!'); + angular.element($window).off('click'); + angular.element('multiple-select').off('click'); + }; + + scope.showCheckboxes = function(){ + scope.isExpanded = !scope.isExpanded; + if(scope.isExpanded){ + startListening(); + }else{ + stopListening(); + if(scope.onDropdownClose){ + scope.onDropdownClose(); + } + } + }; + + scope.onCheckboxClicked = function() { + console.log('checkbox clicked; unique data: ',attrs.uniqueData); + if(scope.onChange) { + scope.onChange(); + } + } + + scope.getTitle = function(){ + var disp = ""; + if(!scope.ngModel || !scope.ngModel.length) { + return disp; + } + scope.ngModel.forEach(function(item){ + if(item[scope.valueAttr]){ + disp+=item[scope.nameAttr] + ","; + } + }); + if(disp!==""){ + disp = disp.slice(0,disp.length-1); + }else{ + disp = scope.placeholder; + } + return disp; + }; + } + }; + }); diff --git a/ecomp-portal-FE/client/app/directives/multiple-select/multiple-select.less b/ecomp-portal-FE/client/app/directives/multiple-select/multiple-select.less new file mode 100644 index 00000000..6e54c8b6 --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/multiple-select/multiple-select.less @@ -0,0 +1,81 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .multiple-select{ + position: relative; + width: 100%; + + .selectBox{ + cursor: pointer; + position: relative; + border: 1px solid @o; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + line-height: 30px; + height: 30px; + padding-left: 10px; + padding-right: 10px; + + &.open::after{ + content: ''; + .arrow_up; + display: block; + position: absolute; + top: 12px; + right: 4px; + } + &.closed::after{ + content: ''; + .arrow_down; + display: block; + position: absolute; + top: 12px; + right: 4px; + } + &.disabled{ + cursor: default; + background: #CCCCCC; + } + + } + + .checkboxes{ + z-index: 99999; + padding-left: 8px; + padding-right: 8px; + position: absolute; + top: 30px; + width: 100%; + background: white; + + display: block; + border: 1px #dadada solid; + + label{ + cursor: pointer; + display: block; + } + input{ + cursor: pointer; + } + + + } +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/app/directives/multiple-select/multiple-select.tpl.html b/ecomp-portal-FE/client/app/directives/multiple-select/multiple-select.tpl.html new file mode 100644 index 00000000..e55e844c --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/multiple-select/multiple-select.tpl.html @@ -0,0 +1,38 @@ + + diff --git a/ecomp-portal-FE/client/app/directives/right-click-menu/right-click-menu.directive.js b/ecomp-portal-FE/client/app/directives/right-click-menu/right-click-menu.directive.js new file mode 100644 index 00000000..358cc56f --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/right-click-menu/right-click-menu.directive.js @@ -0,0 +1,38 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +angular.module('ecompApp').directive( "contextMenu", function($compile){ + contextMenu = {}; + contextMenu.restrict = "AE"; + contextMenu.link = function( lScope, lElem, lAttr ){ + lElem.on("contextmenu", function (e) { + e.preventDefault(); + lElem.append( $compile( lScope[ lAttr.contextMenu ])(lScope) ); + $("#contextmenu-node").css("left", e.clientX); + $("#contextmenu-node").css("top", e.clientY); + }); + lElem.on("mouseleave", function(e){ + console.log("Leaved the div"); + if($("#contextmenu-node") ) + $("#contextmenu-node").remove(); + }); + }; + return contextMenu; +}); diff --git a/ecomp-portal-FE/client/app/directives/right-click/ng-right-click-directive.js b/ecomp-portal-FE/client/app/directives/right-click/ng-right-click-directive.js new file mode 100644 index 00000000..075ea3ac --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/right-click/ng-right-click-directive.js @@ -0,0 +1,32 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +angular.module('ecompApp'). + directive('ngRightClick', function($parse) { + return function(scope, element, attrs) + { + var fn = $parse(attrs.ngRightClick); + element.bind('contextmenu', function(event) { + scope.$apply(function() { + event.preventDefault(); + fn(scope, {$event:event}); + }); + }); + }; + }); diff --git a/ecomp-portal-FE/client/app/directives/right-menu/right-menu.directive.js b/ecomp-portal-FE/client/app/directives/right-menu/right-menu.directive.js new file mode 100644 index 00000000..b778ce7b --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/right-menu/right-menu.directive.js @@ -0,0 +1,140 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +/** + * Created by nnaffar on 1/28/16. + */ +(function () { + class RightMenu { + constructor($rootScope,$window,$log,userbarUpdateService) { + this.templateUrl = 'app/directives/right-menu/right-menu.tpl.html'; + this.restrict = 'AE'; + this.$rootScope = $rootScope; + this.userbarUpdateService = userbarUpdateService; + this.$window = $window; + this.$log = $log; + this.link = this._link.bind(this); + this.scope = { + userList :'=' + } + } + + + + _link(scope) { + let init = () => { + scope.isOpen = true; + scope.rightSideToggleBtn = 'Collapse'; + + scope.openInNewTab = (url) => { + if(url == "self") { + alert("Cannot chat with self!"); + } else { + var win = window.open(url, '_blank'); + setCookie(url.split("chat_id=")[1], 'source', 1); + //window.localStorage.setItem(url.split("chat_id=")[1],'source'); + win.focus(); + } + }; + + }; + + function setCookie(cname,cvalue,exdays) { + var d = new Date(); + d.setTime(d.getTime() + (exdays*24*60*60*1000)); + var expires = "expires=" + d.toGMTString(); + document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; + } + + function getCookie(cname) { + var name = cname + "="; + var decodedCookie = decodeURIComponent(document.cookie); + var ca = decodedCookie.split(';'); + for(var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) == ' ') { + c = c.substring(1); + } + if (c.indexOf(name) == 0) { + return c.substring(name.length, c.length); + } + } + return ""; + } + + + + init(); + + /***Getting the list of the users***/ + scope.toggleSidebar = () => { + scope.isOpen = !scope.isOpen; + if(scope.isOpen){ + scope.rightSideToggleBtn = 'Collapse'; + }else{ + scope.rightSideToggleBtn = 'Expand'; + } + }; + + scope.isBrowserInternetExplorer = false; + scope.browserName = bowser.name; + + if (bowser.msie || bowser.msedge) { + scope.isBrowserInternetExplorer = true; + } else { + scope.isBrowserInternetExplorer = false; + } + + + + + scope.calculateUserBarHeight = () => { + var footerOff = $('#online-userbar').offset().top; + var headOff = $('#footer').offset().top; + var userbarHeight= parseInt($(".online-user-container").css('height'),10); + var defaultOffSet = 45; + // console.log(headOff - footerOff-defaultOffSet); + $(".online-user-container").css({ + "height" : headOff - footerOff-defaultOffSet + }); + }; + + let log = this.$log; + + this.userbarUpdateService.getWidthThresholdRightMenu().then(function (res) { + if (res.status=="ERROR") { + log.error('userbarUpdateService: failed to get window width threshold for collapsing right menu; please make sure "window_width_threshold_right_menu" is specified in system.properties file.'); + } else { + var rightMenuCollapseWidthThreshold = parseInt(res.response.windowWidth); + if ($(window).width() new RightMenu($rootScope,$window,$log,userbarUpdateService)); +})(); diff --git a/ecomp-portal-FE/client/app/directives/right-menu/right-menu.less b/ecomp-portal-FE/client/app/directives/right-menu/right-menu.less new file mode 100644 index 00000000..bc23b614 --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/right-menu/right-menu.less @@ -0,0 +1,178 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + /** + * Created by nnaffar on 1/28/16. + */ +@sidebar-width: 200px; +@transition-duration: 0.25s; +@overlayer-opacity: 0.65; + + +.close-button { + //width: @sidebar-width; + .a24r; + //.bg_u; + font-size: 3em; + line-height: 18px; + position: absolute; + cursor: pointer; + vertical-align: middle; + top: @second-level-top; + left: 0; + -webkit-font-smoothing: antialiased; + height: 53px; + z-index: 101; + //box-shadow: 0 4px 5px rgba(0, 0, 0, .2); + +} +.ecomp-right-sidebar-container{ + position: absolute; + display: block; + left: 0; + z-index: 100; + transition: left @transition-duration; + margin-top: -15px; + + .ecomp-sidebar-main { + //background-color: ; + position: absolute; + margin-top: 65px; + width: @sidebar-width; + height: 100vh; + .bg_u;//white for 1610 + //.bg_w; // gray for 1702 + box-shadow: 0 4px 5px rgba(0, 0, 0, .2); + + padding-right: 10px; + padding-left: 10px; + + .accordion-container{ + margin-top: 45px; + } + .att-accordion-font{ + font-size: .875rem; + color: #666; + display: inline-block; + font-family: arial; + + } + + .att-accordion-active{ + color: #199DDF !important; + } + + .sub-item{ + .att-accordion-font; + cursor: pointer; + height: 37px; + line-height: 37px; + padding-left: 20px; + padding-bottom: 10px; + vertical-align: middle; + width: 100%; + } + .sub-item:hover{ + .att-accordion-active; + } + + .parent-item{ + .att-accordion-font; + border-bottom: 1px solid #bbb; + cursor: pointer; + height: 37px; + line-height: 37px; + padding-bottom: 10px; + vertical-align: middle; + width: 100%; + } + .parent-item:hover{ + .att-accordion-active; + } + + } +} + +.open-sidebar { + right: 0; + transition: right .25s ease-in-out; + -moz-transition: right .25s ease-in-out; + -webkit-transition: right .25s ease-in-out; +} + +.close-sidebar { + right: -@sidebar-width; + transition: right .25s ease-in-out; + -moz-transition: right .25s ease-in-out; + -webkit-transition: right .25s ease-in-out; +} + +.content-overlayed { + position: fixed; + top: 110px; + right: 0; + bottom: 0; + left: 0; + background: none repeat scroll 0 0 rgb(242, 242, 242); + z-index: 9999; +} +.fade-animation{ + opacity: @overlayer-opacity; + transition: opacity @transition-duration ease-in-out; +} +.fade-animation.ng-hide { + opacity:0; + transition: opacity @transition-duration ease-in-out; +} + +.activeUserIcon { + transition: all .2s ease-in-out; + display: block; + margin-left: auto; margin-right: auto; height:55px; width:55px; border-radius: 50%; + +} +.activeUserIcon:hover { transform: scale(1.5); } + +.ecomp-right-sidebar-toggle{ + position: absolute; + top: 400px; + right: 35px; +} + +.open-sidebar-toggle { + // right: 0; + transition: right .25s ease-in-out; + -moz-transition: right .25s ease-in-out; + -webkit-transition: right .25s ease-in-out; +} + +.close-sidebar-toggle { + right: -36px; + transition: right .25s ease-in-out; + -moz-transition: right .25s ease-in-out; + -webkit-transition: right .25s ease-in-out; +} + +.ecomp-right-sidebar-title{ + font-family: arial; + font-size: 14px; + color: #ef6f00; + margin-bottom:20px; + text-align: center; +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/app/directives/right-menu/right-menu.tpl.html b/ecomp-portal-FE/client/app/directives/right-menu/right-menu.tpl.html new file mode 100644 index 00000000..39eb7087 --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/right-menu/right-menu.tpl.html @@ -0,0 +1,40 @@ + +
+
+
+
Online Users
+
+
+
+ User Link +
{{user.userId}}
+
+
+
+
+
+
+ diff --git a/ecomp-portal-FE/client/app/directives/scroll-top/scroll-top.directive.js b/ecomp-portal-FE/client/app/directives/scroll-top/scroll-top.directive.js new file mode 100644 index 00000000..148660b5 --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/scroll-top/scroll-top.directive.js @@ -0,0 +1,35 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +angular.module('ecompApp') + .directive('scrollTop', function () { + return { + restrict: 'A', + scope: {api: '=scrollTop'}, + link: function (scope, element) { + scope.api = scope.api || {}; + scope.api.scrollTop = function() { + element.animate({ + scrollTop : 0 + }, 500); + }; + } + }; + }); diff --git a/ecomp-portal-FE/client/app/directives/search-users/search-users.controller.js b/ecomp-portal-FE/client/app/directives/search-users/search-users.controller.js new file mode 100644 index 00000000..649b3ab2 --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/search-users/search-users.controller.js @@ -0,0 +1,161 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; +(function () { + class SearchUsersCtrl { + + constructor($log, usersService,adminsService, $scope,confirmBoxService) { + + $scope.UserSearchsIsNull=false; + $scope.userExist = false; + this.scrollApi = {};//scrollTop directive + + this.showAddUser = false; + this.showSearch = true; + this.newUser ={ + firstName:'', + lastName:'', + emailAddress:'', + + middleName:'', + loginId:'', + loginPwd:'', + loginPwdCheck:'' + }; + + let activeRequests = []; + let clearReq = (req) => { + activeRequests.splice(activeRequests.indexOf(req), 1); + }; + + this.showAddUserSection = () => { + this.showAddUser = true; + this.showSearch = false; + } + + this.addNewUserFun = () => { + if (this.newUser.loginId =='' || this.newUser.loginPwd == '' || this.newUser.firstName == '' || this.newUser.lastName =='' || this.newUser.emailAddress ==''||this.newUser.loginPwd ==''){ + var warningMsg = "Please enter a value for all fields marked with *."; + confirmBoxService.showInformation(warningMsg).then(isConfirmed => {return;}); + return; + } else if (this.newUser.loginPwd != this.newUser.loginPwdCheck) { + var warningMsg = "Passwords do not match, please try again."; + confirmBoxService.showInformation(warningMsg).then(isConfirmed => {return;}); + return; + } + else { + // check password length complexity. + var warningMsg = adminsService.isComplexPassword(this.newUser.loginPwd); + if (warningMsg != null) { + confirmBoxService.showInformation(warningMsg).then(isConfirmed => {return;}); + return; + } + } // password + + adminsService.addNewUser(this.newUser,'Yes').then(res=> { + + if(res.message == 'Record already exist'){ + + this.showAddUser = true; + this.showSearch = false; + $scope.userExist = true; + + }else{ + + $scope.userExist = false; + this.selectedUser = this.newUser; + this.selectedUser.orgUserId = this.newUser.loginId; + this.searchUsersResults = []; + this.searchUsersResults.push(this.newUser); + this.showAddUser = false; + this.showSearch = true; + this.newUser ={ + firstName:'', + lastName:'', + emailAdress:'', + middleName:'', + loginId:'', + loginPwd:'', + loginPwdCheck:'' + }; + this.searchUserString =''; + $scope.UserSearchsIsNull = false; + } + + + }).catch(err=> { + $log.error('adminsService: addNewUser error:: ', err); + // $scope.errMsg=err; + confirmBoxService.showInformation('Add New User failed: ' + err); + + }).finally(() => { + //this.isLoadingTable = false; + + }); + } + + this.searchUsers = () => { + this.isLoading = true; + if(this.searchUsersInProgress){ + return; + } + this.selectedUser = null; + this.searchUsersInProgress = true; + this.searchUsersResults = null; + + let searchUsersReq = usersService.searchUsers(this.searchUserString); + activeRequests.push(searchUsersReq); + searchUsersReq.promise().then(usersList => { + $log.debug('searchUsers found the following users: ', JSON.stringify(usersList)); + this.searchUsersResults = usersList; + $scope.UserSearchsIsNull=false; + }).catch(err => { + $log.error('SearchUsersCtrl.searchUsers: ' + err); + $scope.UserSearchsIsNull=true; + }).finally(() => { + this.scrollApi.scrollTop(); + this.searchUsersInProgress = false; + clearReq(searchUsersReq); + this.isLoading = false; + }); + }; + + let init = () => { + this.isLoading = false; + this.searchUsersInProgress = false; + }; + + this.setSelectedUser = user => { + this.selectedUser = user; + }; + + init(); + + $scope.$on('$destroy', () => { + activeRequests.forEach(req => { + req.cancel(); + }); + }); + } + } + SearchUsersCtrl.$inject = ['$log', 'usersService','adminsService', '$scope','confirmBoxService']; + angular.module('ecompApp').controller('SearchUsersCtrl', SearchUsersCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/directives/search-users/search-users.controller.spec.js b/ecomp-portal-FE/client/app/directives/search-users/search-users.controller.spec.js new file mode 100644 index 00000000..96f5b0df --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/search-users/search-users.controller.spec.js @@ -0,0 +1,176 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +//'use strict'; +// +//describe('Controller: NewAdminCtrl ', () => { +// beforeEach(module('ecompApp')); +// +// //destroy $http default cache before starting to prevent the error 'default cache already exists' +// beforeEach(inject((_CacheFactory_)=> { +// _CacheFactory_.destroyAll(); +// })); +// +// +// let newCtrl, $controller, $q, $rootScope, $log; +// +// beforeEach(inject((_$controller_, _$q_, _$rootScope_, _$log_)=> { +// [$controller, $q, $rootScope, $log] = [_$controller_, _$q_, _$rootScope_, _$log_]; +// })); +// +// let deferredUsersList, deferredAdminAppsRoles, deferredUpdateRolesRes; +// let usersServiceMock, adminsServiceMock; +// beforeEach(()=> { +// [deferredUsersList, deferredAdminAppsRoles, deferredUpdateRolesRes] = [$q.defer(), $q.defer(), $q.defer()]; +// +// //usersServiceMock = jasmine.createSpyObj('usersServiceMock', ['searchUsers']); +// usersServiceMock = { +// searchUsers: () => { +// var promise = () => {return deferredUsersList.promise}; +// var cancel = jasmine.createSpy(); +// return { +// promise: promise, +// cancel: cancel +// } +// } +// }; +// +// adminsServiceMock = jasmine.createSpyObj('adminsServiceMock', ['getAdminAppsRoles', 'updateAdminAppsRoles']); +// +// //usersServiceMock.searchUsers.and.returnValue(deferredUsersList.promise); +// adminsServiceMock.getAdminAppsRoles.and.returnValue(deferredAdminAppsRoles.promise); +// adminsServiceMock.updateAdminAppsRoles.and.returnValue(deferredUpdateRolesRes.promise); +// +// newCtrl = $controller('NewAdminModalCtrl', { +// $log: $log, +// usersService: usersServiceMock, +// adminsService: adminsServiceMock, +// $scope: $rootScope +// }); +// }); +// +// it('should init default values when loading the controller', ()=> { +// //expect(newCtrl.searchUsersInProgress).toBe(false); +// expect(newCtrl.dialogState).toBe(1); +// expect(newCtrl.selectedUser).toBe(null); +// }); +// +// it('should populate retrieved users when search users service returns a list ', ()=> { +// //spyOn(usersServiceMock, 'searchUsers'); +// let usersListRes = [{user: 1}, {user: 2}]; +// newCtrl.searchUserString = 'some user name'; +// deferredUsersList.resolve(usersListRes); +// newCtrl.searchUsers(); +// $rootScope.$apply(); +// +// //expect(usersServiceMock.searchUsers).toHaveBeenCalledWith(newCtrl.searchUserString); +// expect(newCtrl.searchUsersResults).toEqual(usersListRes); +// expect(newCtrl.searchUsersInProgress).toBe(false); +// }); +// +// it('should log the error when search users fails', ()=> { +// spyOn($log, 'error'); +// deferredUsersList.reject('oh snap!'); +// newCtrl.searchUsers(); +// $rootScope.$apply(); +// expect($log.error).toHaveBeenCalled(); +// }); +// +// it('should populate admin apps roles and move to the next screen when adminsService.getAdminAppsRoles succeeded', ()=> { +// let userApps = {appsRoles: [{id: 1, isAdmin: false}, {id: 2, isAdmin: true}]}; +// deferredAdminAppsRoles.resolve(userApps); +// +// newCtrl.searchUsersInProgress = false; +// newCtrl.selectedUser = {userId: 'userId'}; +// +// newCtrl.getAdminAppsRoles(); +// $rootScope.$apply(); +// +// expect(adminsServiceMock.getAdminAppsRoles).toHaveBeenCalledWith(newCtrl.selectedUser.userId); +// expect(newCtrl.adminAppsRoles).toEqual(userApps.appsRoles); +// expect(newCtrl.dialogState).toBe(2); +// }); +// +// it('should log the error when adminsService.getAdminAppsRoles fails', ()=> { +// spyOn($log, 'error'); +// deferredAdminAppsRoles.reject('some error'); +// +// newCtrl.searchUsersInProgress = false; +// newCtrl.selectedUser = {userId: 'userId'}; +// +// newCtrl.getAdminAppsRoles(); +// $rootScope.$apply(); +// +// expect($log.error).toHaveBeenCalled(); +// }); +// it('should log the error when trying to getAdminAppsRoles without selecting user ', ()=> { +// spyOn($log, 'error'); +// +// newCtrl.searchUsersInProgress = false; +// newCtrl.selectedUser = null; +// +// newCtrl.getAdminAppsRoles(); +// $rootScope.$apply(); +// +// expect($log.error).toHaveBeenCalled(); +// }); +// //it('should setSelectedUser when choosing user', ()=> { +// // +// //}); +// //it('should set isAdmin as false when removing app from the administrated apps list', ()=> { +// //}); +// it('should set isAdmin as true when adding app via the dropdown menu', ()=> { +// newCtrl.adminAppsRoles = [{id: 1, isAdmin: false},{id: 2, isAdmin: true}]; +// //simulate UI change +// $rootScope.$apply('newAdmin.selectedNewApp = null'); +// $rootScope.$apply('newAdmin.selectedNewApp = {id: 1, isAdmin: true}'); +// +// expect(newCtrl.adminAppsRoles[0].isAdmin).toBe(true); +// expect(newCtrl.selectedNewApp).toBe(null); +// }); +// +// it('should close the modal when updating apps roles succeeded', ()=> { +// $rootScope.closeThisDialog = () => {}; +// spyOn($rootScope,'closeThisDialog'); +// +// newCtrl.selectedUser = {userId: 'userId'}; +// newCtrl.adminAppsRoles = [{id: 1}]; +// +// deferredUpdateRolesRes.resolve(); +// newCtrl.updateAdminAppsRoles(); +// $rootScope.$apply(); +// +// expect(adminsServiceMock.updateAdminAppsRoles).toHaveBeenCalledWith({userId: newCtrl.selectedUser.userId, appsRoles: newCtrl.adminAppsRoles}); +// expect($rootScope.closeThisDialog).toHaveBeenCalled(); +// }); +// it('should log the error when updating apps roles fails', ()=> { +// newCtrl.selectedUser = {userId: 'userId'}; +// newCtrl.adminAppsRoles = [{id: 1}]; +// +// spyOn($log,'error'); +// deferredUpdateRolesRes.reject(); +// newCtrl.updateAdminAppsRoles(); +// $rootScope.$apply(); +// expect($log.error).toHaveBeenCalled(); +// }); +// //it('should display the add admin dropdown when clicking the add button', ()=> { +// //}); +// +//}); diff --git a/ecomp-portal-FE/client/app/directives/search-users/search-users.directive.js b/ecomp-portal-FE/client/app/directives/search-users/search-users.directive.js new file mode 100644 index 00000000..4e5809f9 --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/search-users/search-users.directive.js @@ -0,0 +1,34 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +angular.module('ecompApp') + .directive('searchUsers', function () { + return { + restrict: 'E', + templateUrl: 'app/directives/search-users/search-users.tpl.html', + controller: 'SearchUsersCtrl', + controllerAs: 'searchUsers', + bindToController: true, + scope: { + selectedUser: '=', + searchTitle: '@' + } + }; + }); diff --git a/ecomp-portal-FE/client/app/directives/search-users/search-users.less b/ecomp-portal-FE/client/app/directives/search-users/search-users.less new file mode 100644 index 00000000..9c6de2b4 --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/search-users/search-users.less @@ -0,0 +1,154 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .search-users-directive{ + .title { + .n18r; + border-bottom: @a 3px solid; + + } + + .add-user-section{ + margin-top:0px; + margin-left:10px; + } + + .input-text-new-user { + font-style: italic; + padding: 7px 10px; + width: 200px !important; + display: inline-block; + position: relative; + margin-bottom: 10px; + border-radius: 6px; + border: 1px solid #d8d8d8; + height: 32px; + border-color: slategrey !important; + } + + .input-new-user-div{ + + margin-left:10px; + } + + .add-user-button { + cursor: pointer; + float:right; + width: 120px; + + margin-left : 5px; + + .btn-blue; + + line-height: 32px; + height: 29px; + text-align: center; + vertical-align: middle; + line-height: 28px; + + &::before { + .ico_add_user; + content: '\f211'; + font-family: "Ionicons"; + vertical-align: middle; + display: inline-block; + margin-right: 8px; + line-height: 16px; + } + } + + .main { + margin: 16px; + .search-instructions { + .n14r; + margin-bottom: 8px; + } + .search { + .input-field { + display: inline-block; + width: 250px; + height: 30px; + } + .search-button { + .btn-blue; + width: 90px; + display: inline-block; + float: right; + } + } + + .search-results { + margin-top: 10px; + .results-title { + .n14r; + margin-bottom: 8px; + } + + .results-container { + height: 208px; + overflow-y: auto; + .user { + padding-left: 10px; + padding-top: 6px; + height: 48px; + + border-top: 1px solid @p; + border-right: 1px solid @p; + border-left: 1px solid @p; + + &:last-child { + border-bottom: 1px solid @p; + } + + cursor: pointer; + &:hover { + background-color: @v; + } + + &.selected { + background-color: @v; + } + + .main-name { + .n14r; + + } + .sub-job-title { + .p13r; + + } + } + + } + + } + .error-text { + margin-top: 25px; + font-weight: 400; + font-size: 16px; + text-align: left; + color: @err; + } + + .no-user-found { + margin-top: 16px; + .n14r; + } + } +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/app/directives/search-users/search-users.tpl.html b/ecomp-portal-FE/client/app/directives/search-users/search-users.tpl.html new file mode 100644 index 00000000..6c275693 --- /dev/null +++ b/ecomp-portal-FE/client/app/directives/search-users/search-users.tpl.html @@ -0,0 +1,144 @@ + +
+
+
+
+
+
Enter first name, last name or User ID
+ + + +
+
+
+
+
+
+ + +
+
+
+
+
+
+
+ +
+ No match found. +
+ +
+ No users found +
+
+ + +
+
+
+
*First Name
+ +
+
+
Middle Name
+ +
+
+
*Last Name
+ +
+
+
*Email Address ID
+ +
+
+
*Login ID
+ +
+
+
*Login Password
+ +
+
+
*Confirm Login Password
+ +
+
+ The passwords do not match. Try again. + +
+
+ User with same loginId already exists. Try again. + +
+
+ +
+ + +
+ +
+ + + diff --git a/ecomp-portal-FE/client/app/filters/elipsis/elipsis.filter.js b/ecomp-portal-FE/client/app/filters/elipsis/elipsis.filter.js new file mode 100644 index 00000000..310649b3 --- /dev/null +++ b/ecomp-portal-FE/client/app/filters/elipsis/elipsis.filter.js @@ -0,0 +1,38 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +angular.module('ecompApp') + .filter('elipsis', () => { + return (val, limit) => { + if (!val) { + return val; + } + limit = parseInt(limit); + + if (!limit || val.length <= limit) { + return val; + } + + val = val.substr(0, limit); + + return val + '...'; + } + }); diff --git a/ecomp-portal-FE/client/app/filters/elipsis/elipsis.filter.spec.js b/ecomp-portal-FE/client/app/filters/elipsis/elipsis.filter.spec.js new file mode 100644 index 00000000..a9dc7836 --- /dev/null +++ b/ecomp-portal-FE/client/app/filters/elipsis/elipsis.filter.spec.js @@ -0,0 +1,58 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +describe('Filter: elipsis', function () { + 'use strict'; + let filter; + + beforeEach(function () { + module('ecompApp'); + }); + + beforeEach(inject((_CacheFactory_)=> { + _CacheFactory_.destroyAll(); + + })); + + it('should trim the text and return it with ... when the text length is greater than the limit"', inject(function (_$filter_) { + filter = _$filter_; + // given + let text = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore + et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex + ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat + nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim + id est laborum`; + let limit = 50; + + var result = filter('elipsis')(text, limit); + + expect(result).toBe(text.substr(0, 50) + '...'); + })); + + it("should return the exact string where there's no limit", inject(function (_$filter_) { + filter = _$filter_; + + let text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore'; + + let result = filter('elipsis')(text); + + expect(result).toEqual(text); + })); + +}); diff --git a/ecomp-portal-FE/client/app/filters/trusted-url/trusted-url.filter.js b/ecomp-portal-FE/client/app/filters/trusted-url/trusted-url.filter.js new file mode 100644 index 00000000..a6e70d69 --- /dev/null +++ b/ecomp-portal-FE/client/app/filters/trusted-url/trusted-url.filter.js @@ -0,0 +1,26 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +angular.module('ecompApp') + .filter('trusted', function($sce){ + return function(url) { + return $sce.trustAsResourceUrl(url); + }; + }); diff --git a/ecomp-portal-FE/client/app/router.js b/ecomp-portal-FE/client/app/router.js new file mode 100644 index 00000000..8af5f171 --- /dev/null +++ b/ecomp-portal-FE/client/app/router.js @@ -0,0 +1,528 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +angular.module('ecompApp') + .config($stateProvider => { + $stateProvider + .state('root', { + abstract: true, + views: { + 'header@': { + templateUrl: 'app/views/header/header.tpl.html', + controller: 'HeaderCtrl', + controllerAs: 'header' + }, + 'tabbar@': { + templateUrl: 'app/views/tabs/tabs.tpl.html', + controller: 'TabsCtrl', + controllerAs: 'tabsHome' + }, + 'sidebar@':{ + templateUrl: 'app/views/sidebar/sidebar.tpl.html', + controller: 'SidebarCtrl', + controllerAs: 'sidebar' + }, + 'userbar@':{ + templateUrl: 'app/views/userbar/userbar.tpl.html', + controller: 'UserbarCtrl', + controllerAs: 'userbar' + }, + 'footer@': { + templateUrl: 'app/views/footer/footer.tpl.html', + controller: 'FooterCtrl', + controllerAs: 'footer' } + } +// }).state('root.applicationsHome', { +// url: '/applicationsHome', +// views: { +// 'content@': { +// templateUrl: 'app/views/home/applications-home/applications-home.tpl.html', +// controller: 'ApplicationsHomeCtrl', +// controllerAs: 'applicationsHome' +// } +// } + }).state('root.applicationsHome', { + url: '/applicationsHome', + views: { + 'content@': { + templateUrl: 'app/views/dashboard/dashboard.tpl.html', + controller: 'DashboardCtrl', + controllerAs: 'dashboard' + } + } + }).state('root.appCatalog', { + url: '/appCatalog', + views: { + 'content@': { + templateUrl: 'app/views/catalog/catalog.tpl.html', + controller: 'CatalogCtrl', + controllerAs: 'catalog' + } + } + }).state('root.widgetsHome', { + url: '/widgetsHome', + views: { + 'content@': { + templateUrl: 'app/views/home/widgets-home/widgets-home.tpl.html', + controller: 'WidgetsHomeCtrl', + controllerAs: 'widgetsHome' + } + } + }).state('root.admins', { + url: '/admins', + views: { + 'content@': { + templateUrl: 'app/views/admins/admins.tpl.html', + controller: 'AdminsCtrl', + controllerAs: 'admins' + } + } + }).state('root.users', { + url: '/users', + views: { + 'content@': { + templateUrl: 'app/views/users/users.tpl.html', + controller: 'UsersCtrl', + controllerAs: 'users' + } + } + }).state('root.applications', { + url: '/applications', + views: { + 'content@': { + templateUrl: 'app/views/applications/applications.tpl.html', + controller: 'ApplicationsCtrl', + controllerAs: 'apps' + } + } + }).state('root.functionalMenu', { + url: '/functionalMenu', + views: { + 'content@': { + templateUrl: 'app/views/functionalMenu/functionalMenu.tpl.html', + controller: 'FunctionalMenuCtrl', + controllerAs: 'functionalMenu' + } + } + }).state('root.getAccess', { + url: '/getAccess', + params: { + appName: null, + }, + views: { + 'content@': { + templateUrl: 'app/views/support/get-access/get-access.tpl.html', + controller: 'GetAccessCtrl', + controllerAs: 'access' + } + } + }).state('root.contactUs', { + url: '/contactUs', + views: { + 'content@': { + templateUrl: 'app/views/support/contact-us/contact-us.tpl.html', + controller: 'ContactUsCtrl', + controllerAs: 'contact' + } + } + }).state('root.widgets', { + url: '/widgets', + views: { + 'content@': { + templateUrl: 'app/views/widgets/widgets.tpl.html', + controller: 'WidgetsCtrl', + controllerAs: 'widgets' + } + } + }).state('root.portalAdmins', { + url: '/portalAdmins', + views: { + 'content@': { + templateUrl: 'app/views/portal-admin/portal-admin.tpl.html', + controller: 'PortalAdminsCtrl', + controllerAs: 'portalAdmin' + } + } + }).state('root.error404', { + url: '/error404', + views: { + 'content@': { + templateUrl: 'app/views/errors/error.404.tpl.html', + controller: 'Error404Ctrl', + controllerAs: 'error404' + } + } + }).state('noUserError', { + url: '/noUserError', + views: { + 'error@': { + templateUrl: 'app/views/errors/error.tpl.html', + controller: 'ErrorCtrl', + controllerAs: 'error' + } + } + }).state('unKnownError', { + url: '/unKnownError', + views: { + 'error@': { + templateUrl: 'app/views/errors/error.tpl.html', + controller: 'ErrorCtrl', + controllerAs: 'error' + } + } + }).state('root.kpidash', { + url: '/kpidash', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/DCAE/DCAE_KPI.html', + controller: 'DCAE_Ctrl_KPI' + } + } + }).state('root.kpidash_DCAE', { + //url: '/kpidash_DCAE', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/DCAE/DCAE_KPI.html', + controller: 'DCAE_Ctrl_KPI' + } + } + }).state('root.kpidash_DCAE_KPI', { + //url: '/kpidash_DCAE_KPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/DCAE/DCAE_KPI.html', + controller: 'DCAE_Ctrl_KPI' + } + } + }).state('root.kpidash_DCAE_UserDefinedKPI', { + //url: '/kpidash_DCAE_UserDefinedKPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/DCAE/DCAE_UserDefinedKPI.html', + controller: 'DCAE_Ctrl_UserDefinedKPI' + } + } + }).state('root.kpidash_DCAE_Metrics', { + //url: '/kpidash_DCAE_Metrics', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/DCAE/DCAE_Metrics.html', + controller: 'DCAE_Ctrl_Metrics' + } + } + }).state('root.kpidash_ECOMP', { + //url: '/kpidash_ECOMP', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/ECOMP.html', + controller: 'ECOMP_Ctrl' + } + } + }).state('root.kpidash_AAI', { + //url: '/kpidash_AAI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/AAI/AAI_KPI.html', + controller: 'AAI_Ctrl_KPI' + } + } + }).state('root.kpidash_AAI_KPI', { + //url: '/kpidash_AAI_KPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/AAI/AAI_KPI.html', + controller: 'AAI_Ctrl_KPI' + } + } + }).state('root.kpidash_AAI_UserDefinedKPI', { + //url: '/kpidash_AAI_UserDefinedKPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/AAI/AAI_UserDefinedKPI.html', + controller: 'AAI_Ctrl_UserDefinedKPI' + } + } + }).state('root.kpidash_AAI_Metrics', { + //url: '/kpidash_AAI_Metrics', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/AAI/AAI_Metrics.html', + controller: 'AAI_Ctrl_Metrics' + } + } + }).state('root.kpidash_APPC', { + //url: '/kpidash_APPC', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/APPC/APPC_KPI.html', + controller: 'APPC_Ctrl_KPI' + } + } + }).state('root.kpidash_APPC_KPI', { + //url: '/kpidash_APPC_KPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/APPC/APPC_KPI.html', + controller: 'APPC_Ctrl_KPI' + } + } + }).state('root.kpidash_APPC_UserDefinedKPI', { + //url: '/kpidash_APPC_UserDefinedKPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/APPC/APPC_UserDefinedKPI.html', + controller: 'APPC_Ctrl_UserDefinedKPI' + } + } + }).state('root.kpidash_APPC_Metrics', { + //url: '/kpidash_APPC_Metrics', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/APPC/APPC_Metrics.html', + controller: 'APPC_Ctrl_Metrics' + } + } + }).state('root.kpidash_ASDC', { + //url: '/kpidash_ASDC', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/ASDC/ASDC_KPI.html', + controller: 'ASDC_Ctrl_KPI' + } + } + }).state('root.kpidash_ASDC_KPI', { + //url: '/kpidash_ASDC_KPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/ASDC/ASDC_KPI.html', + controller: 'ASDC_Ctrl_KPI' + } + } + }).state('root.kpidash_ASDC_UserDefinedKPI', { + //url: '/kpidash_ASDC_UserDefinedKPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/ASDC/ASDC_UserDefinedKPI.html', + controller: 'ASDC_Ctrl_UserDefinedKPI' + } + } + }).state('root.kpidash_ASDC_Metrics', { + //url: '/kpidash_ASDC_Metrics', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/ASDC/ASDC_Metrics.html', + controller: 'ASDC_Ctrl_Metrics' + } + } + }).state('root.kpidash_Closedloop', { + //url: '/kpidash_Closedloop', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/Closedloop/Closedloop_KPI.html', + controller: 'Closedloop_Ctrl_KPI' + } + } + }).state('root.kpidash_Closedloop_KPI', { + //url: '/kpidash_Closedloop_KPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/Closedloop/Closedloop_KPI.html', + controller: 'Closedloop_Ctrl_KPI' + } + } + }).state('root.kpidash_Closedloop_UserDefinedKPI', { + //url: '/kpidash_Closedloop_UserDefinedKPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/Closedloop/Closedloop_UserDefinedKPI.html', + controller: 'Closedloop_Ctrl_UserDefinedKPI' + } + } + }).state('root.kpidash_Closedloop_Metrics', { + //url: '/kpidash_Closedloop_Metrics', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/Closedloop/Closedloop_Metrics.html', + controller: 'Closedloop_Ctrl_Metrics' + } + } + }).state('root.kpidash_DMaaP', { + //url: '/kpidash_DMaaP', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/DMaaP/DMaaP_KPI.html', + controller: 'DMaaP_Ctrl_KPI' + } + } + }).state('root.kpidash_DMaaP_KPI', { + //url: '/kpidash_DMaaP_KPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/DMaaP/DMaaP_KPI.html', + controller: 'DMaaP_Ctrl_KPI' + } + } + }).state('root.kpidash_DMaaP_UserDefinedKPI', { + //url: '/kpidash_DMaaP_UserDefinedKPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/DMaaP/DMaaP_UserDefinedKPI.html', + controller: 'DMaaP_Ctrl_UserDefinedKPI' + } + } + }).state('root.kpidash_DMaaP_Metrics', { + //url: '/kpidash_DMaaP_Metrics', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/DMaaP/DMaaP_Metrics.html', + controller: 'DMaaP_Ctrl_Metrics' + } + } + }).state('root.kpidash_ECOMP_Portal', { + //url: '/kpidash_ECOMP_Portal', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/ECOMP_Portal/ECOMP_Portal_KPI.html', + controller: 'ECOMP_Portal_Ctrl_KPI' + } + } + }).state('root.kpidash_ECOMP_Portal_KPI', { + //url: '/kpidash_ECOMP_Portal_KPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/ECOMP_Portal/ECOMP_Portal_KPI.html', + controller: 'ECOMP_Portal_Ctrl_KPI' + } + } + }).state('root.kpidash_ECOMP_Portal_UserDefinedKPI', { + //url: '/kpidash_ECOMP_Portal_UserDefinedKPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/ECOMP_Portal/ECOMP_Portal_UserDefinedKPI.html', + controller: 'ECOMP_Portal_Ctrl_UserDefinedKPI' + } + } + }).state('root.kpidash_ECOMP_Portal_Metrics', { + //url: '/kpidash_ECOMP_Portal_Metrics', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/ECOMP_Portal/ECOMP_Portal_Metrics.html', + controller: 'ECOMP_Portal_Ctrl_Metrics' + } + } + }).state('root.kpidash_InfrastructurePortal', { + //url: '/kpidash_InfrastructurePortal', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/InfrastructurePortal/InfrastructurePortal_KPI.html', + controller: 'InfrastructurePortal_Ctrl_KPI' + } + } + }).state('root.kpidash_InfrastructurePortal_KPI', { + //url: '/kpidash_InfrastructurePortal_KPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/InfrastructurePortal/InfrastructurePortal_KPI.html', + controller: 'InfrastructurePortal_Ctrl_KPI' + } + } + }).state('root.kpidash_InfrastructurePortal_UserDefinedKPI', { + //url: '/kpidash_InfrastructurePortal_UserDefinedKPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/InfrastructurePortal/InfrastructurePortal_UserDefinedKPI.html', + controller: 'InfrastructurePortal_Ctrl_UserDefinedKPI' + } + } + }).state('root.kpidash_InfrastructurePortal_Metrics', { + //url: '/kpidash_InfrastructurePortal_Metrics', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/InfrastructurePortal/InfrastructurePortal_Metrics.html', + controller: 'InfrastructurePortal_Ctrl_Metrics' + } + } + }).state('root.kpidash_MSO', { + //url: '/kpidash_MSO', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/MSO/MSO_KPI.html', + controller: 'MSO_Ctrl_KPI' + } + } + }).state('root.kpidash_MSO_KPI', { + //url: '/kpidash_MSO_KPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/MSO/MSO_KPI.html', + controller: 'MSO_Ctrl_KPI' + } + } + }).state('root.kpidash_MSO_UserDefinedKPI', { + //url: '/kpidash_MSO_UserDefinedKPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/MSO/MSO_UserDefinedKPI.html', + controller: 'MSO_Ctrl_UserDefinedKPI' + } + } + }).state('root.kpidash_MSO_Metrics', { + //url: '/kpidash_MSO_Metrics', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/MSO/MSO_Metrics.html', + controller: 'MSO_Ctrl_Metrics' + } + } + }).state('root.kpidash_Policy', { + //url: '/kpidash_Policy', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/Policy/Policy_KPI.html', + controller: 'Policy_Ctrl_KPI' + } + } + }).state('root.kpidash_Policy_KPI', { + //url: '/kpidash_Policy_KPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/Policy/Policy_KPI.html', + controller: 'Policy_Ctrl_KPI' + } + } + }).state('root.kpidash_Policy_UserDefinedKPI', { + //url: '/kpidash_Policy_UserDefinedKPI', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/Policy/Policy_UserDefinedKPI.html', + controller: 'Policy_Ctrl_UserDefinedKPI' + } + } + }).state('root.kpidash_Policy_Metrics', { + //url: '/kpidash_Policy_Metrics', + views: { + 'content@': { + templateUrl: 'kpi-dashboard/views/Policy/Policy_Metrics.html', + controller: 'Policy_Ctrl_Metrics' + } + } + }); + }); diff --git a/ecomp-portal-FE/client/app/services/admins/admins.service.js b/ecomp-portal-FE/client/app/services/admins/admins.service.js new file mode 100644 index 00000000..ee742af8 --- /dev/null +++ b/ecomp-portal-FE/client/app/services/admins/admins.service.js @@ -0,0 +1,176 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +(function () { + class AdminsService { + constructor($q, $log, $http, conf,uuid) { + this.$q = $q; + this.$log = $log; + this.$http = $http; + this.conf = conf; + this.uuid = uuid; + } + + getAccountAdmins() { + let deferred = this.$q.defer(); + this.$log.info('AdminsService::get all applications admins list'); + this.$http({ + method: "GET", + cache: false, + url: this.conf.api.accountAdmins, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }) + .then( res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("AdminsService::getAccountAdmins Failed"); + } else { + this.$log.info('AdminsService::getAccountAdmins Succeeded'); + deferred.resolve(res.data); + } + }) + .catch( status => { + deferred.reject(status); + }); + + return deferred.promise; + } + + getAdminAppsRoles(orgUserId) { + let deferred = this.$q.defer(); + this.$log.info('AdminsService::getAdminAppsRoles.adminAppsRoles'); + + this.$http({ + method: "GET", + url: this.conf.api.adminAppsRoles, + params: {orgUserId: orgUserId}, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then( res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("AdminsService::getAdminAppsRoles.adminAppsRoles Failed"); + } else { + this.$log.info('AdminsService::getAdminAppsRoles.adminAppsRoles Succeeded'); + deferred.resolve(res.data); + } + }) + .catch( status => { + deferred.reject(status); + }); + + return deferred.promise; + } + + updateAdminAppsRoles(newAdminAppRoles) { + let deferred = this.$q.defer(); + this.$log.info('AdminsService::updateAdminAppsRoles'); + this.$http({ + method: "PUT", + url: this.conf.api.adminAppsRoles, + data: newAdminAppRoles, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then( res => { + deferred.resolve(res.data); + }) + .catch( status => { + deferred.reject(status); + }); + + return deferred.promise; + } + + addNewUser(newUser,checkDuplicate) { + // this.$log.info(newContactUs) + let deferred = this.$q.defer(); + // this.$log.info('ContactUsService:: add Contact Us' + JSON.stringify(newContactUs)); + + var newUserObj={ + firstName:newUser.firstName, + middleInitial:newUser.middleName, + lastName:newUser.lastName, + email:newUser.emailAddress, + loginId:newUser.loginId, + loginPwd:newUser.loginPwd, + }; + this.$http({ + url: this.conf.api.saveNewUser + "?isCheck=" + checkDuplicate, + method: 'POST', + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + }, + data: newUserObj + }).then(res => { + // this.$log.info('ContactUsService:: add Contact Us res' ,res); + // If response comes back as a redirected HTML page which IS NOT a success + if (res==null || Object.keys(res.data).length == 0 || res.data.message == 'failure') { + deferred.reject("Add new User failed"); + this.$log.error('adminService:: add New User failed'); + } else { + deferred.resolve(res.data); + } + }).catch(errRes => { + deferred.reject(errRes); + }); + return deferred.promise; + } + + /** + * Tests the specified password against complexity requirements. + * Returns an explanation message if the test fails; null if it passes. + */ + isComplexPassword(str) { + let minLength = 8; + let message = 'Password is too simple. Minimum length is '+ minLength + ', ' + + 'and it must use letters, digits and special characters.'; + if (str == null) + return message; + + let hasLetter = false; + let hasDigit = false; + let hasSpecial = false; + var code, i, len; + for (i = 0, len = str.length; i < len; i++) { + code = str.charCodeAt(i); + if (code > 47 && code < 58) // numeric (0-9) + hasDigit = true; + else if ((code > 64 && code < 91) || (code > 96 && code < 123)) // A-Z, a-z + hasLetter = true; + else + hasSpecial = true; + } // for + + if (str.length < minLength || !hasLetter || !hasDigit || !hasSpecial) + return message; + + // All is well. + return null; + } + + } + AdminsService.$inject = ['$q', '$log', '$http', 'conf','uuid4']; + angular.module('ecompApp').service('adminsService', AdminsService) +})(); diff --git a/ecomp-portal-FE/client/app/services/applications/applications.service.js b/ecomp-portal-FE/client/app/services/applications/applications.service.js new file mode 100644 index 00000000..38cfd650 --- /dev/null +++ b/ecomp-portal-FE/client/app/services/applications/applications.service.js @@ -0,0 +1,346 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; + +(function () { + class ApplicationsService { + constructor($q, $log, $http, conf, uuid) { + this.$q = $q; + this.$log = $log; + this.$http = $http; + this.conf = conf; + this.uuid = uuid; + } + + getPersUserApps() { + let deferred = this.$q.defer(); + // this.$log.info('ApplicationsService::getPersUserApps'); + this.$http.get(this.conf.api.persUserApps, + { + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }) + .then( res => { + // If response comes back as a redirected HTML page which IS NOT a success + // But don't declare an empty list to be an error. + if (res == null || res.data == null) { + deferred.reject("ApplicationsService::getPersUserApps Failed"); + } else { + deferred.resolve(res.data); + } + }) + .catch( status => { + deferred.reject(status); + }); + return deferred.promise; + } + + getUserApps(){ + let deferred = this.$q.defer(); + this.$log.info('ApplicationsService::getUserApps'); + this.$http.get(this.conf.api.userApps, + { + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }) + .then( res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("ApplicationsService::getUserApps Failed"); + } else { + this.$log.info('ApplicationsService::getUserApps Succeeded'); + deferred.resolve(res.data); + } + }) + .catch( status => { + deferred.reject(status); + }); + + return deferred.promise; + } + + getAvailableApps() { + let deferred = this.$q.defer(); + this.$log.info('ApplicationsService::getAvailableApps'); + this.$http( + { + method: "GET", + url: this.conf.api.availableApps, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }) + .then( res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("ApplicationsService::getAvailableApps Failed"); + } else { + this.$log.info('ApplicationsService::getAvailableApps Succeeded'); + deferred.resolve(res.data); + } + }) + .catch( status => { + deferred.reject(status); + }); + + return deferred.promise; + } + + getAdminApps(){ + let canceller = this.$q.defer(); + let isActive = false; + + let cancel = () => { + if(isActive){ + this.$log.debug('ApplicationsService::getAdminApps: canceling the request'); + canceller.resolve(); + } + }; + + let promise = () => { + isActive = true; + let deferred = this.$q.defer(); + this.$log.info('ApplicationsService::getAdminApps: starting'); + this.$http({method: "GET", + url: this.conf.api.adminApps, + cache: false, + timeout: canceller.promise, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + isActive = false; + if (Object.keys(res.data).length == 0) { + deferred.reject("ApplicationsService::adminApps Failed"); + } else { + this.$log.info('ApplicationsService::adminApps Succeeded'); + deferred.resolve(res.data); + } + }) + .catch(status => { + isActive = false; + deferred.reject(status); + }); + return deferred.promise; + }; + + return { + cancel: cancel, + promise: promise + }; + } + + getAppsForSuperAdminAndAccountAdmin(){ + let deferred = this.$q.defer(); + this.$log.info('ApplicationsService::getAppsForSuperAdminAndAccountAdmin'); + this.$http({method: "GET", + url: this.conf.api.appsForSuperAdminAndAccountAdmin, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("ApplicationsService::getAppsForSuperAdminAndAccountAdmin Failed"); + } else { + this.$log.info('ApplicationsService::getAppsForSuperAdminAndAccountAdmin Succeeded'); + deferred.resolve(res.data); + } + }) + .catch(status => { + deferred.reject(status); + }); + return deferred.promise; + } + + getAdminAppsSimpler(){ + let deferred = this.$q.defer(); + this.$log.info('ApplicationsService::getAdminApps'); + this.$http({method: "GET", + url: this.conf.api.adminApps, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("ApplicationsService::getAdminApps Failed"); + } else { + this.$log.info('ApplicationsService::getAdminApps Succeeded'); + deferred.resolve(res.data); + } + }) + .catch(status => { + deferred.reject(status); + }); + return deferred.promise; + } + + getOnboardingApps(){ + let deferred = this.$q.defer(); + this.$log.info('ApplicationsService::getOnboardingApps'); + + this.$http.get(this.conf.api.onboardingApps, + { + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }) + .then( res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("ApplicationsService::getOnboardingApps Failed"); + } else { + this.$log.info('ApplicationsService::getOnboardingApps Succeeded'); + deferred.resolve(res.data); + } + }) + .catch( status => { + deferred.reject(status); + }); + + return deferred.promise; + } + + addOnboardingApp(newApp){ + let deferred = this.$q.defer(); + this.$log.info('applications-service::addOnboardingApp'); + this.$log.debug('applications-service::addOnboardingApp with:', newApp); + + this.$http({ + method: "POST", + url: this.conf.api.onboardingApps, + data: newApp, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then( res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("ApplicationsService::addOnboardingApp Failed"); + } else { + this.$log.info('ApplicationsService::addOnboardingApp Succeeded'); + deferred.resolve(res.data); + } + }) + .catch( status => { + deferred.reject(status); + }); + return deferred.promise; + } + + updateOnboardingApp(appData){ + let deferred = this.$q.defer(); + this.$log.info('ApplicationsService::addOnboardingApp'); + if(!appData.id){ + this.$log.error('ApplicationsService::addOnboardingApp: App id not found!'); + return deferred.reject('App id not found'); + } + + this.$http({ + method: "PUT", + url: this.conf.api.onboardingApps, + data: appData, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then( res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("ApplicationsService::updateOnboardingApp Failed"); + } else { + this.$log.info('ApplicationsService::updateOnboardingApp Succeeded'); + deferred.resolve(res.data); + } + }) + .catch( status => { + deferred.reject(status); + }); + return deferred.promise; + } + + deleteOnboardingApp(appId) { + let deferred = this.$q.defer(); + let url = this.conf.api.onboardingApps + '/' + appId; + + this.$log.info('applications.service::deleteOnboardingApp' +appId); + + this.$http({ + method: "DELETE", + url: url, + cache: false, + data:'', + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("applications.service::deleteOnboardingApp Failed"); + } else { + this.$log.info('applications.service::deleteOnboardingApp succeeded: '); + deferred.resolve(res.data); + } + }) + .catch(errRes => { + deferred.reject(errRes); + }); + return deferred.promise; + } + + getTopMenuData(selectedApp) { + let deferred = this.$q.defer(); + this.$log.info('ApplicationsService:getTopMenuData'); + this.$log.debug('ApplicationsService:getTopMenuData with:', selectedApp); + + } + + ping(){ + let deferred = this.$q.defer(); + this.$log.info('ApplicationsService::ping: '); + + this.$http.get(this.conf.api.ping, + { + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }) + .success( res => { + if (Object.keys(res).length == 0) { + deferred.reject("ApplicationsService::ping: Failed"); + } else { + this.$log.info('ApplicationsService::ping: Succeeded'); + deferred.resolve(res); + } + }) + .error( status => { + deferred.reject(status); + }); + + return deferred.promise; + } + } + ApplicationsService.$inject = ['$q', '$log', '$http', 'conf','uuid4']; + angular.module('ecompApp').service('applicationsService', ApplicationsService) +})(); diff --git a/ecomp-portal-FE/client/app/services/catalog/catalog.service.js b/ecomp-portal-FE/client/app/services/catalog/catalog.service.js new file mode 100644 index 00000000..2ad594a2 --- /dev/null +++ b/ecomp-portal-FE/client/app/services/catalog/catalog.service.js @@ -0,0 +1,95 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +(function () { + class CatalogService { + + constructor($q, $log, $http, conf, uuid) { + this.$q = $q; + this.$log = $log; + this.$http = $http; + this.conf = conf; + this.uuid = uuid; + } + + getAppCatalog() { + let deferred = this.$q.defer(); + this.$http( + { + method: "GET", + url: this.conf.api.appCatalog, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }) + .then( res => { + // Detect non-JSON + if (res == null || res.data == null) { + deferred.reject("CatalogService::getAppCatalog Failed"); + } else { + deferred.resolve(res.data); + } + }) + .catch( status => { + this.$log.error('CatalogService:getAppCatalog failed: ' + status); + deferred.reject(status); + }); + return deferred.promise; + } + + // Expects an object with fields matching model class AppCatalogSelection: + // appId (number), select (boolean), pending (boolean). + updateAppCatalog(appData) { + let deferred = this.$q.defer(); + // Validate the request, maybe this is overkill + if (appData == null || appData.appId == null || appData.select == null) { + var msg = 'CatalogService::updateAppCatalog: field appId and/or select not found'; + this.$log.error(msg); + return deferred.reject(msg); + } + this.$http({ + method: "PUT", + url: this.conf.api.appCatalog, + data: appData, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then( res => { + // Detect non-JSON + if (res == null || res.data == null) { + deferred.reject("CatalogService::updateAppCatalog Failed"); + } else { + deferred.resolve(res.data); + } + }) + .catch( status => { + this.$log.error('CatalogService:updateAppCatalog failed: ' + status); + deferred.reject(status); + }); + return deferred.promise; + } + + } + + CatalogService.$inject = ['$q', '$log', '$http', 'conf','uuid4']; + angular.module('ecompApp').service('catalogService', CatalogService) +})(); diff --git a/ecomp-portal-FE/client/app/services/confirm-box/confirm-box.service.js b/ecomp-portal-FE/client/app/services/confirm-box/confirm-box.service.js new file mode 100644 index 00000000..affc6d3f --- /dev/null +++ b/ecomp-portal-FE/client/app/services/confirm-box/confirm-box.service.js @@ -0,0 +1,153 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; + +(function () { + class ConfirmBoxService { + constructor($q, $log, ngDialog) { + this.$q = $q; + this.$log = $log; + this.ngDialog = ngDialog; + } + + showInformation(message) { + let deferred = this.$q.defer(); + this.ngDialog.open({ + templateUrl: 'app/views/confirmation-box/information-box.tpl.html', + controller: 'ConfirmationBoxCtrl', + controllerAs: 'confirmBox', + className: 'confirm-box ngdialog-theme-default', + showClose: false, + data: { + message: message + } + }).closePromise.then(confirmed => { + deferred.resolve(confirmed.value); + }).catch(err => { + deferred.reject(err); + }); + return deferred.promise; + }; + + confirm(message) { + let deferred = this.$q.defer(); + this.ngDialog.open({ + templateUrl: 'app/views/confirmation-box/confirmation-box.tpl.html', + controller: 'ConfirmationBoxCtrl', + controllerAs: 'confirmBox', + className: 'confirm-box ngdialog-theme-default', + showClose: false, + data: { + message: message + } + }).closePromise.then(confirmed => { + deferred.resolve(confirmed.value); + }).catch(err => { + deferred.reject(err); + }); + return deferred.promise; + }; + + + showDynamicInformation(message, templatePath, controller) { + let deferred = this.$q.defer(); + this.ngDialog.open({ + templateUrl: templatePath, + controller: controller, + controllerAs: 'confirmBox', + className: 'confirm-box ngdialog-theme-default', + showClose: false, + data: { + message: message + } + }).closePromise.then(confirmed => { + deferred.resolve(confirmed.value); + }).catch(err => { + deferred.reject(err); + }); + return deferred.promise; + }; + + deleteItem(item) { + let deferred = this.$q.defer(); + this.ngDialog.open({ + templateUrl: 'app/views/confirmation-box/confirmation-box.tpl.html', + controller: 'ConfirmationBoxCtrl', + controllerAs: 'confirmBox', + className: 'confirm-box ngdialog-theme-default', + showClose: false, + data: { + item: item, + title: 'Functional Menu - Delete' + } + }).closePromise.then(confirmed => { + deferred.resolve(confirmed.value); + }).catch(err => { + deferred.reject(err); + }); + return deferred.promise; + }; + + moveMenuItem(message) { + let deferred = this.$q.defer(); + this.ngDialog.open({ + templateUrl: 'app/views/confirmation-box/dragdrop-confirmation-box.tpl.html', + controller: 'ConfirmationBoxCtrl', + controllerAs: 'confirmBox', + className: 'confirm-box ngdialog-theme-default', + showClose: false, + data: { + message: message, + title:'Functional Menu - Move' + } + }).closePromise.then(confirmed => { + deferred.resolve(confirmed.value); + }).catch(err => { + deferred.reject(err); + }); + return deferred.promise; + }; + + makeAdminChanges(message) { + let deferred = this.$q.defer(); + this.ngDialog.open({ + templateUrl: 'app/views/confirmation-box/admin-confirmation-box.tpl.html', + controller: 'ConfirmationBoxCtrl', + controllerAs: 'confirmBox', + className: 'confirm-box ngdialog-theme-default', + showClose: false, + data: { + message: message, + title: 'Admin Update' + } + }).closePromise.then(confirmed => { + deferred.resolve(confirmed.value); + }).catch(err => { + deferred.reject(err); + }); + return deferred.promise; + }; + + + } + ConfirmBoxService.$inject = ['$q', '$log', 'ngDialog']; + angular.module('ecompApp').service('confirmBoxService', ConfirmBoxService) +})(); diff --git a/ecomp-portal-FE/client/app/services/contact-us/contact-us.service.js b/ecomp-portal-FE/client/app/services/contact-us/contact-us.service.js new file mode 100644 index 00000000..241a12ab --- /dev/null +++ b/ecomp-portal-FE/client/app/services/contact-us/contact-us.service.js @@ -0,0 +1,247 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +/** + * Created by robertlo on 10/10/2016. + */ +'use strict'; + +(function () { + class ContactUsService { + constructor($q, $log, $http, conf, uuid) { + this.$q = $q; + this.$log = $log; + this.$http = $http; + this.conf = conf; + this.uuid = uuid; + } + getListOfApp() { + // this.$log.info('ContactUsService::getListOfavailableApps: get all app list'); + let deferred = this.$q.defer(); + // this.$log.info('ContactUsService::getListOfavailableApps: ', this.conf.api.listOfApp); + this.$http({ + method: "GET", + url: this.conf.api.availableApps, + cache: false + }).then( res => { + // If response comes back as a redirected HTML page which IS NOT a success + // this.$log.info('ContactUsService::getListOfavailableApps availableApps response: ', res); + if (Object.keys(res).length == 0) { + deferred.reject("ContactUsService::getListOfavailableApps: Failed"); + } else { + // this.$log.debug('ContactUsService::getListOfavailableApps: Succeeded results: ', res); + deferred.resolve(res); + } + }).catch( status => { + this.$log.error('ContactUsService::getListOfavailableApps: query error: ',status); + deferred.reject(status); + }); + return deferred.promise; + } + + getContactUs() { + let deferred = this.$q.defer(); + // this.$log.info('ContactUsService::get all Contact Us list'); + this.$http({ + url: this.conf.api.getContactUS, + method: 'GET', + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + // If response comes back as a redirected HTML page which IS NOT a success + if (Object.keys(res.data).length == 0) { + deferred.reject("ContactUsService::getContactUs Failed"); + } else { + deferred.resolve(res.data); + } + }) + .catch(status => { + deferred.reject(status); + }); + return deferred.promise; + } + + getAppsAndContacts() { + let deferred = this.$q.defer(); + // this.$log.info('ContactUsService::getAppsAndContacts'); + this.$http({ + url: this.conf.api.getAppsAndContacts, + method: 'GET', + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + // If response comes back as a redirected HTML page which IS NOT a success + if (Object.keys(res.data).length == 0) { + deferred.reject("ContactUsService::getAppsAndContacts Failed"); + } else { + deferred.resolve(res.data); + } + }) + .catch(status => { + deferred.reject(status); + }); + return deferred.promise; + } + + getContactUSPortalDetails(){ + let deferred = this.$q.defer(); + // this.$log.info('ContactUsService::get all Contact Us Portal Details'); + this.$http({ + url: this.conf.api.getContactUSPortalDetails, + method: 'GET', + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + // If response comes back as a redirected HTML page which IS NOT a success + if (Object.keys(res.data).length == 0) { + deferred.reject("ContactUsService::getContactUSPortalDetails Failed"); + } else { + deferred.resolve(res.data); + } + }) + .catch(status => { + deferred.reject(status); + }); + return deferred.promise; + } + + getAppCategoryFunctions(){ + let deferred = this.$q.defer(); + // this.$log.info('ContactUsService::get all App Category Functions'); + this.$http({ + url: this.conf.api.getAppCategoryFunctions, + method: 'GET', + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + // If response comes back as a redirected HTML page which IS NOT a success + if (Object.keys(res.data).length == 0) { + deferred.reject("ContactUsService::getAppCategoryFunctions Failed"); + } else { + deferred.resolve(res.data); + } + }) + .catch(status => { + deferred.reject(status); + }); + return deferred.promise; + } + + addContactUs(newContactUs) { + // this.$log.info('ContactUsService::add a new Contact Us'); + // this.$log.info(newContactUs) + let deferred = this.$q.defer(); + // this.$log.info('ContactUsService:: add Contact Us' + JSON.stringify(newContactUs)); + + var contactUsObj={ + appId:newContactUs.app.value, + appName:newContactUs.app.title, + description:newContactUs.desc, + contactName:newContactUs.name, + contactEmail:newContactUs.email, + url:newContactUs.url, + }; + this.$http({ + url: this.conf.api.saveContactUS, + method: 'POST', + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + }, + data: contactUsObj + }).then(res => { + // this.$log.info('ContactUsService:: add Contact Us res' ,res); + // If response comes back as a redirected HTML page which IS NOT a success + if (res==null || Object.keys(res.data).length == 0 || res.data.message == 'failure') { + deferred.reject("Add Contact Us failed"); + this.$log.error('ContactUsService:: add Contact Us failed'); + } else { + deferred.resolve(res.data); + } + }).catch(errRes => { + deferred.reject(errRes); + }); + return deferred.promise; + } + + modifyContactUs(contactUsObj) { + // this.$log.info('ContactUsService::edit Contact Us',contactUsObj); + let deferred = this.$q.defer(); + this.$http({ + url: this.conf.api.saveContactUS, + method: 'POST', + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + }, + data: contactUsObj + }).then(res => { + // this.$log.info('ContactUsService:: edit Contact Us res' ,res); + // If response comes back as a redirected HTML page which IS NOT a success + if (res==null || Object.keys(res.data).length == 0 || res.data.message == 'failure') { + deferred.reject("Edit Contact Us failed"); + this.$log.error('ContactUsService:: edit Contact Us failed'); + } else { + deferred.resolve(res.data); + } + }).catch(errRes => { + deferred.reject(errRes); + }); + return deferred.promise; + } + + removeContactUs(id) { + let deferred = this.$q.defer(); + let url = this.conf.api.deleteContactUS + '/' + id; + // this.$log.info('ContactUsService:: remove Contact Us'); + this.$http({ + url: url, + method: 'POST', + cache: false, + data: '', + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + // If response comes back as a redirected HTML page which IS NOT a success + // this.$log.info("ContactUsService::removeContactUs res",res); + deferred.resolve(res.data); + if (Object.keys(res.data).length == 0) { + deferred.reject("ContactUsService::removeContactUs Failed"); + } else { + deferred.resolve(res.data); + } + }).catch(errRes => { + deferred.reject(errRes); + }); + + return deferred.promise; + } + } + ContactUsService.$inject = ['$q', '$log', '$http', 'conf', 'uuid4']; + angular.module('ecompApp').service('contactUsService', ContactUsService) +})(); \ No newline at end of file diff --git a/ecomp-portal-FE/client/app/services/dashboard/dashboard.service.js b/ecomp-portal-FE/client/app/services/dashboard/dashboard.service.js new file mode 100644 index 00000000..bc9c9cee --- /dev/null +++ b/ecomp-portal-FE/client/app/services/dashboard/dashboard.service.js @@ -0,0 +1,185 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +/** + * Created by robertlo on 09/26/2016. + */ +'use strict'; + +(function () { + class DashboardService { + constructor($q, $log, $http, conf, uuid) { + this.$q = $q; + this.$log = $log; + this.$http = $http; + this.conf = conf; + this.dashboardService = null; + this.uuid = uuid; + } + + getCommonWidgetData(widgetType) { + // this.$log.info('ecomp::dashboard-service::getting news data'); + let deferred = this.$q.defer(); + let url = this.conf.api.commonWidget + '?resourceType=' + widgetType; + + this.$http({ + method: "GET", + url: url, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }) + .then( res => { + // this.$log.info('ecomp::dashboard-service::getting news data',res); + // If response comes back as a redirected HTML page which IS NOT a success + if (Object.keys(res.data).length == 0 || Object.keys(res.data.response) ==null || Object.keys(res.data.response.items) ==null) { + deferred.reject("ecomp::dashboard-service::getNewsData Failed"); + } else { + this.userProfile = res.data; + // this.$log.info('ecomp::dashboard-service::getNewsData Succeeded'); + deferred.resolve(res.data); + } + }) + .catch( status => { + deferred.reject(status); + }); + return deferred.promise; + } + + saveCommonWidgetData(newData){ + let deferred = this.$q.defer(); + //this.$log.info('ecomp::dashboard-service::saveCommonWidgetData'); + //this.$log.debug('ecomp::dashboard-service::saveCommonWidgetData with:', newData); + + this.$http({ + method: "POST", + url: this.conf.api.commonWidget, + data: newData, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then( res => { + // If response comes back as a redirected HTML page which IS NOT a success + // this.$log.info(res.data); + if (Object.keys(res.data).length == 0) { + deferred.reject("ecomp::dashboard-service::saveCommonWidgetData Failed"); + } else { + // this.$log.info('ecomp::dashboard-service::saveCommonWidgetData Succeeded'); + deferred.resolve(res.data); + } + }) + .catch( status => { + deferred.reject(status); + }); + return deferred.promise; + } + + + + removeCommonWidgetData(widgetToRemove){ + let deferred = this.$q.defer(); + // this.$log.info('ecomp::dashboard-service::removeCommonWidgetData'); + // this.$log.debug('ecomp::dashboard-service::removeCommonWidgetData with:', widgetToRemove); + this.$http({ + method: "POST", + url: this.conf.api.deleteCommonWidget, + data: widgetToRemove, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then( res => { + // If response comes back as a redirected HTML page which IS NOT a success + // this.$log.info(res.data); + if (Object.keys(res.data).length == 0) { + deferred.reject("ecomp::dashboard-service::saveCommonWidgetData Failed"); + } else { + // this.$log.info('ecomp::dashboard-service::saveCommonWidgetData Succeeded'); + deferred.resolve(res.data); + } + }) + .catch( status => { + deferred.reject(status); + }); + return deferred.promise; + } + + getSearchAllByStringResults(searchStr) { + // this.$log.info('ecomp::getSearchAllByStringResults::getting search by string results'); + let deferred = this.$q.defer(); + let url = this.conf.api.getSearchAllByStringResults; + + this.$http({ + method: "GET", + url : url, + params : { + 'searchString' : searchStr + }, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then( res => { + // If response comes back as a redirected HTML page which IS NOT a success + if (Object.keys(res.data).length == 0) { + deferred.reject("ecomp::dashboard-service::getSearchAllByStringResults Failed"); + } else { + //this.searchResults = res.data; + // this.$log.info('ecomp::dashboard-service::getSearchAllByStringResults Succeeded'); + deferred.resolve(res.data.response); + } + }).catch( status => { + this.$log.error('ecomp::getSearchAllByStringResults error'); + deferred.reject(status); + }); + return deferred.promise; + + } + + getOnlineUserUpdateRate() { + let deferred = this.$q.defer(); + let url = this.conf.api.onlineUserUpdateRate; + this.$http({ + method: "GET", + url: url, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then( res => { + // If response comes back as a redirected HTML page which IS NOT a success + if (Object.keys(res.data).length == 0) { + deferred.reject("ecomp::dashboard-service::getOnlineUserUpdateRate Failed"); + } else { + this.$log.info('ecomp::dashboard-service::getOnlineUserUpdateRate Succeeded',res); + deferred.resolve(res.data); + } + }).catch( status => { + deferred.reject(status); + }); + return deferred.promise; + } + + } + + DashboardService.$inject = ['$q', '$log', '$http', 'conf', 'uuid4']; + angular.module('ecompApp').service('dashboardService', DashboardService) +})(); diff --git a/ecomp-portal-FE/client/app/services/error-messages/error-messages.service.js b/ecomp-portal-FE/client/app/services/error-messages/error-messages.service.js new file mode 100644 index 00000000..032fb803 --- /dev/null +++ b/ecomp-portal-FE/client/app/services/error-messages/error-messages.service.js @@ -0,0 +1,23 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; +let errorMessageByCode = {1201: 'Value already exists'}; +angular.module('ecompApp').constant('errorMessageByCode', errorMessageByCode); diff --git a/ecomp-portal-FE/client/app/services/functionalMenu/functionalMenu.service.js b/ecomp-portal-FE/client/app/services/functionalMenu/functionalMenu.service.js new file mode 100644 index 00000000..ec3bd387 --- /dev/null +++ b/ecomp-portal-FE/client/app/services/functionalMenu/functionalMenu.service.js @@ -0,0 +1,253 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; + +(function () { + class FunctionalMenuService { + constructor($q, $log, $http, conf,uuid) { + this.$q = $q; + this.$log = $log; + this.$http = $http; + this.conf = conf; + this.uuid = uuid; + } + + + getManagedRolesMenu( appId ) + { + let deferred = this.$q.defer(); + this.$log.info('FunctionalMenuService::getManagedRolesMenu'); + let url = this.conf.api.appRoles.replace(':appId', appId); + this.$log.info("FunctionalMenuService::getManagedRolesMenu url: "+url); + + this.$http({ + method: "GET", + url: url, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("functionalMenu.service::getManagedRolesMenu Failed"); + } else { + this.$log.info('functionalMenu.service::getManagedRolesMenu succeeded: '); + deferred.resolve(res.data); + } + }) + .catch(status => { + deferred.reject(status); + }); + return deferred.promise; + } + + getAvailableApplications() + { + let deferred = this.$q.defer(); + this.$log.info('FunctionalMenuService::getManagedRolesMenu:getAvailableApplications'); + + this.$http({ + method: "GET", +// url: this.conf.api.availableApps, + url: this.conf.api.allAvailableApps, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("FunctionalMenuService::getManagedRolesMenu:getAvailableApplications Failed"); + } else { + this.$log.info('FunctionalMenuService::getManagedRolesMenu:getAvailableApplications succeeded: '); + deferred.resolve(res.data); + } + }) + .catch(status => { + deferred.reject(status); + }); + return deferred.promise; + } + getMenuDetails( menuId ) + { + let deferred = this.$q.defer(); + this.$log.info('FunctionalMenuService::getMenuDetails:getMenuDetails'); + let url = this.conf.api.functionalMenuItemDetails.replace(':menuId',menuId); + this.$log.info("FunctionalMenuService::getMenuDetails url: "+url); + + this.$http({ + method: "GET", + url: url, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("FunctionalMenuService::getMenuDetails:getMenuDetails Failed"); + } else { + this.$log.info('FunctionalMenuService::getMenuDetails:getMenuDetails succeeded: '); + deferred.resolve(res.data); + } + }) + .catch(status => { + deferred.reject(status); + }); + return deferred.promise; + } + + + getManagedFunctionalMenu() { + let deferred = this.$q.defer(); + this.$log.info('FunctionalMenuService::getMenuDetails:getManagedFunctionalMenu'); + + this.$http({ + method: "GET", + url: this.conf.api.functionalMenuForEditing, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("FunctionalMenuService::getManagedFunctionalMenu Failed"); + } else { + this.$log.info('FunctionalMenuService::getManagedFunctionalMenu succeeded: '); + deferred.resolve(res.data); + } + }) + .catch(status => { + deferred.reject(status); + }); + return deferred.promise; + } + + regenerateFunctionalMenuAncestors() { + let deferred = this.$q.defer(); + this.$log.info('FunctionalMenuService::regenerateFunctionalMenuAncestors'); + + this.$http({ + method: "GET", + url: this.conf.api.regenerateFunctionalMenuAncestors, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("FunctionalMenuService::regenerateFunctionalMenuAncestors Failed"); + } else { + this.$log.info('FunctionalMenuService::regenerateFunctionalMenuAncestors succeeded: '); + deferred.resolve(res.data); + } + }) + .catch(status => { + deferred.reject(status); + }); + return deferred.promise; + } + + saveEditedMenuItem(menuData) { + let deferred = this.$q.defer(); + this.$log.info('FunctionalMenuService::saveEditedMenuItem: ' + menuData); + + let url = this.conf.api.functionalMenuItem; + this.$http({ + method: "PUT", + url: url, + cache: false, + data: menuData, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("FunctionalMenuService::saveEditedMenuItem Failed"); + } else { + this.$log.info('FunctionalMenuService::saveEditedMenuItem succeeded: '); + deferred.resolve(res.data); + } + }) + .catch(errRes => { + deferred.reject(errRes); + }); + return deferred.promise; + } + + saveMenuItem(menuData) { + let deferred = this.$q.defer(); + this.$log.info('FunctionalMenuService::saveMenuItem: ' + JSON.stringify(menuData)); + + let url = this.conf.api.functionalMenuItem; + this.$http({ + method: "POST", + url: url, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + }, + data: menuData + }).then(res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("FunctionalMenuService::saveMenuItem: Failed"); + } else { + this.$log.info('FunctionalMenuService::saveMenuItem: succeeded: '); + deferred.resolve(res.data); + } + }) + .catch(errRes => { + deferred.reject(errRes); + }); + return deferred.promise; + } + + + deleteMenuItem(menuId) { + let deferred = this.$q.defer(); + let url = this.conf.api.functionalMenuItem + '/' + menuId; + + this.$log.info('FunctionalMenuService::deleteMenuItem: ' +menuId); + + this.$http({ + method: "DELETE", + url: url, + cache: false, + data:'', + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("FunctionalMenuService::deleteMenuItem Failed"); + } else { + this.$log.info('FunctionalMenuService::deleteMenuItem succeeded: '); + deferred.resolve(res.data); + } + }) + .catch(errRes => { + deferred.reject(errRes); + }); + return deferred.promise; + } + +} + FunctionalMenuService.$inject = ['$q', '$log', '$http', 'conf','uuid4']; + angular.module('ecompApp').service('functionalMenuService', FunctionalMenuService) +})(); diff --git a/ecomp-portal-FE/client/app/services/global-constants/global-constants.js b/ecomp-portal-FE/client/app/services/global-constants/global-constants.js new file mode 100644 index 00000000..fa13dae4 --- /dev/null +++ b/ecomp-portal-FE/client/app/services/global-constants/global-constants.js @@ -0,0 +1,21 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +angular.module('ecompApp').value('ECOMP_URL_REGEX', /^((?:https?\:\/\/|ftp?\:\/\/)?(w{3}.)?(?:[-a-z0-9]+\.)*[-a-z0-9]+.*)[^-_.]$/i); diff --git a/ecomp-portal-FE/client/app/services/kpi-dashboard/kpi-dashboard.service.js b/ecomp-portal-FE/client/app/services/kpi-dashboard/kpi-dashboard.service.js new file mode 100644 index 00000000..ff9b66b8 --- /dev/null +++ b/ecomp-portal-FE/client/app/services/kpi-dashboard/kpi-dashboard.service.js @@ -0,0 +1,185 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; + +(function () { + class KpiDashboardService { + constructor($q, $log, $http, conf, uuid) { + this.$q = $q; + this.$log = $log; + this.$http = $http; + this.conf = conf; + this.uuid = uuid; + this.applicationsHomeUrl = 'kpidash'; + } + + kpiApiCall(ApiName) { + let deferred = this.$q.defer(); + this.$log.info('KpiDashboardService::kpiApiCall: '+ApiName); + this.$http({ + method: 'GET', + url: this.conf.api[ApiName], + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + this.$log.debug("KpiDashboardService::kpiApiCall: response successfully retrieved"); + if (Object.keys(res.data).length == 0) { + deferred.reject("KpiDashboardService::kpiApiCall: "+ ApiName+ " Failed"); + } else { + deferred.resolve(res); + } + }) + .catch(status => { + deferred.reject(status); + }); + return deferred.promise; + } + + getKpiUserStoriesStats(){ + return this.kpiApiCall("getKpiDashUserStoriesStats"); + } + getKpiUserApiStats(){ + return this.kpiApiCall("getKpiDashUserApiStats"); + } + getKpiLocStats(){ + return this.kpiApiCall("getKpiDashLocStats"); + } + getKpiLocStatsCat(){ + return this.kpiApiCall("getKpiDashLocStatsCat"); + } + getKpiServiceSupported(){ + return this.kpiApiCall("getKpiDashServiceSupported"); + } + getKpiPublishedDelivered(){ + return this.kpiApiCall("getKpiDashPublishedDelivered"); + } + getKpiFeedStats(){ + return this.kpiApiCall("getKpiDashFeedStats"); + } + getKpiUserApis(){ + return this.kpiApiCall("getKpiDashUserApis"); + } + getKpiGeoMapUrl(){ + return this.kpiApiCall("getKpiDashGeoMapUrl"); + } + getKpiRCloudAUrl(){ + return this.kpiApiCall("getKpiDashRCloudAUrl"); + } + getKpiGeoMapApiUrl(){ + return this.kpiApiCall("getKpiDashGeoMapApiUrl"); + } + + getToplevelgTabs1() { + var toplevelgTabs1=[{ + title : 'eCOMP', + id : 'ECOMP', + url : this.applicationsHomeUrl, + state : 'root.kpidash_ECOMP' + }] + return toplevelgTabs1; + } + + getToplevelgTabs2() { + var toplevelgTabs2 = [ { + title : 'A&AI', + id : 'A&AI', + url : this.applicationsHomeUrl, + state : 'root.kpidash_AAI' + }, { + title : 'APP-C', + id : 'APP-C', + url : this.applicationsHomeUrl, + state : 'root.kpidash_APPC' + }, { + title : 'ASDC', + id : 'ASDC', + url : this.applicationsHomeUrl, + state : 'root.kpidash_ASDC' + }, { + title : 'DCAE', + id : 'DCAE', + url : this.applicationsHomeUrl, + state : 'root.kpidash_DCAE' + }, { + title : 'OpenECOMP Portal', + id : 'OpenECOMP Portal', + url : this.applicationsHomeUrl, + state : 'root.kpidash_ECOMP_Portal' + }, { + title : 'i-Portal', + id : 'i-Portal', + url : this.applicationsHomeUrl, + state : 'root.kpidash_InfrastructurePortal' + }, { + title : 'MSO', + id : 'MSO', + url : this.applicationsHomeUrl, + state : 'root.kpidash_MSO' + }, { + title : 'Policy', + id : 'Policy', + url : this.applicationsHomeUrl, + state : 'root.kpidash_Policy' + }]; + return toplevelgTabs2; + } + + getToplevelgTabs3() { + var toplevelgTabs3 = [ { + title : 'Closed Loop', + id : 'Closedloop', + url : this.applicationsHomeUrl, + state : 'root.kpidash_Closedloop' + }, { + title : 'eDMaaP', + id : 'DMaaP', + url : this.applicationsHomeUrl, + state : 'root.kpidash_DMaaP' + } ]; + return toplevelgTabs3; + } + + getGenericTabs(activeTab) { + var gTabs = [ { + title : 'KPI', + id : 'KPI', + url : this.applicationsHomeUrl, + state : 'root.kpidash_'+activeTab+'_KPI' + }, { + title : 'User Defined KPI', + id : 'User Defined KPI', + url : this.applicationsHomeUrl, + state : 'root.kpidash_'+activeTab+'_UserDefinedKPI' + }, { + title : 'Metrics', + id : 'Metrics', + url : this.applicationsHomeUrl, + state : 'root.kpidash_'+activeTab+'_Metrics' + } ]; + return gTabs; + } + } + + KpiDashboardService.$inject = ['$q', '$log', '$http', 'conf', 'uuid4']; + angular.module('ecompApp').service('KpiDashboardService', KpiDashboardService) +})(); diff --git a/ecomp-portal-FE/client/app/services/manifest/manifest.service.js b/ecomp-portal-FE/client/app/services/manifest/manifest.service.js new file mode 100644 index 00000000..4341a138 --- /dev/null +++ b/ecomp-portal-FE/client/app/services/manifest/manifest.service.js @@ -0,0 +1,64 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +/** + * Created by mlittle on 9/9/2016. + */ +'use strict'; + +(function () { + class ManifestService { + constructor($q, $log, $http, conf, uuid, utilsService) { + this.$q = $q; + this.$log = $log; + this.$http = $http; + this.conf = conf; + this.uuid = uuid; + this.utilsService = utilsService; + } + + getManifest() { + let deferred = this.$q.defer(); + this.$http({ + method: "GET", + url: this.conf.api.getManifest, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then( res => { + if (this.utilsService.isValidJSON(res)== false) { + this.$log.error('ManifestService.getManifest failed: '); + deferred.reject('ManifestService.getManifest: response.data null or not object'); + } else { + // this.$log.info('ManifestService.getManifest Succeeded'); + // this.$log.debug('ManifestService.getManifest: ', JSON.stringify(res)) + deferred.resolve(res.data); + } + }).catch( status => { + this.$log.error('ManifestService.getManifest failed: ' + status.data); + deferred.reject(status); + }); + return deferred.promise; + } + + } + ManifestService.$inject = ['$q', '$log', '$http', 'conf', 'uuid4', 'utilsService']; + angular.module('ecompApp').service('manifestService', ManifestService) +})(); diff --git a/ecomp-portal-FE/client/app/services/menus/menus.service.js b/ecomp-portal-FE/client/app/services/menus/menus.service.js new file mode 100644 index 00000000..19bb385b --- /dev/null +++ b/ecomp-portal-FE/client/app/services/menus/menus.service.js @@ -0,0 +1,145 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; + +(function () { + class MenusService { + constructor($q, $log, $http, conf, uuid) { + this.$q = $q; + this.$log = $log; + this.$http = $http; + this.conf = conf; + this.uuid = uuid; + } + + GetFunctionalMenuForUser() { + let deferred = this.$q.defer(); + // this.$log.info('MenusService::GetFunctionalMenuForUser via REST API'); + this.$http({ + method: "GET", + url: this.conf.api.functionalMenuForAuthUser, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then( res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("MenusService::GetFunctionalMenuForUser Failed"); + } else { + this.$log.info('MenusService::GetFunctionalMenuForUser success:'); + deferred.resolve(res.data); + } + }) + .catch( status => { + this.$log.info('MenusService::rejection:' + status); + deferred.reject(status); + }); + + + return deferred.promise; + } + + getFavoriteItems() { + let deferred = this.$q.defer(); + // this.$log.info('MenusService::getFavoriteItems via REST API'); + this.$http({ + method: "GET", + url: this.conf.api.getFavoriteItems +"?date="+new Date().getTime(), + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then( res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("MenusService::getFavoriteItems Failed"); + } else { + this.$log.info('MenusService::getFavoriteItems success:'); + deferred.resolve(res.data); + } + }) + .catch( status => { + this.$log.error('MenusService::getFavoriteItems rejection:' + status); + deferred.reject(status); + }); + + + return deferred.promise; + } + + setFavoriteItem(menuId) { + let deferred = this.$q.defer(); + // this.$log.info('menus-service.service::setFavoriteItem via REST API' + menuId); + let url = this.conf.api.setFavoriteItem; + this.$http({ + method: "POST", + url: url, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate(), + 'Content-Type': 'application/json' + }, + data: menuId + }).then(res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("MenusService::setFavoriteItem Failed"); + } else { + this.$log.info('MenusService::setFavoriteItem success:'); + deferred.resolve(res.data); + } + }) + .catch(errRes => { + this.$log.error('MenusService::setFavoriteItem rejection:' + JSON.stringify(errRes)); + deferred.reject(errRes); + }); + + return deferred.promise; + } + + removeFavoriteItem(menuId) { + let deferred = this.$q.defer(); + // this.$log.info('menus-service.service::removeFavoriteItem via REST API'); + let url = this.conf.api.removeFavoriteItem.replace(':menuId', menuId); + this.$http({ + method: "DELETE", + url: url, + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("MenusService::removeFavoriteItem Failed"); + } else { + // this.$log.info('MenusService::removeFavoriteItem success:'); + deferred.resolve(res.data); + } + }) + .catch(errRes => { + this.$log.error('MenusService::removeFavoriteItem rejection:' + status); + deferred.reject(errRes); + }); + return deferred.promise; + } + + } + MenusService.$inject = ['$q', '$log', '$http', 'conf', 'uuid4']; + angular.module('ecompApp').service('menusService', MenusService) +})(); diff --git a/ecomp-portal-FE/client/app/services/portal-admins/portal-admins.service.js b/ecomp-portal-FE/client/app/services/portal-admins/portal-admins.service.js new file mode 100644 index 00000000..683c0f02 --- /dev/null +++ b/ecomp-portal-FE/client/app/services/portal-admins/portal-admins.service.js @@ -0,0 +1,107 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; + +(function () { + class PortalAdminsService { + constructor($q, $log, $http, conf, uuid) { + this.$q = $q; + this.$log = $log; + this.$http = $http; + this.conf = conf; + this.uuid = uuid; + } + + getPortalAdmins() { + let deferred = this.$q.defer(); + this.$log.info('PortalAdminsService::get all portal admins list'); + this.$http({ + url: this.conf.api.portalAdmins, + method: 'GET', + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("PortalAdminsService::getPortalAdmins Failed"); + } else { + deferred.resolve(res.data); + } + }) + .catch(status => { + deferred.reject(status); + }); + return deferred.promise; + } + + addPortalAdmin(userData) { + let deferred = this.$q.defer(); + this.$log.info('PortalAdminsService:: add Portal Admin' + JSON.stringify(userData)); + this.$http({ + url: this.conf.api.portalAdmin, + method: 'POST', + cache: false, + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + }, + data: userData + }).then(res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("PortalAdminsService::addPortalAdmin Failed"); + } else { + deferred.resolve(res.data); + } + }) + .catch(errRes => { + deferred.reject(errRes); + }); + return deferred.promise; + } + + removePortalAdmin(userId) { + let deferred = this.$q.defer(); + let url = this.conf.api.portalAdmin + '/' + userId; + this.$log.info('PortalAdminsService:: remove Portal Admin'); + this.$http({ + url: url, + method: 'DELETE', + cache: false, + data: '', + headers: { + 'X-ECOMP-RequestID':this.uuid.generate() + } + }).then(res => { + if (Object.keys(res.data).length == 0) { + deferred.reject("PortalAdminsService::removePortalAdmin Failed"); + } else { + deferred.resolve(res.data); + } + }).catch(errRes => { + deferred.reject(errRes); + }); + + return deferred.promise; + } + } + PortalAdminsService.$inject = ['$q', '$log', '$http', 'conf', 'uuid4']; + angular.module('ecompApp').service('portalAdminsService', PortalAdminsService) +})(); diff --git a/ecomp-portal-FE/client/app/services/support/getAccess/get-access.service.js b/ecomp-portal-FE/client/app/services/support/getAccess/get-access.service.js new file mode 100644 index 00000000..24b6945d --- /dev/null +++ b/ecomp-portal-FE/client/app/services/support/getAccess/get-access.service.js @@ -0,0 +1,61 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; + +(function () { + class GetAccessService { + constructor($q, $log, $http, conf,uuid) { + this.$q = $q; + this.$log = $log; + this.$http = $http; + this.conf = conf; + this.uuid = uuid; + } + getListOfApp(searchStr) { + //this.$log.info('GetAccessService::getListOfApp: get all app list'); + let deferred = this.$q.defer(); + //this.$log.info('GetAccessService::getListOfApp: searchStr', searchStr); + //this.$log.info('GetAccessService::getListOfApp: ', this.conf.api.listOfApp); + this.$http({ + method: "GET", + url: this.conf.api.listOfApp, + params: {search:searchStr}, + cache: false + }).then( res => { + // this.$log.info('GetAccessService::getListOfApp response: ', res); + if (Object.keys(res).length == 0) { + deferred.reject("GetAccessService::getListOfApp: Failed"); + } else { + // this.$log.debug('GetAccessService::getListOfApp: query results: ', res); + // this.$log.info('GetAccessService::getListOfApp Succeeded'); + deferred.resolve(res); + } + }).catch( status => { + this.$log.error('GetAccessService::getListOfApp: query error: ',status); + deferred.reject(status); + }); + return deferred.promise; + } + + } + GetAccessService.$inject = ['$q', '$log', '$http', 'conf','uuid4']; + angular.module('ecompApp').service('getAccessService', GetAccessService) +})(); diff --git a/ecomp-portal-FE/client/app/services/support/session/session.service.js b/ecomp-portal-FE/client/app/services/support/session/session.service.js new file mode 100644 index 00000000..d18c42a4 --- /dev/null +++ b/ecomp-portal-FE/client/app/services/support/session/session.service.js @@ -0,0 +1,56 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; + +(function () { + class SessionService { + constructor($q, $log, $http, conf,uuid,$sce) { + this.$q = $q; + this.$log = $log; + this.$http = $http; + this.conf = conf; + this.uuid = uuid; + this.$sce = $sce; + } + + logout(appStr) { + this.$log.info('SessionService::logout from App'); + let deferred = this.$q.defer(); + this.$log.info('SessionService appStr: ', appStr); + + var eaccessPattern = '\https?\:\/\/[^/]+/[^/]+/[^/]+'; + var standardPattern = '\https?\:\/\/[^/]+/[^/]+'; + + if(appStr.includes("e-access")) { + standardPattern = eaccessPattern; + } + + var contextUrl = appStr.match(new RegExp(standardPattern)); + var logoutUrl = contextUrl + "/logout.htm" ; + this.$sce.trustAsResourceUrl(logoutUrl); + console.log('logoutUrl ' + logoutUrl); + jQuery('#reg-logout-div').append(" + + + + + +
+
X
+ +
+ + + +
+ To request access to an application, please visit the Get Access page. +
+ + diff --git a/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.js b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.js new file mode 100644 index 00000000..b5216101 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.js @@ -0,0 +1,85 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class NewPortalAdminModalCtrl { + constructor($log, portalAdminsService, $scope, confirmBoxService) { + + let init = () => { + this.isSaving = false; + if($scope.ngDialogData && $scope.ngDialogData.selectedUser && $scope.ngDialogData.dialogState){ + this.selectedUser = $scope.ngDialogData.selectedUser; + this.dialogState = $scope.ngDialogData.dialogState; + }else{ + this.selectedUser = null; + this.dialogState = 1; + } + $log.info('NewPortalAdminModalCtrl:: initiated'); + }; + + this.addNewPortalAdmin = () => { + portalAdminsService.getPortalAdmins().then(result=> { + var dupNameCheck = JSON.stringify(result).search(this.selectedUser.orgUserId); + if (dupNameCheck != -1) { + $log.error("NewPortalAdminModalCtrl::addNewPortalAdmin: userId already exists as a portal admin! dupNameCheck=",dupNameCheck); + confirmBoxService.showInformation('This user already exists as a portal admin!').then(function (isConfirmed) { + $scope.closeThisDialog(true); + }); + } else { + confirmBoxService.makeAdminChanges('Are you sure you want to add "' + this.selectedUser.firstName + ' ' + this.selectedUser.lastName + '" as a Portal Admin?') + .then(isConfirmed => { + if(isConfirmed) { + if (!this.selectedUser || !this.selectedUser.orgUserId) { + $log.error('NewPortalAdminModalCtrl::makeAdminChanges: No portal admin or ID... cannot add'); + return; + } + portalAdminsService.addPortalAdmin(this.selectedUser.orgUserId) + .then(() => { + $log.debug("NewPortalAdminModalCtrl::addNewPortalAdmin: portal admin added successfully"); + $scope.closeThisDialog(true); + }).catch(err => { + $log.error('NewPortalAdminModalCtrl::addNewPortalAdmin error status: ' + err.status); + confirmBoxService.showInformation('There was a unknown problem adding the portal admin. ' + 'Please try again later. Error Status: '+ err.status).then(function (isConfirmed) {}); + }); + } + }).catch(err => { + $log.error('portalAdminsService.addPortalAdmin error status: '+ err.status); + }); + } + }).catch(err=> { + $log.error('NewPortalAdminModalCtrl::addNewPortalAdmin error getting portal admins list:',err); + }); + }; + + this.setSelectedUser = (user) => { + $log.debug('NewPortalAdminModalCtrl::setSelectedUser: selected user: ', user); + this.selectedUser = user; + }; + + init(); + + $scope.$on('$stateChangeStart', e => { + e.preventDefault(); + }); + } + } + NewPortalAdminModalCtrl.$inject = ['$log', 'portalAdminsService', '$scope', 'confirmBoxService']; + angular.module('ecompApp').controller('NewPortalAdminModalCtrl', NewPortalAdminModalCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.spec.js b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html new file mode 100644 index 00000000..f235b391 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html @@ -0,0 +1,32 @@ + +
+
+ +
+
Save +
+
Cancel
+
+
+
diff --git a/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.less b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.less new file mode 100644 index 00000000..dcc4fc52 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.less @@ -0,0 +1,99 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .new-portal-admin-modal { + display:block; + overflow:auto; + min-height: 450px; + + .search-users { + } + + .admin-app-roles { + .title { + .n18r; + border-bottom: @a 3px solid; + + } + + .app-roles-main { + margin-top: 16px; + .app-roles-main-title { + .n14r; + margin-bottom: 8px; + .left { + display: inline-block; + } + .right { + display: inline-block; + color: @a; + float: right; + cursor: pointer; + } + } + + .select-input{ + width: 460px; + } + + .new-administrated-app { + height: 30px; + line-height: 30px; + + border: 1px solid @p; + margin-bottom: 8px; + border-radius: 2px; + padding-left: 6px; + padding-top: 0; + .o14r; + } + + .admin-roles-list { + height: 240px; + overflow-y: auto; + } + + .administrated-application { + width: 460px; + height: 30px; + border: 1px solid @p; + margin-bottom: 8px; + border-radius: 2px; + padding: 6px; + .o14r; + display: inline-block; + + } + + .delete-application { + .ico_trash_default; + display: inline-block; + vertical-align: 4px; + cursor: pointer; + position: relative; + top: 6px; + color: transparent; + margin-left: 8px; + } + + } + + } +} + diff --git a/ecomp-portal-FE/client/app/views/portal-admin/portal-admin-controller.js b/ecomp-portal-FE/client/app/views/portal-admin/portal-admin-controller.js new file mode 100644 index 00000000..10043005 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/portal-admin/portal-admin-controller.js @@ -0,0 +1,127 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class PortalAdminsCtrl { + constructor($log, portalAdminsService, ngDialog, confirmBoxService) { + + let updateTableData = () => { + this.isLoadingTable = true; + portalAdminsService.getPortalAdmins().then(result=> { + $log.debug('PortalAdminsCtrl::updateTableData: result: ' + JSON.stringify(result)); + if (!result || !result.length) { + $log.info('PortalAdminsCtrl::updateTableData: no Portal Admins err handling'); + this.portalAdminsTableData = []; + return; + } + this.portalAdminsTableData = result; + }).catch(err=> { + $log.error('PortalAdminsCtrl::updateTableData error :',err); + }).finally(() => { + this.isLoadingTable = false; + }); + }; + + let init = () => { + $log.info('portalAdminsService.getPortalAdmins::initializing...'); + this.isLoadingTable = false; + + this.searchString= ''; + this.portalAdminsTableHeaders = ['First Name', 'Last Name', 'User ID', 'Delete']; + this.portalAdminsTableData = []; + updateTableData(); + }; + + init(); + + this.removePortalAdmin = pAdmin => { + $log.debug('pAdmin = ' + JSON.stringify(pAdmin)); + confirmBoxService.deleteItem(pAdmin.firstName + ' ' + pAdmin.lastName ) + .then(isConfirmed => { + if(isConfirmed){ + if(!pAdmin || !pAdmin.userId){ + $log.error('PortalAdminsCtrl::removePortalAdmin No portal admin or ID... cannot delete'); + return; + } + portalAdminsService.removePortalAdmin(pAdmin.userId).then(() => { + $log.info("PortalAdminsCtrl::removePortalAdmin removed admin"); + init(); + }).catch(err => { + $log.error('PortalAdminsCtrl::removePortalAdmin.deleteItem error: '+ err); + }); + } + }).catch(err => { + $log.error('PortalAdminsCtrl::removePortalAdmin.deleteItem error: '+ err); + }); + }; + + this.openAddNewPortalAdminModal = (user) => { + let data = null; + if(user){ + data = { + dialogState: 2, + selectedUser:{ + userId: user.orgUserId, + firstName: user.firstName, + lastName: user.lastName + } + } + } + ngDialog.open({ + templateUrl: 'app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html', + controller: 'NewPortalAdminModalCtrl', + controllerAs: 'newPortalAdmin', + data: data + }).closePromise.then(needUpdate => { + if(needUpdate.value === true){ + $log.debug('PortalAdminsCtrl::openAddNewPortalAdminModal: updating Portal Admin table data...'); + updateTableData(); + } + }); + }; + + this.openEditUserModal = (loginId) => { + var data = { + loginId : loginId, + updateRemoteApp : false, + appId : this.selectedApp!=null?this.selectedApp.id:'' + } + var modalInstance = ngDialog.open({ + templateUrl: 'app/views/header/user-edit/edit-user.tpl.html', + controller: 'editUserController', + data: data, + resolve: { + message: function message() { + var message = { + type: 'Contact', + }; + return message; + } + } + }).closePromise.then(needUpdate => { + updateTableData(); + }); + } + + } + } + PortalAdminsCtrl.$inject = ['$log', 'portalAdminsService', 'ngDialog', 'confirmBoxService']; + angular.module('ecompApp').controller('PortalAdminsCtrl', PortalAdminsCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/portal-admin/portal-admin.tpl.html b/ecomp-portal-FE/client/app/views/portal-admin/portal-admin.tpl.html new file mode 100644 index 00000000..05216f34 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/portal-admin/portal-admin.tpl.html @@ -0,0 +1,73 @@ + +
+
+
+
Portal Admins
+
+
+ +
Add Portal Admin
+
+ +
+ + + + + + + + + + + + + + + + + +
{{portalAdmin.portalAdminsTableHeaders[0]}}{{portalAdmin.portalAdminsTableHeaders[1]}}{{portalAdmin.portalAdminsTableHeaders[2]}}{{portalAdmin.portalAdminsTableHeaders[3]}}
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
diff --git a/ecomp-portal-FE/client/app/views/portal-admin/portal-admins.less b/ecomp-portal-FE/client/app/views/portal-admin/portal-admins.less new file mode 100644 index 00000000..29fd9e32 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/portal-admin/portal-admins.less @@ -0,0 +1,56 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .portal-admins-page-main { + .bg_w; + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + overflow-y: @page-main-overflow-y; + padding-left: @padding-left-side; + + + .portal-admins-table { + width: @table-width; + margin: @table-margin; + + .table-control { + .table-dropdown-filter{ + width: @table-dropdown-filter-width; + display: @table-dropdown-filter-display; + } + } + + .table-body { + cursor: pointer; + } + } + + .delete-user{ + .ico_trash_default; + } + + .portal-add-button { + width: 160px; + } +} + diff --git a/ecomp-portal-FE/client/app/views/search/search.controller.js b/ecomp-portal-FE/client/app/views/search/search.controller.js new file mode 100644 index 00000000..60e27593 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/search/search.controller.js @@ -0,0 +1,184 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + + class SearchCtrl { + constructor($log, $scope, $cookies, $timeout, userProfileService, sessionService, dashboardService) { + $scope.firstName=""; + $scope.lastName=""; + + function showHideSearchSnippet() { + + setTimeout(function() { + jQuery("#mainSearchSnippet").click(); + },500); + + setTimeout(function() { + jQuery("#mainSearchText").focus(); + },1000); + } + + jQuery("#mainSearchDiv").keyup(function(event){ + if(event.keyCode == 13){ + // there is a watch on this variable which will trigger the database pull + dashboardService.searchString = jQuery("#mainSearchText").val(); + + // opens the popup + var popupDomObj = jQuery("[content='searchSnippet.html']"); + if(popupDomObj.length == 0) { + showHideSearchSnippet(); + } else { + jQuery("#mainSearchSnippet").click(); + showHideSearchSnippet(); + } + + + + } + }); + + + + } + + + } + + + class SearchSnippetCtrl { + constructor($log, $scope, $cookies, $timeout, userProfileService, sessionService, dashboardService,applicationsService, $window, $state) { + $scope.firstName=""; + $scope.lastName=""; + $scope.goToUrl = goToUrl; + $scope.dService = dashboardService; + + + + function goToUrl (item, type) { + $log.info("goToUrl called") + $log.info(item + "/" + type); + + + if(type == 'intra') { + + var intraSearcLink = "http://to_do_link.com"; + var intraSpecSearcLink = intraSearcLink + encodeURIComponent(dashboardService.searchString); + $window.open(intraSpecSearcLink, '_blank'); + + } else if (type == 'extra') { + var extraSearcLink = "https://to_do_link.com"; + var extraSpecSearcLink = extraSearcLink + encodeURIComponent(dashboardService.searchString); + $window.open(extraSpecSearcLink, '_blank'); + } + + let url = item.target; + let restrictedApp = item.uuid; + let getAccessState = "root.getAccess" + if (!url) { + + applicationsService.goGetAccessAppName = item.name; + if($state.current.name == getAccessState) + $state.reload(); + else + $state.go(getAccessState); + //$log.info('No url found for this application, doing nothing..'); + return; + } + + if (restrictedApp != "true") { + $window.open(url, '_blank'); + } else { + if(item.url=="root.access"){ + $state.go(url); + var tabContent = { id: new Date(), title: 'Home', url: url }; + $cookies.putObject('addTab', tabContent ); + } else { + var tabContent = { id: new Date(), title: item.name, url: url }; + $cookies.putObject('addTab', tabContent ); + } + } + + } + + function getItems(searchString) { + + var items; + var itemMap = dashboardService.getSearchAllByStringResults(searchString) + .then(res => { + $scope.items = res; + + + }).catch(err => { + $scope.items = []; + $log.error('Couldnt get search results...', err) + }); + + } + + $scope.$watch('dService.searchString', function(searchString) { + if(searchString != undefined ) + getItems(searchString); + + }); + + + + } + + + + } + + + + SearchCtrl.$inject = ['$log', '$scope', '$cookies', '$timeout','userProfileService', 'sessionService', 'dashboardService']; + SearchSnippetCtrl.$inject = ['$log', '$scope', '$cookies', '$timeout','userProfileService', 'sessionService', 'dashboardService','applicationsService', '$window','$state']; + angular.module('ecompApp').controller('searchCtrl', SearchCtrl); + angular.module('ecompApp').controller('searchSnippetCtrl', SearchSnippetCtrl); + angular.module('ecompApp').directive('searchBox', function() { + return { + restrict: "E", + templateUrl: 'app/views/search/search.tpl.html', + link: function(scope, element) { + + //var iframeId = "#tabframe-" + scope.$parent.tab.title.split(' ').join('-'); + // jQuery(iframeId).load(function() { + // alert("hello"); + // }); //.attr("src",'{{tab.content | trusted}}' ); //src='{{tab.content | trusted}}' + // jQuery(iframeId).attr('src', '{{tab.content | trusted}}'); + + //element.childNodes[0].on('load', function() { + // alert('hello'); + //}); + } + } + }); + angular.module( 'ecompApp' ).config( [ + '$compileProvider', + function( $compileProvider ) + { + $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension|qto):/); + } + ]); + +})(); + + diff --git a/ecomp-portal-FE/client/app/views/search/search.controller.spec.js b/ecomp-portal-FE/client/app/views/search/search.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/search/search.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/search/search.less b/ecomp-portal-FE/client/app/views/search/search.less new file mode 100644 index 00000000..a21559ec --- /dev/null +++ b/ecomp-portal-FE/client/app/views/search/search.less @@ -0,0 +1,55 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .searchLiHeader { +font-weight: bold; +color: #0574ac; +font-size: 16px; +padding-bottom: 10px; +line-height: 1.5; +font-family: arial; + +} + +.searchLiItems{ +cursor: pointer; +font-weight: normal; +font-size: 12px; +color: #444444; +font-family: arial; +} + +.searchUl { +list-style: none; +border-bottom: 1px solid #bbb; +padding-bottom: 20px; +} + +#contentVertical { + height: 300px; + width: 250px; + } + +#contentVertical .scroll-viewport { + height: 300px; + width: 250px; + } +#mainSearchText::-ms-clear{ + display:none; +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/search/search.tpl.html b/ecomp-portal-FE/client/app/views/search/search.tpl.html new file mode 100644 index 00000000..bd37f772 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/search/search.tpl.html @@ -0,0 +1,82 @@ + + +
+
+ +   + +
+
+
+ +
+ + + + + diff --git a/ecomp-portal-FE/client/app/views/sidebar/sidebar.controller.js b/ecomp-portal-FE/client/app/views/sidebar/sidebar.controller.js new file mode 100644 index 00000000..76d5301c --- /dev/null +++ b/ecomp-portal-FE/client/app/views/sidebar/sidebar.controller.js @@ -0,0 +1,143 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class SidebarCtrl { + constructor(userProfileService, $log, $rootScope) { + this.$log = $log; + this.userProfileService = userProfileService; + this.$rootScope = $rootScope; + $rootScope.isAdminPortalAdmin = false; + + //if (bowser.msie || bowser.msedge) + // $log.debug('SidebarCtrl::init: Browser is: Internet Explorer or Edge'); + // else + // $log.debug('SidebarCtrl::init: Browser is: ' + bowser.name + ': ' + bowser.version); + + userProfileService.getUserProfile() + .then(profile=> { + // $log.debug('SidebarCtrl::getUserProfile: profile.roles.indexOf(superAdmin) = ' + profile.roles.indexOf('superAdmin')); + // $log.debug('SidebarCtrl::getUserProfile: profile.roles.indexOf(admin) = ' + profile.roles.indexOf('admin')); + if (profile.roles.indexOf('superAdmin') > -1) { + $rootScope.isAdminPortalAdmin = true; + // this.$log.debug('SidebarCtrl::getUserProfile: user has the superAdmin role'); + this.sidebarModel = { + label: 'ECOMP portal', + navItems: [ + { + name: 'Home', + state: 'root.applicationsHome', + imageSrc : 'ion-home' + }, { + name: 'Application Catalog', + state: 'root.appCatalog', + imageSrc : 'ion-grid' + }, { + name: 'Widgets', + state: 'root.widgetsHome', + imageSrc : 'ion-android-apps' + }, { + name: 'Admins', + state: 'root.admins', + imageSrc : 'ion-android-star' + }, { + name: 'Users', + state: 'root.users', + imageSrc : 'ion-person' + },{ + name: 'Portal Admins', + state: 'root.portalAdmins', + imageSrc : 'ion-gear-a' + }, { + name: 'Application Onboarding', + state: 'root.applications', + imageSrc : 'ion-ios-upload-outline' + }, { + name: 'Widget Onboarding', + state: 'root.widgets', + imageSrc : 'ion-ios-upload-outline' + },{ + name: 'Edit Functional Menu', + state: 'root.functionalMenu', + imageSrc : 'ion-compose' + } + ] + }; + } + else if (profile.roles.indexOf('admin') > -1) { + // this.$log.debug('SidebarCtrl::getUserProfile: user has the admin role'); + this.sidebarModel = { + label: 'ECOMP portal', + navItems: [ + { + name: 'Home', + state: 'root.applicationsHome', + imageSrc : 'ion-home' + }, { + name: 'Application Catalog', + state: 'root.appCatalog', + imageSrc : 'ion-grid' + }, { + name: 'Widgets', + state: 'root.widgetsHome', + imageSrc : 'ion-grid' + }, { + name: 'Users', + state: 'root.users', + imageSrc : 'ion-person' + }, { + name: 'Widget Onboarding', + state: 'root.widgets', + imageSrc : 'ion-grid' + } + ] + }; + } + else { + $rootScope.isAdminPortalAdmin = false; + // this.$log.debug('SidebarCtrl::getUserProfile: user is not superAdmin nor admin'); + this.sidebarModel = { + label: 'ECOMP portal', + navItems: [ + { + name: 'Home', + state: 'root.applicationsHome', + imageSrc : 'ion-home' + }, { + name: 'Application Catalog', + state: 'root.appCatalog', + imageSrc : 'ion-grid' + }, { + name: 'Widgets', + state: 'root.widgetsHome', + imageSrc : 'ion-grid' + } + ] + }; + } + }).catch(err=> { + $log.error('SidebarCtrl: failed to get user profile: ' + err); + }); + + } + } + SidebarCtrl.$inject = ['userProfileService', '$log', '$rootScope']; + angular.module('ecompApp').controller('SidebarCtrl', SidebarCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/sidebar/sidebar.less b/ecomp-portal-FE/client/app/views/sidebar/sidebar.less new file mode 100644 index 00000000..97650fed --- /dev/null +++ b/ecomp-portal-FE/client/app/views/sidebar/sidebar.less @@ -0,0 +1,37 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .w-ecomp-sidebar { + position: relative; + left: 0; + right: 0; + top: 5px; + padding-left: 0; +} + + @media screen and (-webkit-min-device-pixel-ratio:0) +{ + .w-ecomp-sidebar { + position: relative; + left: 0; + right: 0; + top: -5px; + padding-left: 0; + } +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/sidebar/sidebar.tpl.html b/ecomp-portal-FE/client/app/views/sidebar/sidebar.tpl.html new file mode 100644 index 00000000..a5cbf8ff --- /dev/null +++ b/ecomp-portal-FE/client/app/views/sidebar/sidebar.tpl.html @@ -0,0 +1,20 @@ + + diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.js b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.js new file mode 100644 index 00000000..368f2fe3 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.js @@ -0,0 +1,194 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +(function () { + class ContactUsManageController { + constructor($scope,$log, message, $q, $http, conf,contactUsService,confirmBoxService) { + $scope.contactUsList=[]; + $scope.contactUsAllAppList=[]; + $scope.errMsg=''; + $scope.newContactUs ={ + app:'', + name:'', + email:'', + url:'', + desc:'' + }; + /*Get the existing contact us first, then based on the existing, filter from all apps*/ + $scope.getContactUsList = function(){ + contactUsService.getContactUs().then(res=> { + // $log.info('getting getContactUs',res.response); + if(res!=null && res.response!=null){ + for(var i=0; i { + var tableData=[]; + $scope.contactUsAllAppListTemp=[]; + // $log.info('contactUsService::getListOfApp: getting res'); + var result = (typeof(res.data) != "undefined" && res.data!=null)?res.data:null; + // $log.info('contactUsService::getListOfApp: result',result); + var res1 = result; + var realAppIndex = 0; + $scope.contactUsAllAppList.length=0; + for (var i = 1; i <= res1.length; i++) { + if (!res1[i - 1].restrictedApp) { + var okToAdd = true; + for(var j =0; j<$scope.contactUsList.length;j++){ + if(res1[i - 1].title==$scope.contactUsList[j].appName) + okToAdd=false; + } + // not allowed to add(duplicate) another entry if the app is already available in the table + if(okToAdd){ + if(res1[i - 1].title){ + $scope.contactUsAllAppList.push({ + index: realAppIndex, + title: res1[i - 1].title, + value: res1[i - 1].index + }); + } + realAppIndex = realAppIndex + 1; + } + } else { + // $log.debug('contactUsService:getAvailableApps:: Restricted/URL only App will not be used = ' + res1[i - 1].title); + } + } + }).catch(err=> { + $log.error('contactUsService:error:: ', err); + + + }).finally(() => { + //this.isLoadingTable = false; + }); + }); + } + + $scope.getContactUsList(); + + $scope.closeDialog = function(){ + $scope.closeThisDialog( $scope.widgetData); + } + + /*Add new Contact Us*/ + $scope.newContactUsFun = function(){ + if($scope.newContactUs.app.title==null || $scope.newContactUs.app.title=='' ){ + confirmBoxService.showInformation('Please select an App to add').then(isConfirmed => { + return; + }); + } + + if($scope.newContactUs.url !=null && $scope.newContactUs.url != '' && !validateUrl($scope.newContactUs.url)){ + var warningMsg = "Please enter a valid URL"; + confirmBoxService.showInformation(warningMsg).then(isConfirmed => {return;}); + return; + } + + contactUsService.addContactUs($scope.newContactUs).then(res=> { + // $log.info('contactUsService: add ContactUs successfully'); + $scope.contactUsList.length=0; + // $log.info('contactUsService: refreshing the Contact US table'); + $scope.getContactUsList(); + $scope.errMsg=''; + /* confirmBoxService.showInformation('You have added a new Contact Us item').then(isConfirmed => { });*/ + var defaultSel={ + index: 0, + title: '', + value: '' + } + $scope.newContactUs ={ + app:defaultSel, + name:'', + email:'', + url:'', + desc:'' + }; + + }).catch(err=> { + $log.error('contactUsService: addContactUs error:: ', err); + // $scope.errMsg=err; + confirmBoxService.showInformation('Add Contact Us list failed: ' + err); + + }).finally(() => { + //this.isLoadingTable = false; + }); + } + /* Edit Contact Us*/ + $scope.editContactUsFun = function(contactObj){ + // $log.info('contactUsService: edit ContactUs save successfully', contactObj); + var contactUsObj={ + appId:contactObj.appId, + appName:contactObj.appName, + description:contactObj.description, + contactName:contactObj.contactName, + contactEmail:contactObj.contactEmail, + url:contactObj.url, + }; + + contactUsService.modifyContactUs(contactUsObj).then(res=> { + // $log.info('contactUsService: edit ContactUs successfully'); + // confirmBoxService.showInformation('You have saved the changes').then(isConfirmed => {}); + $scope.errMsg=''; + + }).catch(err=> { + $log.error('contactUsService: editContactUs error:: ', err); + confirmBoxService.showInformation('Edit Contact Us list failed: ' + err); + // $scope.errMsg=err; + }).finally(() => { + //this.isLoadingTable = false; + }); + + } + /*del Contact Us*/ + $scope.delContactUsFun = function(appObj){ + var confirmMsg = 'Are you sure you want to delete '+appObj.appName +' from the list?' + ' Press OK to delete.'; + confirmBoxService.confirm(confirmMsg).then(function (confirmed) { + if (confirmed == true) { + contactUsService.removeContactUs(appObj.appId).then(res=> { + // $log.info('delContactUsFun: delete ContactUs successfully',res); + $scope.errMsg=''; + $scope.contactUsList.length=0; + $scope.getContactUsList(); + confirmBoxService.showInformation('Item has been deleted').then(isConfirmed => {}); + }).catch(err=> { + $log.error('contactUsService: addContactUs error:: ', err); + confirmBoxService.showInformation('Deletion failed: ' + err); + // $scope.errMsg=err; + }).finally(() => { + //this.isLoadingTable = false; + }); + } + }); + + } + + } + } + ContactUsManageController.$inject = ['$scope','$log', 'message', '$q', '$http', 'conf','contactUsService','confirmBoxService']; + angular.module('ecompApp').controller('ContactUsManageController', ContactUsManageController); + + +})(); +function validateUrl(value){ + return /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value); + } diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.less b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.less new file mode 100644 index 00000000..ef8c88df --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.less @@ -0,0 +1,159 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .searchLiHeader { +font-weight: bold; +color: #0574ac; +font-size: 16px; +padding-bottom: 10px; +line-height: 1.5; +font-family: arial; + +} + +.searchLiItems{ +cursor: pointer; +font-weight: normal; +font-size: 12px; +color: #444444; +font-family: arial; +} + +.searchUl { +list-style: none; +border-bottom: 1px solid #bbb; +padding-bottom: 20px; +} + +.manage-contactUs-home-title{ + color: #067ab4; + font-family: arial; + font-size: 24px; + margin-left: 78px; + width: 1170px; + } + +.manage-contactUs-home-manageWidgets-title{ + color: #067ab4; + font-family: arial; + font-size: 24px; + margin-left: 30px; + width: 1170px; + } + +#editWidgetsContent { + height: 300px; + width: 1250px; + } + +#editWidgetsContent .scroll-viewport { + height: 300px; + width: 1250px; + } + + .add-contact-us-field{ + width:250px; + display: inline-block; + margin:10px; + } + .add-contact-us-field-des{ + width:800px; + margin:10px; + } + +.errMsg{ + color:red; +} +.sucessMsg{ + color:green; +} +.contact-us-margin{ + margin-left: 80px; + margin-right: 120px; +} +.contact-us-manage-table { + + + .delete-contact-us{ + font-size:20px; + } + + .portal-add-button { + width: 160px; + } +} + +.select2-container .select2-chosen, .select2-container .select2-container input{ + line-height:39px; +} +.select2-container .select2-choice{ + -webkit-transition: border-color .2s ease-out; + -moz-transition: border-color .2s ease-out; + transition: border-color .2s ease-out; + background: #fff; + border-radius: 6px; + display: block; + border: 1px solid #888; + box-shadow: none; + filter: none; + height: 32px !important; + padding: 0; + position: relative; + z-index: 5; + cursor: pointer; +} + +.c-ecomp-att-abs-table .contactUs{ + + height:300px; + +} + +.ngdialog-overlay { + pointer-events: none !important; +} + +.input-text-area { + font-style: italic; + padding: 7px 10px; + width: 250px; + display: inline-block; + position: relative; + margin-bottom: 10px; + border-radius: 6px; + border: 1px solid #d8d8d8; + height: 32px !important; + border-color: slategrey !important; +} +.input-inline-edit-text { + font-style: italic; + padding: 7px 10px; + display: inline-block; + position: relative; + margin-bottom: 10px; + border-radius: 6px; + border: 1px solid #d8d8d8; + height: 32px; + border-color: slategrey !important; + width:100%; + } +.glyphicon { + position: relative !important; + line-height: 0 !important; +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.html b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.html new file mode 100644 index 00000000..257226a0 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.html @@ -0,0 +1,136 @@ + +
+ +
+
+ +
+
Manage Contact Us
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
App NameContact NameContact EmailContact URLDescriptionEditDelete
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + Save + +
+
+ +
+
+
+ +
+
+
+

Add Application Contact Information

+ +
+
+
+
+
App Name
+
+
+
+
+
Contact Name
+ +
+
+
Contact Email
+ +
+
+
Contact URL
+ +
+
+
Description
+ +
+
+
+ Add New +
+ +
+ +
+ Close +
+ +
+ +
+ +
+
+
+
+ + \ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.js b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.js new file mode 100644 index 00000000..63e38a48 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.js @@ -0,0 +1,166 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +(function () { + + class ContactUsCtrl { + constructor($log, contactUsService, applicationsService, $modal, ngDialog, $state) { + + contactUsService.getContactUSPortalDetails().then(res => { + // $log.info('ContactUsCtrl:: contactUsService getContactUSPortalDetails res',res); + // $log.info('getting res',res); + var result = (typeof(res.response) != "undefined" && res.response!=null)?res.response:null; + // $log.info('result',result); + // $log.info('done'); + var source = JSON.parse(result); + // $log.info(source); + this.ush_TicketInfoUrl = source.ush_ticket_url; + this.portalInfo_Address = source.feedback_email_address; + this.feedback_Url = source.portal_info_url; + }).catch(err=> { + $log.error('ContactUsCtrl:error:: ', err); + }).finally(() => { + }); + + let init = () => { + // $log.info('ecomp app::contact-us-controller::initializing...'); + this.appTable=[]; + this.functionalTableData=[]; + }; + init(); + + let updateContactUsTable = () => { + contactUsService.getAppsAndContacts().then(res=> { + // $log.info('ContactUsCtrl:: contactUsService getAppsAndContacts res',res); + var tableData=[]; + // $log.info('getting res',res); + var result = (typeof(res.response) != "undefined" && res.response!=null)?res.response:null; + // $log.info('result',result); + // $log.info('done'); + var source = result; + // $log.info(source); + // Drop Portal app, empty name entries + for(var i=0;i { + $log.error('ContactUsCtrl.updateContactUsTable:error:: ', err); + }) + }; + + contactUsService.getAppCategoryFunctions().then(res=> { + // $log.info('ContactUsCtrl:: contactUsService getAppCategoryFunctionsthen res',res); + var tablefunctionalData=[]; + // $log.info('getting res',res); + var result = (typeof(res.response) != "undefined" && res.response!=null)?res.response:null; + // $log.info('result',result); + // $log.info('done'); + var source = result; + // $log.info(source); + for(var i=0;i { + $log.error('ContactUsCtrl:error:: ', err); + }) + + updateContactUsTable(); + this.editContactUsModalPopup = () => { + // $log.debug('ContactUsCtrl::editContactUsModalPopup updating table data...'); + var modalInstance = ngDialog.open({ + templateUrl: 'app/views/support/contact-us/contact-us-manage/contact-us-manage.html', + controller: 'ContactUsManageController', + resolve: { + message: function message() { + var message = { + type: 'Contact', + }; + return message; + } + } + }).closePromise.then(needUpdate => { + updateContactUsTable(); + }); + }; + + this.goToSection = (id) => { + var targetDiv = document.getElementById(id); + var offSetHeight = 0; + for(var i=0;i d.showFlag = false); + // let index = this.appTable.findIndex(a => a.app_Id == id); + var index =-1; + for(var i=0; i -1) { + // setting the showFlag to true based on index comparing with the app_Id + this.appTable[index].showFlag = true; + $('#contentId').animate({ + scrollTop: targetDiv.offsetTop-offSetHeight + 'px' + }, 'fast'); + } + + }; + + // Take the user to the application on the get access page. + this.goGetAccess = (appName) => { + // $log.debug('ContactUsCtrl::goGetAccess received name ' + appName); + applicationsService.goGetAccessAppName = appName; + $state.go('root.getAccess'); + }; + + } + } + ContactUsCtrl.$inject = ['$log','contactUsService', 'applicationsService', '$modal', 'ngDialog', '$state']; + angular.module('ecompApp').controller('ContactUsCtrl', ContactUsCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.spec.js b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.less b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.less new file mode 100644 index 00000000..b0c0af4f --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.less @@ -0,0 +1,104 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .w-ecomp-contactUs-home { + .bg_w; + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + padding-left: @padding-left-side; + + .contactUs-home-container { + .content_justify; + position: relative; + //padding-top: 15px; + padding-right: 0; + padding-left: 0; + padding-bottom: 32px; + width: 100%; + + .contactUs-home-title { + .a24r; + margin: auto; + .content_justify; + } + + .contactUs-general-div { + margin: auto; + padding-top: 15px; + padding-bottom: 15px; + .content_justify; + } + } + +.collapsibleArrow { + width: 30px; + height: 30px; + margin-left: 10px; + } + +.contactUs-collapsible-panel { + margin: auto; + width: 1170px; + + } + + .contactUs-collapsible-table { + margin: auto; + width: 1170px; + } + + .contactUs-txt { + font-weight: normal; + color: #666666; + font-size: 15px; + font-family: arial; + margin:10px; + } + +.contactUs-panel-header { + font-weight: normal; + color: #666666; + font-size: 18px; + font-family: sans-serif; + border-top: 1px solid #ccc; + outline : none; + height: 35px; + width: 1050px; + } + + .contactUs-panel-header:hover{ + color: #428bca; + background: #eae8e8; + cursor: pointer; cursor: hand; + } + +.contactUs-panel-labels { + font-weight: normal; + color: #666666; + font-size: 15px; + font-family: sans-serif; + padding-top: 5px; + padding-bottom: 5px; +} + +} diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.tpl.html b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.tpl.html new file mode 100644 index 00000000..0b403ece --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.tpl.html @@ -0,0 +1,125 @@ + +
+
+
+
Contact Us + +
+ +
+ To give feedback or report an issue to the OpenECOMP Portal team, send email to {{contact.portalInfo_Address}}. +
+ +
+ +
Application Functions +
+
+ + + + + + + + + + + + + + + + + +
CategoryECOMP FunctionsECOMP Application
+
+
+
+
+
+ {{rowData.app_Name}} +
+
+
+
+
+ +
+
All Applications +
+
+
+ + + + + + +
+ +
+
+ No application information is available. Please use the links above to contact the OpenECOMP Portal team. +
+
+ + + + + + + + + + + + + + + + + + + + + +
Contact:{{rowData.contact_name}}
Email:{{rowData.contact_email}}
Info URL:{{rowData.url_Info}}
Description:{{rowData.desc}}
Get access:Click for application and role information
+
+
+ +
+
+ +
+
+
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.js b/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.js new file mode 100644 index 00000000..b9b254ac --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.js @@ -0,0 +1,70 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class GetAccessCtrl { + constructor($log, $stateParams, getAccessService,applicationsService, ngDialog) { + // $log.debug('GetAccessCtrl: appService param is: ' + applicationsService.goGetAccessAppName); + + this.updateAppsList = () => { + getAccessService.getListOfApp().then(res=> { + var tableData=[]; + // $log.info('GetAccessCtrl::updateAppsList: getting res'); + var result = (typeof(res.data) != "undefined" && res.data!=null)?res.data:null; + // $log.info('GetAccessCtrl::updateAppsList: result',result); + // $log.info('GetAccessCtrl::updateAppsList: done'); + var source = result; + // $log.info('GetAccessCtrl::updateAppsList source: ', source); + for(var i=0;i { + $log.error('GetAccessCtrl:error:: ', err); + }).finally(() => { + this.isLoadingTable = false; + }); + }; + let init = () => { + // $log.info('GetAccessCtrl:: initializing...'); + this.searchString = ''; + this.getAccessTableHeaders = ['ECOMP Function', 'Application Name', 'Role Name']; + this.appTable=[]; + this.updateAppsList(); + }; + init(); + } + } + GetAccessCtrl.$inject = ['$log', '$stateParams', 'getAccessService', 'applicationsService', 'ngDialog']; + angular.module('ecompApp').controller('GetAccessCtrl', GetAccessCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.spec.js b/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/support/get-access/get-access.less b/ecomp-portal-FE/client/app/views/support/get-access/get-access.less new file mode 100644 index 00000000..20b406e9 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/get-access/get-access.less @@ -0,0 +1,67 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .w-ecomp-get-access-home { + .bg_w; + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + padding-left: @padding-left-side; + + .get-access-home-container { + .content_justify; + position: relative; + padding-right: 0; + padding-left: 0; + padding-bottom: 32px; + width: 100%; + + .get-access-home-title { + .a24r; + margin: auto; + .content_justify; + } + + .get-access-general-div { + margin: auto; + padding-top: 15px; + padding-bottom: 15px; + .content_justify; + } + } + + .get-access-table { + width: @table-width; + margin: @table-margin; + + .table-control { + .table-dropdown-filter { + width: @table-dropdown-filter-width; + display: @table-dropdown-filter-display; + } + } + + .table-body { + cursor: pointer; + } + } +} diff --git a/ecomp-portal-FE/client/app/views/support/get-access/get-access.tpl.html b/ecomp-portal-FE/client/app/views/support/get-access/get-access.tpl.html new file mode 100644 index 00000000..3ed92f14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/get-access/get-access.tpl.html @@ -0,0 +1,88 @@ + +
+
+
+
Get Access
+
+ + Request access for following functions by sending email to the Application's Admin. For Admin contact details, please click here. + +
+
+
+ + +
+ + + + + + + + + + + + + + + + + +
{{access.getAccessTableHeaders[0]}}{{access.getAccessTableHeaders[1]}}{{access.getAccessTableHeaders[2]}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ diff --git a/ecomp-portal-FE/client/app/views/tabs/tabframe.html b/ecomp-portal-FE/client/app/views/tabs/tabframe.html new file mode 100644 index 00000000..bc428832 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/tabs/tabframe.html @@ -0,0 +1,22 @@ + + diff --git a/ecomp-portal-FE/client/app/views/tabs/tabs.controller.js b/ecomp-portal-FE/client/app/views/tabs/tabs.controller.js new file mode 100644 index 00000000..0c970f0e --- /dev/null +++ b/ecomp-portal-FE/client/app/views/tabs/tabs.controller.js @@ -0,0 +1,231 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + const HTTP_PROTOCOL_RGX = /https?:\/\//; + class TabsCtrl { + constructor(applicationsService, $log, $window, conf, userProfileService, $scope,$cookies,$rootScope,confirmBoxService) { + + var counter = 1; + var tabLimit = 6; + this.conf = conf; + var cookieDomain = this.conf.cookieDomain; + $scope.tabs = []; + $scope.notificationShow=true; + $rootScope.showFooter = ""; + console.log("****************** get cookie domain from config json file " + this.conf.cookieDomain); + $cookies.putObject('show_app_header', false,{domain: cookieDomain, path : '/'}); + + + let noRefresh = function () { + window.onbeforeunload = function(e) { + + var isQtoHref = false; + try{ + isQtoHref = e.srcElement.activeElement.href.includes("mailto"); + } catch(err) { + + } + + if ($scope.tabs.length > 1 && isQtoHref == false) { + return "Changes you made may not be saved. Are you sure you want to refresh?"; + } else { + return null; + } + } + } + var addTab = function (title, content) { + if($scope.tabs.length==tabLimit){ + confirmBoxService.showInformation('You have reached your maximum limit of tabs allowed.').then(isConfirmed => {}); + } else { + if(title!=='Home' && content.indexOf('https') == -1){ + console.log('App URL: '+content+'. The application URL you are trying to open is not HTTPS. We recommend to use secured HTTPS URL while on-boarding the application.'); + } + + $scope.tabs.push({ title: title, content: content }); + counter++; + $scope.selectedIndex = $scope.tabs.length - 1; + if ($scope.tabs.length > 1) { + noRefresh(); + } + $cookies.putObject('cookieTabs', $scope.tabs,{domain: cookieDomain, path : '/'}); + $cookies.putObject('visInVisCookieTabs', $scope.tabs,{domain: cookieDomain, path : '/'}); + } + }; + + var adjustTitle = function (title) { + var index = 15; + var nonEmptyCharPattern = /(\s|\w)/; + var adjustedTitle = title.substring(0,index); + var ext = title.charAt(index).replace(nonEmptyCharPattern,'...'); + return adjustedTitle.concat(ext); + + + }; + + var removeTab = function (event, index) { + event.preventDefault(); + event.stopPropagation(); + $scope.tabs.splice(index, 1); + $cookies.putObject('cookieTabs', $scope.tabs,{domain: cookieDomain, path : '/'}); + }; + + var selectTab = function (title) { + if(title=='Home') { + $rootScope.ContentModel.IsVisible=true; + $rootScope.showFooter = true; + $rootScope.tabBottom = 75; + } + else { + $rootScope.ContentModel.IsVisible=false; + $rootScope.showFooter = false; + $rootScope.tabBottom = 0; + } + }; + + $scope.addTab = addTab; + $scope.removeTab = removeTab; + $scope.selectTab = selectTab; + $scope.adjustTitle = adjustTitle; + + + $rootScope.ContentModel = { + IsVisible : false, + ViewUrl : null, + }; + + + var sessionActive = applicationsService.ping() + .then(sessionActive => { + $log.debug('TabsCtrl::addTab: applicationsService.ping() = ' + JSON.stringify(sessionActive)); + + var cookieTabs = $cookies.getObject('cookieTabs'); + if(cookieTabs!=null){ + for(var t in cookieTabs){ + console.log('TabsCtrl::addTab: cookieTabs title: '+cookieTabs[t].title); + if(cookieTabs[t].title!=null && cookieTabs[t].title==='Home'){ + cookieTabs[t].content = ""; + $rootScope.ContentModel.IsVisible=true; + } + + addTab( cookieTabs[t].title, cookieTabs[t].content) ; + } + } else { + for (var i = 0; i < 1; i++) { + var content=""; + var title=""; + if(i==0){ + title="Home"; + $rootScope.ContentModel.IsVisible=true; + } + addTab(title, content); + } + } + + $scope.selectedIndex = 0; + }); + + $scope.$watchCollection(function() { return $cookies.getObject('addTab'); }, function(newValue) { + var tabContent = $cookies.getObject('addTab'); + if(tabContent!=null && tabContent.url!=null){ + var tabExists = false; + for(var x in $scope.tabs){ + console.log($scope.tabs[x].content); + if($scope.tabs[x].title==tabContent.title){ + tabExists = true; + $scope.selectedIndex = x; + } + } + if(!tabExists){ + addTab( tabContent.title, tabContent.url) ; + } + $cookies.remove('addTab'); + } + }); + + + } + + + } + + TabsCtrl.$inject = ['applicationsService', '$log', '$window', 'conf','userProfileService', '$scope','$cookies','$rootScope','confirmBoxService']; + angular.module('ecompApp').controller('TabsCtrl', TabsCtrl); + + angular.module('ecompApp').directive('mainArea', function() { + return { + restrict: "E", + templateUrl: 'app/views/tabs/tabframe.html', + link: function(scope, element) { + + } + } + }); + + + + angular.module('ecompApp').directive('tabHighlight', [function () { + return { + restrict: 'A', + link: function (scope, element) { + var x, y, initial_background = '#c3d5e6'; + + element + .removeAttr('style') + .mousemove(function (e) { + if(!element.hasClass('md-active')) + { + x = e.pageX - this.offsetLeft; + y = e.pageY - this.offsetTop; + + element + .css({ background: '-moz-radial-gradient(circle at ' + x + 'px ' + y + 'px, rgba(255,255,255,0.4) 0px, rgba(255,255,255,0.0) 45px), ' + initial_background }) + .css({ background: '-webkit-radial-gradient(circle at ' + x + 'px ' + y + 'px, rgba(255,255,255,0.4) 0px, rgba(255,255,255,0.0) 45px), ' + initial_background }) + .css({ background: 'radial-gradient(circle at ' + x + 'px ' + y + 'px, rgba(255,255,255,0.4) 0px, rgba(255,255,255,0.0) 45px), ' + initial_background }); + } + }) + .mouseout(function () { + element.removeAttr('style'); + }); + } + }; + }]); + + + +})(); + +function getParameterByName(name, url) { + if (!url) url = window.location.href; + name = name.replace(/[\[\]]/g, "\\$&"); + var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), + results = regex.exec(url); + if (!results) return ''; + if (!results[2]) return ''; + return (results[2].replace(/\+/g, " ")); +} + +function isCascadeFrame(ref) { + if (self != top) { + var e = document.body; + e.parentNode.removeChild(e); + window.location = "unKnownError"; + } +} diff --git a/ecomp-portal-FE/client/app/views/tabs/tabs.controller.spec.js b/ecomp-portal-FE/client/app/views/tabs/tabs.controller.spec.js new file mode 100644 index 00000000..44aea239 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/tabs/tabs.controller.spec.js @@ -0,0 +1,80 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +describe('Controller: TabsCtrl ',() => { + beforeEach(module('ecompApp')); + + beforeEach(inject((_CacheFactory_)=>{ + _CacheFactory_.destroyAll(); + })); + + let TabsCtrl, $controller, $q, $rootScope, $log, $window, $cookies,$scope; + + beforeEach(inject((_$controller_, _$q_, _$rootScope_, _$log_, _$window_, _$cookies_)=>{ + [$controller, $q, $rootScope, $log, $window, $cookies] = [_$controller_, _$q_, _$rootScope_, _$log_, _$window_, _$cookies_]; + })); + + var deferredApps, deferredUserProfile; + beforeEach(()=>{ + deferredApps = $q.defer(); + deferredUserProfile = $q.defer(); + let applicationsServiceMock = jasmine.createSpyObj('applicationsServiceMock', ['getUserApps']); + applicationsServiceMock.getUserApps.and.returnValue(deferredApps.promise); + + let userProfileServiceMock = jasmine.createSpyObj('userProfileServiceMock',['getUserProfile']); + userProfileServiceMock.getUserProfile.and.returnValue(deferredUserProfile.promise); + + $scope = $rootScope.$new(); + TabsCtrl = $controller('TabsCtrl', { + applicationsService: applicationsServiceMock, + $log: $log, + $window: $window, + userProfileService: userProfileServiceMock, + $scope: $scope, + $cookies: $cookies, + $rootScope: $rootScope + }); + }); + + it('should populate this.apps with data from portals service getUserApps', ()=>{ + var profile = {roles:'superAdmin',userId:'userid'}; + deferredUserProfile.resolve(profile); + deferredApps.resolve([{name: 'portal1'},{name: 'portal2'},{name: 'portal3'}]); + $rootScope.$apply(); + expect($scope.appsViewData.length).toBe(3); + }); + + it('should call $log error when getAllPortals fail', ()=>{ + spyOn($log, 'error'); + deferredUserProfile.reject('something happened!'); + $rootScope.$apply(); + expect($log.error).toHaveBeenCalled(); + }); + + it('should open link in a new window when clicking app thumbnail', () => { + spyOn($window, 'open'); + let someUrl = 'http://some/url/'; + TabsCtrl.goToPortal(someUrl); + expect($window.open).toHaveBeenCalledWith(someUrl, '_self'); + }); + + +}); diff --git a/ecomp-portal-FE/client/app/views/tabs/tabs.less b/ecomp-portal-FE/client/app/views/tabs/tabs.less new file mode 100644 index 00000000..0247e3d0 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/tabs/tabs.less @@ -0,0 +1,658 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .w-ecomp-tabs { + .bg_s; + + position: fixed; + left: 0; + right: 0; + bottom: @footer-height; + overflow-y: hidden; + top: @header-height; + padding-left: 0; + + .go-button { + .btn-green; + width: 96px; + position: absolute; + border-radius: 0px; + } + + .tabs-container { + .content_justify; + position: relative; + padding: 15px 0 32px 0; + width: 100%; + + .tabs-title { + .a24r; + margin: auto; + .content_justify; + } + .portals-list { + min-height: 70vh; + justify-content: center; + flex-flow: row wrap; + width: 1170px; + + margin: auto; + margin-bottom: 63px; + + .app-gridster-header { + background-color: @u; + } + + .app-gridster-footer { + background-color: @u; + } + + .portals-list-item { + background-color: @u; + border-radius: 2px; + box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1); + display: inline-block; + width: 360px; + height: 300px; + background-size: cover; + cursor: pointer; + margin: 15px; + overflow: hidden; + + .portals-item-info { + background-color: @u; + height: 120px; + top: 180px; + position: relative; + box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1); + padding: 16px; + + .info-title { + .a24r; + margin-bottom: 4px; + + text-overflow: ellipsis; + overflow: hidden; + } + .info-description { + .c16r; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + .info-button { + .btn-green; + width: 96px; + position: absolute; + bottom: 16px; + left: 16px; + } + + &:hover { + opacity: .93; + z-index: 3; + } + } + } + } + } +} + + +.build-number { + .o12i; +} + +@keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + + + +.tab-container { + background: #8dc8fb; + margin: 0; + padding: 0; + max-height: 40px; +} +.tab-container ul.nav-tabs { + margin: 0; + list-style-type: none; + line-height: 40px; + max-height: 40px; + overflow: hidden; + display: inline-block; + display: -webkit-flex; + display: flex; + padding-right: 20px; + border-bottom: 5px solid #f7f7f7; +} +.tab-container ul.nav-tabs > li { + margin: 5px -14px 0; + -moz-border-radius-topleft: 28px 145px; + -webkit-border-top-left-radius: 28px 145px; + border-top-left-radius: 28px 145px; + -moz-border-radius-topright: 28px 145px; + -webkit-border-top-right-radius: 28px 145px; + border-top-right-radius: 28px 145px; + padding: 0 30px 0 25px; + height: 170px; + background: #c3d5e6; + position: relative; + -moz-box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5); + -webkit-box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5); + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5); + width: 200px; + max-width: 200px; + min-width: 20px; + border: 1px solid #aaa; +} +.tab-container ul.nav-tabs > li:first-child { + margin-left: 0; +} +.tab-container ul.nav-tabs > li:last-of-type { + margin-right: 0; +} +.tab-container ul.nav-tabs > li > a { + display: block; + max-width: 100%; + text-decoration: none; + color: #222; + padding: 3px 7px; +} +.tab-container ul.nav-tabs > li > a span { + overflow: hidden; + white-space: nowrap; + display: block; +} +.tab-container ul.nav-tabs > li > a:focus, .tab-container ul.nav-tabs > li > a:hover { + background-color: transparent; + border-color: transparent; +} +.tab-container ul.nav-tabs > li > a .glyphicon-remove { + color: #777; + display: inline-block; + padding: 3px; + font-size: 10px; + position: absolute; + z-index: 10; + top: 7px; + right: -10px; + -moz-border-radius: 50%; + -webkit-border-radius: 50%; + border-radius: 50%; +} +.tab-container ul.nav-tabs > li > a .glyphicon-remove:hover { + background: #d39ea3; + color: white; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.25); + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.25); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.25); + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25); +} +.tab-container ul.nav-tabs > li.active { + z-index: 4; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Y3ZjdmNyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); + background-size: 100%; + background-image: -webkit-gradient(linear, 50% 0%, 50% 30, color-stop(0%, #ffffff), color-stop(100%, #f7f7f7)); + background-image: -moz-linear-gradient(#ffffff, #f7f7f7 30px); + background-image: -webkit-linear-gradient(#ffffff, #f7f7f7 30px); + background-image: linear-gradient(#ffffff, #f7f7f7 30px); +} +.tab-container ul.nav-tabs > li.active > a { + background-color: transparent; + border-color: transparent; + border-bottom-color: transparent; +} +.tab-container ul.nav-tabs > li.active > a:focus, .tab-container ul.nav-tabs > li.active > a:hover { + background-color: transparent; + border-color: transparent; + border-bottom-color: transparent; +} +.tab-container ul.nav-tabs .btn { + float: left; + height: 20px; + width: 35px; + min-width: 35px; + max-width: 35px; + margin: 10px 0 0 0; + border-color: #71a0c9; + outline: none; + -moz-transform: skew(30deg); + -ms-transform: skew(30deg); + -webkit-transform: skew(30deg); + transform: skew(30deg); +} +.tab-container ul.nav-tabs .btn.btn-default { + background: #c3d5e6; +} +.tab-container ul.nav-tabs .btn.btn-default:hover { + background: #d2deeb; +} +.tab-container ul.nav-tabs .btn.btn-default:active { + background: #9cb5cc; +} +.tab-container .tab-pane { + padding: 0px 0px; + text-align: center; +} +.tab-container .tab-pane.active { + border-top: 1px solid #ddd; +} + +.tab-container md-content { + background-color: transparent !important; } + .tab-container md-content md-tabs { + border: 1px solid #e1e1e1; } + .tab-container md-content md-tabs md-tab-content { + background: #f6f6f6; } + .tab-container md-content md-tabs md-tabs-canvas { + background: white; } + .tab-container md-content h1:first-child { + margin-top: 0; } +.tab-container md-input-container { + padding-bottom: 0; } +.tab-container .remove-tab { + margin-bottom: 40px; } +.tab-container .demo-tab > div > div { + padding: 25px; + box-sizing: border-box; } +.tab-container .edit-form input { + width: 100%; } +.tab-container md-tabs { + border-bottom: 1px solid rgba(0, 0, 0, 0.12); } +.tab-container md-tab[disabled] { + opacity: 0.5; } +.tab-container label { + text-align: left; } +.tab-container .long > input { + width: 264px; } +.tab-container .md-button.add-tab { + transform: translateY(5px); } + +.md-tab{ + font-size: 13px; + line-height: 30px; + margin: 5px -3px 0; + border-top-left-radius: 88px 205px; + border-top-right-radius: 88px 205px; + padding: 0 30px 0 25px; + height: 40px; + background: #c3d5e6; + position: relative; + box-shadow: 0 4px 6px rgba(0,0,0,.5); + width: 180px; + max-width: 200px; + min-width: 20px; + border: 1px solid #aaa; + text-transform: capitalize; + text-align: left; +} + +md-tabs .md-tab { + color: #222; +} + +md-tabs-canvas { + border-bottom: 5px solid #f7f7f7; + height: 40px; +} +.md-tab.md-active { + z-index: 4; + background-color: #f5f5f5 !important; +} +.md-tab:first-child{ + margin-left: 10px; +} +md-ink-bar{ + z-index: 5 !important; +} + +.glyphicon { + position: fixed; + line-height: 4; +} +.close_button { + font-size: x-small;width: 10px;margin-left: 130px; +} +@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { + .close_button { + font-size: x-small;width: 10px;margin-left: 130px;margin-top: 55px; + } +} + +*, +*:after, +*:before +{ + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + + +.button-default +{ + .transition(@transitionDefault color); + background: transparent; + border: none; + cursor: pointer; + margin: 0; + outline: none; + position: relative; +} + +.show-notifications +{ + position: relative; + + &:hover, + &:focus, + &.active + { + #icon-bell + { + fill: @colorWetAsphalt; + } + } + + #icon-bell + { + fill: @colorAsbestos; + } + + + + &.active ~ .notifications + { + opacity: 1; + top: 35px; + } +} +.notifications_li{ + border-top: 1px solid #bdc3c7; + color: #7f8c8d; + cursor: default; + display: block; + padding: 10px; + position: relative; + white-space: nowrap; + width: 350px; +} +.notifications_li:hover{ + background:#eee; +} +.notifications_detail{ + margin-left: 10px; + white-space: normal; + width: 280px; + display: inline-block; + vertical-align: middle; +} + +.notifications_empty{ + display: none; + text-align: center; +} +.notifications_title{ + display: block; +} + +.notifications_date{ + color: #95a5a6; + font-size: .85em; + margin-top: 3px; +} +.notifications +{ + .border-radius(@borderRadius); + .transition(@transitionDefault opacity); + background: @colorClouds; + border: 1px solid @colorSilver; + left: 10px; + opacity: 0; + + &:after + { + border: 10px solid transparent; + border-bottom-color: @colorPeterRiver; + content: ''; + display: block; + height: 0; + left: 10px; + position: absolute; + top: -20px; + width: 0; + } + + h3, + .show-all + { + background: @colorPeterRiver; + color: @colorWhite; + margin: 0; + padding: 10px; + width: 350px; + } + + h3 + { + cursor: default; + font-size: 1.05em; + font-weight: normal; + } + + .show-all + { + display: block; + text-align: center; + text-decoration: none; + + &:hover, + &:focus + { + text-decoration: underline; + } + } + + .notifications-list + { + list-style: none; + margin: 0; + overflow: hidden; + padding: 0; + + .item + { + .transition-transform(@transitionDefault); + border-top: 1px solid @colorSilver; + color: @colorAsbestos; + cursor: default; + display: block; + padding: 10px; + position: relative; + white-space: nowrap; + width: 350px; + + &:before, + .details, + .button-dismiss + { + display: inline-block; + vertical-align: middle; + } + + &:before + { + .border-radius(50%); + background: @colorPeterRiver; + content: ''; + height: 8px; + width: 8px; + } + + .details + { + margin-left: 10px; + white-space: normal; + width: 280px; + + .title, + .date + { + display: block; + } + + .date + { + color: @colorConcrete; + font-size: .85em; + margin-top: 3px; + } + } + + .button-dismiss + { + color: @colorSilver; + font-size: 2.25em; + + &:hover, + &:focus + { + color: @colorConcrete; + } + } + + &.no-data + { + display: none; + text-align: center; + + &:before + { + display: none; + } + } + + &.expired + { + color: @colorSilver; + + &:before + { + background: @colorSilver; + } + + .details + { + .date + { + color: @colorSilver; + } + } + } + + &.dismissed + { + .transform(translateX(100%)); + } + } + } + + &.empty + { + .notifications-list + { + .no-data + { + display: block; + padding: 10px; + } + } + } +} + +/* variables */ +@colorClouds: #ecf0f1; +@colorSilver: #bdc3c7; +@colorWhite: #fefefe; +@colorPeterRiver: #3498db; +@colorConcrete: #95a5a6; +@colorAsbestos: #7f8c8d; +@colorWetAsphalt: #34495e; + +@borderRadius: 2px; + +@transitionDefault: 0.25s ease-out 0.10s; + +/* mixins */ +.background-clip(@value: border-box) +{ + -moz-background-clip: @value; + -webkit-background-clip: @value; + background-clip: @value; +} + +.border-radius(@value: 5px) +{ + -moz-border-radius: @value; + -webkit-border-radius: @value; + border-radius: @value; + .background-clip(padding-box); +} + +.transform(@value) +{ + -webkit-transform: @value; + -moz-transform: @value; + -ms-transform: @value; + -o-transform: @value; + transform: @value; +} + +.transition(@value: all 0.25s ease-out) +{ + -webkit-transition: @value; + -moz-transition: @value; + -o-transition: @value; + transition: @value; +} + +.transition-transform(@transition: 0.25s ease-out) +{ + -webkit-transition: -webkit-transform @transition; + -moz-transition: -moz-transform @transition; + -o-transition: -o-transform @transition; + transition: transform @transition; +} + +.tab-cross-sign{ + position:relative; + margin-left:130px; + font-size:9px; + top:-30px; + @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { + top:-70px; + } +} diff --git a/ecomp-portal-FE/client/app/views/tabs/tabs.tpl.html b/ecomp-portal-FE/client/app/views/tabs/tabs.tpl.html new file mode 100644 index 00000000..62af73e9 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/tabs/tabs.tpl.html @@ -0,0 +1,46 @@ + +
+
+ + + + + {{adjustTitle(tab.title)}} + + + + + + + + + + +
+
+ +
+
+ +
+
diff --git a/ecomp-portal-FE/client/app/views/userbar/userbar.controller.js b/ecomp-portal-FE/client/app/views/userbar/userbar.controller.js new file mode 100644 index 00000000..5dae2f86 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/userbar/userbar.controller.js @@ -0,0 +1,262 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +(function () { + + class UserbarCtrl { + constructor(userProfileService,userbarUpdateService, $log, $rootScope , $interval,$scope,$timeout,dashboardService) { + this.$log = $log; + this.userProfileService = userProfileService; + this.$rootScope = $rootScope; + $rootScope.isAdminPortalAdmin = false; + $scope.updateRate = 10000; //default online user bar refreshing rate + var intervalPromise = null; + $scope.myservice = userbarUpdateService; + $scope.userList=this.userLists; + var websocket = ''; + var currentUser = ''; + var remoteUser = ''; + var f = ''; + + + function socketSetup(initialPageVisit,_currentUser, _remoteUser, _f) { + + + + if( true) { + + var href = window.location.href; + var hostPatt = new RegExp(window.location.host +"/[^/]*"); + var res = hostPatt.exec(href); + var protocol = window.location.protocol.replace("http","ws"); + var signalingServerPath = protocol + "//" + res + "/opencontact"; + var wsUri = signalingServerPath; + console.log("Connecting to " + wsUri); + websocket = new WebSocket(wsUri); + //localStorage.notifySocket = JSON.stringify(websocket); + //window.top.notifySocket = websocket; + + currentUser = _currentUser; + remoteUser = _remoteUser; + f = socketSend; + + + } + + //var websocket = JSON.parse(localStorage.notifySocket || "{}") ; + if( websocket != null) { + websocket.onopen = function(evt) { onOpen(evt); }; + websocket.onmessage = function(evt) { onMessage(evt); }; + websocket.onerror = function(evt) { onError(evt); }; + + } + + //if(f != undefined) + // f(); + + //window.top.notifySocket.send(""); + } + + function socketSend(currentUser, remoteUser) { + + + + websocket.send(JSON.stringify({ + from: currentUser, + to: remoteUser + })); + + //window.top.notifySocket.send(""); + } + + + + + function onOpen(evt) { + console.log("onOpen"); + //writeToScreen("CONNECTED"); + + if(f != undefined) + f(currentUser, remoteUser); + + } + + function onMessage(evt) { + if (typeof evt.data == "string") { + writeToScreen("RECEIVED (text): " + evt.data); + var chatObject = JSON.parse(evt.data); + if(confirm("User " + chatObject.from + " is requesting a chat session with you. Please click ok to accept")== true) { + + var url = userProfileService.getSortedUserIdCombination(chatObject.from, chatObject.to); + var win = window.open(url, '_blank'); + win.focus(); + } else { + // + } + + + } else { + writeToScreen("RECEIVED (binary): " + evt.data); + } + } + + function onError(evt) { + writeToScreen('ERROR: ' + evt.data); + } + + function writeToScreen(message) { + console.log(message); + } + + function callAtInterval() { + userProfileService.getActiveUser() + .then(res=> { + $log.info('sasasasasa getting userinfo from shared context.. '); + if(res==null){ + $log.info('failed getting userinfo from shared context.. '); + $log.info('getting userinfo from session '); + }else{ + $log.info('getting ActiveUser successfully',res); + $scope.userList = []; + for(var i=0;i { + var footerOff = $('#online-userbar').offset().top; + var headOff = $('#footer').offset().top; + var userbarHeight= parseInt($(".online-user-container").css('height'),10); + var defaultOffSet = 45; + $(".online-user-container").css({ + "height" : headOff - footerOff-defaultOffSet + }); + }); + } + + + var intervalPromise; + dashboardService.getOnlineUserUpdateRate().then(res=> { + // $log.info('getting Online User Update Rate init***********************', res); + if (res == null || res.response == null) { + $log.error('UserbarCtlr: failed to get online user update rate!'); + } else { + // $log.debug('UserbarCtlr: got online User Update Rate ' + res.response); + var rate = parseInt(res.response.onlineUserUpdateRate); + // var updateRate = parseInt(res.response.onlineUserUpdateRate); + var duration = parseInt(res.response.onlineUserUpdateDuration); + userbarUpdateService.setMaxRefreshCount(parseInt(duration/rate)+1); + userbarUpdateService.setRefreshCount(userbarUpdateService.maxCount); + // $scope.refreshCount = userbarUpdateService.getRefreshCount(); + + if (rate != NaN && duration != NaN) { + // $log.debug('UserbarCtlr: scheduling function at interval ' + millis); + $scope.updateRate=rate; + $scope.start($scope.updateRate); + } + } + }).catch(err=> { + $log.error('Header Controller:: getOnlineUserUpdateRate() failed: ' + err); + }); + + $scope.start = function(rate) { + // stops any running interval to avoid two intervals running at the same time + $scope.stop(); + // store the interval promise + intervalPromise = $interval(updateActiveUsers, rate); + }; + + $scope.stop = function() { + $interval.cancel(intervalPromise); + }; + + $scope.$watch('myservice.getRefreshCount()', function (newVal,oldVal) { + //$log.info("refresh "+$scope.myservice.refreshCount + " more time(s)"); + if (newVal<=0) { + // $log.info("UserbarCtrl: stopped refreshing online users"); + $scope.stop(); + } else if (newVal== $scope.myservice.maxCount){ + // $log.info("UserbarCtrl: start refreshing online users at every "+($scope.updateRate/1000)+" sec"); + // initial update of activate users + userProfileService.getUserProfile().then(res=> { + if (res == null || res.orgUserId == null) { + $log.error('UserbarCtlr: failed to get profile of the user!'); + } + else { + updateActiveUsers(); + $scope.start($scope.updateRate); + socketSetup(null, res.orgUserId, null, "socketSend"); + } + }); + + + } + }); + function updateActiveUsers() { + userProfileService.getActiveUser() + .then(res=> { + $log.info('getting Active User init***********************'); + // decrement the counter every time updateActiveUser is called; + userbarUpdateService.decrementRefreshCount(); + + if(res==null ||res.status!='OK'){ + $log.error('failed updateActiveUsers res is null.. '); + + }else{ + $log.info('getting ActiveUser successfully',res); + $scope.userList = []; + var maxItems = 10; + var users = res.response; + if (users.length < maxItems) + maxItems = users.length; + for(var i=0;i { + var footerOff = $('#online-userbar').offset().top; + var headOff = $('#footer').offset().top; + var userbarHeight= parseInt($(".online-user-container").css('height'),10); + var defaultOffSet = 45; + $(".online-user-container").css({ + "height" : headOff - footerOff-defaultOffSet + }); + }); + } + } + } + UserbarCtrl.$inject = ['userProfileService', 'userbarUpdateService', '$log', '$rootScope','$interval','$scope','$timeout','dashboardService']; + angular.module('ecompApp').controller('UserbarCtrl', UserbarCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/userbar/userbar.less b/ecomp-portal-FE/client/app/views/userbar/userbar.less new file mode 100644 index 00000000..489185f7 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/userbar/userbar.less @@ -0,0 +1,41 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .w-ecomp-sidebar { + + position: relative; + left: 0; + right: 0; + top: 5px; + padding-left: 0; + + } + + @media screen and (-webkit-min-device-pixel-ratio:0) +{ + .w-ecomp-sidebar { + + position: relative; + left: 0; + right: 0; + top: -5px; + padding-left: 0; + + } +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/userbar/userbar.tpl.html b/ecomp-portal-FE/client/app/views/userbar/userbar.tpl.html new file mode 100644 index 00000000..dab62910 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/userbar/userbar.tpl.html @@ -0,0 +1,20 @@ + + diff --git a/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.js b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.js new file mode 100644 index 00000000..182ffe8f --- /dev/null +++ b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.js @@ -0,0 +1,211 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; +(function () { + class NewUserModalCtrl { + constructor($scope, $log, usersService, applicationsService, confirmBoxService) { + let init = () => { + $log.info('NewUserModalCtrl::init'); + this.isSaving = false; + this.anyChanges = false; + this.isGettingAdminApps = false; + if($scope.ngDialogData && $scope.ngDialogData.selectedUser && $scope.ngDialogData.dialogState){ + this.selectedUser = $scope.ngDialogData.selectedUser; + this.dialogState = $scope.ngDialogData.dialogState; + this.isShowBack = false; + if(this.dialogState === 3){ + this.getUserAppsRoles(); + } + }else{ + this.isShowBack = true; + this.selectedUser = null; + this.dialogState = 1; + } + }; + + this.appChanged = (index) => { + let myApp = this.adminApps[index]; + $log.debug('NewUserModalCtrl::appChanged: index: ', index, '; app id: ', myApp.id, 'app name: ',myApp.name); + myApp.isChanged = true; + this.anyChanges = true; + } + + this.deleteApp = (app) => { + let appMessage = this.selectedUser.firstName + ' ' + this.selectedUser.lastName; + confirmBoxService.deleteItem(appMessage).then(isConfirmed => { + if(isConfirmed){ + app.isChanged = true; + this.anyChanges = true; + app.isDeleted = true; + app.appRoles.forEach(function(role){ + role.isApplied = false; + }); + } + }).catch(err => { + $log.error('NewUserModalCtrl::deleteApp error: ',err); + }); + }; + + this.getUserAppsRoles = () => { + if (!this.selectedUser || !this.selectedUser.orgUserId) { + $log.error('NewUserModalCtrl::getUserAppsRoles error: No user is selected'); + this.dialogState = 1; + return; + } + $log.debug('NewUserModalCtrl::getUserAppsRoles: about to call getAdminAppsSimpler'); + this.isGettingAdminApps = true; + applicationsService.getAdminAppsSimpler().then((apps) => { + $log.debug('NewUserModalCtrl::getUserAppsRoles: beginning of then for getAdminAppsSimpler'); + this.isGettingAdminApps = false; + if (!apps || !apps.length) { + $log.error('NewUserModalCtrl::getUserAppsRoles error: no apps found'); + return null; + } + $log.debug('NewUserModalCtrl::getUserAppsRoles: then for getAdminAppsSimpler: step 2'); + $log.debug('NewUserModalCtrl::getUserAppsRoles: admin apps: ', apps); + this.adminApps = apps; + this.dialogState = 3; + this.userAppRoles = {}; + this.numberAppsProcessed = 0; + this.isLoading = true; + apps.forEach(app => { + $log.debug('NewUserModalCtrl::getUserAppsRoles: app: id: ', app.id, 'name: ',app.name); + app.isChanged = false; + app.isLoading = true; + app.isError = false; + app.isDeleted = false; + app.printNoChanges = false; + app.isUpdating = false; + app.isErrorUpdating = false; + app.isDoneUpdating = false; + app.errorMessage = ""; + usersService.getUserAppRoles(app.id, this.selectedUser.orgUserId).then((userAppRolesResult) => { + $log.debug('NewUserModalCtrl::getUserAppsRoles: got a result for app: ',app.id,': ',app.name,': ',userAppRolesResult); + app.appRoles = userAppRolesResult; + app.isLoading = false; + + }).catch(err => { + $log.error(err); + app.isError = true; + app.isLoading = false; + app.errorMessage = err.headers('FEErrorString'); + $log.debug('NewUserModalCtrl::getUserAppsRoles: in new-user.controller: response header: '+err.headers('FEErrorString')); + }).finally(()=>{ + this.numberAppsProcessed++; + if (this.numberAppsProcessed == this.adminApps.length) { + this.isLoading = false; + } + }); + }) + return; + }).catch(err => { + $log.error(err); + }) + + } + + this.getAdminApps = () => { + if (!this.selectedUser || !this.selectedUserorgUserId) { + $log.error('NewUserModalCtrl::getAdminApps: No user is selected'); + this.dialogState = 1; + return; + } + applicationsService.getAdminApps().promise().then(apps => { + if (!apps || !apps.length) { + $log.error('NewUserModalCtrl::getAdminApps: no apps found'); + return null; + } + $log.debug('NewUserModalCtrl::getAdminApps: admin apps: ', apps); + this.adminApps = apps; + this.dialogState = 3; + return; + }).catch(err => { + $log.error('NewUserModalCtrl::getAdminApps: ', err); + }) + + } + + this.updateUserAppsRoles = () => { + $log.debug('NewUserModalCtrl::updateUserAppsRoles: entering updateUserAppsRoles'); + if(!this.selectedUser || !this.selectedUser.orgUserId || !this.adminApps){ + $log.debug('NewUserModalCtrl::updateUserAppsRoles: returning early'); + return; + } + this.isSaving = true; + $log.debug('NewUserModalCtrl::updateUserAppsRoles: going to update user: ' + this.selectedUser.orgUserId); + this.numberAppsProcessed = 0; + this.numberAppsSucceeded = 0; + this.adminApps.forEach(app => { + if (app.isChanged) { + $log.debug('NewUserModalCtrl::updateUserAppsRoles: app roles have changed; going to update: id: ', app.id, '; name: ', app.name); + app.isUpdating = true; + usersService.updateUserAppRoles({orgUserId: this.selectedUser.orgUserId, appId: app.id, appRoles: app.appRoles}) + .then(res => { + $log.debug('NewUserModalCtrl::updateUserAppsRoles: User app roles updated successfully on app: ',app.id); + app.isUpdating = false; + app.isDoneUpdating = true; + this.numberAppsSucceeded++; + }).catch(err => { + $log.error(err); + app.isErrorUpdating = true; + }).finally(()=>{ + this.numberAppsProcessed++; + if (this.numberAppsProcessed == this.adminApps.length) { + this.isSaving = false; + } + if (this.numberAppsSucceeded == this.adminApps.length) { + $scope.closeThisDialog(true); + } + }) + } else { + $log.debug('NewUserModalCtrl::updateUserAppsRoles: app roles have NOT changed; NOT going to update: id: ', app.id, '; name: ', app.name); + app.noChanges = true; + app.isError = false; + this.numberAppsProcessed++; + this.numberAppsSucceeded++; + if (this.numberAppsProcessed == this.adminApps.length) { + this.isSaving = false; + } + if (this.numberAppsSucceeded == this.adminApps.length) { + $scope.closeThisDialog(true); + } + } + }); + }; + + this.navigateBack = () => { + if (this.dialogState === 1) { + } + if (this.dialogState === 3) { + this.dialogState = 1; + } + }; + + init(); + + $scope.$on('$stateChangeStart', e => { + e.preventDefault(); + }); + } + } + NewUserModalCtrl.$inject = ['$scope', '$log', 'usersService', 'applicationsService', 'confirmBoxService']; + angular.module('ecompApp').controller('NewUserModalCtrl', NewUserModalCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.spec.js b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.spec.js new file mode 100644 index 00000000..54c564b7 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.spec.js @@ -0,0 +1,222 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; + +describe('Controller: NewUserModalCtrl ', () => { + beforeEach(module('testUtils')); + beforeEach(module('ecompApp')); + + let promisesTestUtils; + beforeEach(inject((_CacheFactory_, _promisesTestUtils_)=> { + _CacheFactory_.destroyAll(); + promisesTestUtils = _promisesTestUtils_; + })); + + let newUser, $controller, $q, $rootScope, $log, $scope; + + let applicationsServiceMock, usersServiceMock, confirmBoxServiceMock; + let deferredAdminApps, deferredUsersAccounts, deferredUsersAppRoles, deferredUsersAppRoleUpdate; + + beforeEach(inject((_$controller_, _$q_, _$rootScope_, _$log_)=> { + $rootScope = _$rootScope_; + $q = _$q_; + $controller = _$controller_; + $log = _$log_; + })); + + beforeEach(()=> { + [deferredAdminApps, deferredUsersAccounts, deferredUsersAppRoles, deferredUsersAppRoleUpdate] = [$q.defer(),$q.defer(), $q.defer(), $q.defer()]; + + /*applicationsServiceMock = { + getAdminApps: () => { + var promise = () => {return deferredAdminApps.promise}; + var cancel = jasmine.createSpy(); + return { + promise: promise, + cancel: cancel + } + } + };*/ + + confirmBoxServiceMock = { + deleteItem: () => { + var promise = () => {return deferredAdminApps.promise}; + var cancel = jasmine.createSpy(); + return { + promise: promise, + cancel: cancel + } + } + }; + + applicationsServiceMock = jasmine.createSpyObj('applicationsServiceMock', ['getAdminAppsSimpler']); + applicationsServiceMock.getAdminAppsSimpler.and.returnValue(deferredAdminApps.promise); + + usersServiceMock = jasmine.createSpyObj('usersServiceMock', ['getAccountUsers','getUserAppRoles','updateUserAppsRoles']); + + usersServiceMock.getAccountUsers.and.returnValue(deferredUsersAccounts.promise); + usersServiceMock.getUserAppRoles.and.returnValue(deferredUsersAppRoles.promise); + usersServiceMock.updateUserAppsRoles.and.returnValue(deferredUsersAppRoleUpdate.promise); + + $scope = $rootScope.$new(); + newUser = $controller('NewUserModalCtrl', { + $scope: $scope, + $log: $log, + usersService: usersServiceMock, + applicationsService: applicationsServiceMock, + confirmBoxService: confirmBoxServiceMock + }); + }); + + it('should open modal window without user when no user is selected', ()=> { + expect(newUser.selectedUser).toBe(null); + }); + + it('should open modal window with selectedUser apps roles when user is selected', ()=> { + let roles = {apps: [{id: 1, appRoles: [{id: 3, isApplied: true}]}]}; + let someUser = {userId: 'asdfjl'}; + + deferredUsersAppRoles.resolve(roles); + deferredAdminApps.resolve(roles.apps); + + $scope.ngDialogData = { + selectedUser: someUser, + dialogState: 2 + }; + + newUser = $controller('NewUserModalCtrl', { + $scope: $scope, + $log: $log, + usersService: usersServiceMock, + applicationsService: applicationsServiceMock, + confirmBoxService: confirmBoxServiceMock + }); + + newUser.getUserAppsRoles(); + $scope.$apply(); + + expect(newUser.selectedUser).toBe(someUser); + expect(newUser.adminApps).toEqual(roles.apps); + }); + + it('should push to apps order list only apps that has applied roles when initializing', () => { + let roles = {apps: [{appId: 13, appRoles: [{id: 3, isApplied: true}]},{appId: 20, appRoles: [{id: 3, isApplied: false}]}]}; + let someUser = {userId: 'asdfjl'}; + + deferredUsersAppRoles.resolve(roles); + + $scope.ngDialogData = { + selectedUser: someUser, + dialogState: 2 + }; + + newUser = $controller('NewUserModalCtrl', { + $scope: $scope, + $log: $log, + usersService: usersServiceMock, + applicationsService: applicationsServiceMock, + confirmBoxService: confirmBoxServiceMock + }); + + $scope.$apply(); + + }); + + it('should push app to apps order list when applying at least one role to user from app', () => { + let roles = {apps: [{appId: 13, appRoles: [{id: 3, isApplied: true}]},{appId: 20, appRoles: [{id: 3, isApplied: false}]}]}; + let someUser = {userId: 'asdfjl'}; + + deferredUsersAppRoles.resolve(roles); + + $scope.ngDialogData = { + selectedUser: someUser, + dialogState: 2 + }; + + newUser = $controller('NewUserModalCtrl', { + $scope: $scope, + $log: $log, + usersService: usersServiceMock, + applicationsService: applicationsServiceMock, + confirmBoxService: confirmBoxServiceMock + }); + + $scope.$apply(); + + }); + + + it('should remove app from list when removing all user roles in it', () => { + let roles = {apps: [{appName: 'aaa', appId: 13, appRoles: [{id: 3, isApplied: true}]},{appName: 'vvv', appId: 20, appRoles: [{id: 3, isApplied: true}]}]}; + let someUser = {userId: 'asdfjl'}; + + promisesTestUtils.resolvePromise(confirmBoxServiceMock, 'deleteItem', true); + + deferredUsersAppRoles.resolve(roles); + + $scope.ngDialogData = { + selectedUser: someUser, + dialogState: 2 + }; + + newUser = $controller('NewUserModalCtrl', { + $scope: $scope, + $log: $log, + usersService: usersServiceMock, + applicationsService: applicationsServiceMock, + confirmBoxService: confirmBoxServiceMock + }); + + $scope.$apply(); + newUser.deleteApp(roles.apps[0]); + $scope.$apply(); + + }); + + it('should close the modal when update changes succeeded', () => { + let roles = {apps: [{appName: 'aaa', appId: 13, appRoles: [{id: 3, isApplied: true}]},{appName: 'vvv', appId: 20, appRoles: [{id: 3, isApplied: true}]}]}; + let someUser = {userId: 'asdfjl'}; + deferredUsersAppRoles.resolve(roles); + deferredUsersAppRoleUpdate.resolve(); + deferredAdminApps.resolve(roles.apps); + + $scope.ngDialogData = { + selectedUser: someUser, + dialogState: 2 + }; + + newUser = $controller('NewUserModalCtrl', { + $scope: $scope, + $log: $log, + usersService: usersServiceMock, + applicationsService: applicationsServiceMock, + confirmBoxService: confirmBoxServiceMock + }); + $scope.closeThisDialog = function(){}; + spyOn($scope, 'closeThisDialog'); + + newUser.getUserAppsRoles(); + $scope.$apply(); + newUser.updateUserAppsRoles(); + $scope.$apply(); + expect($scope.closeThisDialog).toHaveBeenCalledWith(true); + }); + }); diff --git a/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.html b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.html new file mode 100644 index 00000000..e50c9d4a --- /dev/null +++ b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.html @@ -0,0 +1,70 @@ + +
+
+ +
+
Next +
+
Cancel
+
+
+
+
+
+
+ Access and roles: +
+
+
+
{{app.name | elipsis: 27}}
+
+ +
+
{{app.errorMessage}}
+
Contacting application...
+
No changes
+
Updating application...
+
Finished updating application
+
Could not update application...
+ +
+
+
+
+ +
Back
+
Save +
+
Cancel
+
+
+
+
diff --git a/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.less b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.less new file mode 100644 index 00000000..9f86b022 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.less @@ -0,0 +1,126 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .new-user-modal { + display:block; + overflow:auto; + min-height: 450px; + + .user-apps-roles{ + .title{ + .n18r; + border-bottom: @a 3px solid; + } + + .app-roles-list{ + height: 286px; + overflow-y: auto; + + .app-item{ + border: 1px solid #d8d8d8; + border-radius: 2px; + background-color: #f8f8f8; + + padding: 10px; + margin-top: 8px; + + .app-item-left{ + padding-top: 0; + line-height: 30px; + height: 30px; + vertical-align: middle; + display:inline-block; + width: 45%; + border-radius: 2px; + border: 1px solid #d8d8d8; + margin-right: 10px; + padding-left: 4px; + background: #fff; + white-space: nowrap; + + } + .app-item-right{ + display:inline-block; + width: 45%; + border-radius: 2px; + border: 1px solid #d8d8d8; + background: #fff; + vertical-align: middle; + } + + .app-item-right-error{ + .k; + padding: 7px 7px 7px 7px; + display:inline-block; + width: 45%; + border-radius: 2px; + border: 1px solid #d8d8d8; + background: #fff; + vertical-align: middle; + } + + .app-item-right-contacting{ + .e; + padding: 7px 7px 7px 7px; + display:inline-block; + width: 45%; + border-radius: 2px; + border: 1px solid #d8d8d8; + background: #fff; + vertical-align: middle; + } + + .app-select-left{ + width: 45%; + margin-right: 10px; + vertical-align: middle; + + + .select-field{ + padding-top: 0; + line-height: 30px; + height: 30px; + vertical-align: middle; + border-radius: 2px; + border: 1px solid #d8d8d8; + margin-right: 10px; + padding-left: 4px; + background: #fff; + display:inline-block; + } + } + + + .app-item-delete{ + .ico_trash_default; + display: inline-block; + vertical-align: 2px; + cursor: pointer; + position: relative; + top: 6px; + color: transparent; + margin-left: 8px; + + } + + } + } + + } +} diff --git a/ecomp-portal-FE/client/app/views/users/users.controller.js b/ecomp-portal-FE/client/app/views/users/users.controller.js new file mode 100644 index 00000000..433b24e4 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/users/users.controller.js @@ -0,0 +1,187 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class UsersCtrl { + constructor($log, applicationsService, usersService, $scope, ngDialog,$timeout) { + this.$log = $log; + $scope.adminAppsIsNull = false; + $scope.appsIsDown = false; + + $log.info('UsersCtrl:: initializing...'); + let activeRequests = []; + let clearReq = (req) => { + activeRequests.splice(activeRequests.indexOf(req), 1); + }; + + let getAdminApps = () => { + $log.debug('UsersCtrl::getAdminApps: - Starting getAdminApps'); + try { + this.isLoadingTable = true; + var adminAppsReq = applicationsService.getAdminApps(); + + activeRequests.push(adminAppsReq); + adminAppsReq.promise().then(apps => { + $log.debug('UsersCtrl::getAdminApps: Apps for this user are: ' + JSON.stringify(apps)); + $log.debug('UsersCtrl::getAdminApps: Apps length: ' + apps.length); + var res1 = apps.sort(getSortOrder("name")); + if (!res1 || !res1.length) { + $log.error('UsersCtrl::getAdminApps: - no apps found'); + return null; + } + for (let i = 0; i < apps.length; i++) { + res1[i].index = i; + res1[i].value = apps[i].name; + res1[i].title = apps[i].name; + } + + this.adminApps = apps; + this.selectedApp = apps[0]; + clearReq(adminAppsReq); + $scope.adminAppsIsNull = false; + }).catch(e => { + $scope.adminAppsIsNull = true; + $log.error('UsersCtrl::getAdminApps: - getAdminApps() failed = '+ e.message); + clearReq(adminAppsReq); + }).finally(() => { + this.isLoadingTable = false; + }); + } catch (e) { + $scope.adminAppsIsNull = true; + $log.error('UsersCtrl::getAdminApps: - getAdminApps() failed!'); + this.isLoadingTable = false; + } + }; + + let getSortOrder = (prop) => { + return function(a, b) { + if (a[prop] > b[prop]) { + return 1; + } else if (a[prop] < b[prop]) { + return -1; + } + return 0; + } + } + + this.updateUsersList = () => { + $scope.appsIsDown = false; + $log.debug('UsersCtrl::updateUsersList: Starting updateUsersList'); + this.searchString = ''; + this.isAppSelectDisabled = true; + this.isLoadingTable = true; + usersService.getAccountUsers(this.selectedApp.id) + .then(accountUsers => { + $log.debug('UsersCtrl::updateUsersList length: '+ Object.keys(accountUsers).length); + this.isAppSelectDisabled = false; + this.accountUsers = accountUsers; + }).catch(err => { + this.isAppSelectDisabled = false; + $log.error('UsersCtrl::updateUsersList: ' + err); + $scope.appsIsDown = true; + }).finally(() => { + this.isLoadingTable = false; + }); + }; + + + let init = () => { + this.isLoadingTable = false; + this.selectedApp = null; + this.isAppSelectDisabled = false; + getAdminApps(); + + this.searchString = ''; + this.usersTableHeaders = ['First Name', 'Last Name', 'User ID', 'Roles']; + this.accountUsers = []; + }; + + this.openAddNewUserModal = (user) => { + let data = null; + if (user) { + data = { + dialogState: 3, + selectedUser: { + orgUserId: user.orgUserId, + firstName: user.firstName, + lastName: user.lastName + } + } + } + ngDialog.open({ + templateUrl: 'app/views/users/new-user-dialogs/new-user.modal.html', + controller: 'NewUserModalCtrl', + controllerAs: 'newUser', + data: data + }).closePromise.then(needUpdate => { + if (needUpdate.value === true) { + $log.debug('UsersCtrl::openAddNewUserModal updating table data...'); + this.updateUsersList(); + } + }); + }; + + this.openEditUserModal = (loginId) => { + var data = { + loginId : loginId, + updateRemoteApp : true, + appId : this.selectedApp!=null?this.selectedApp.id:'' + } + var modalInstance = ngDialog.open({ + templateUrl: 'app/views/header/user-edit/edit-user.tpl.html', + controller: 'editUserController', + data: data, + resolve: { + message: function message() { + var message = { + type: 'Contact', + }; + return message; + } + } + }).closePromise.then(needUpdate => { + //update selected app's database for this user. + console.log("'''''''''''''''''' now updating user list after update remote server"); + $timeout(this.updateUsersList, 1500); + }); + } + + + $scope.$watch('users.selectedApp', (newVal, oldVal) => { + if (!newVal || _.isEqual(newVal, oldVal)) { + return; + } + $log.debug('UsersCtrl::openAddNewUserModal:$watch selectedApp -> Fire with: ', newVal); + this.accountUsers = []; + this.updateUsersList(); + }); + + $scope.$on('$destroy', () => { + activeRequests.forEach(req => { + req.cancel(); + }); + }); + + init(); + } + } + UsersCtrl.$inject = ['$log', 'applicationsService', 'usersService', '$scope', 'ngDialog','$timeout']; + angular.module('ecompApp').controller('UsersCtrl', UsersCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/users/users.controller.spec.js b/ecomp-portal-FE/client/app/views/users/users.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/users/users.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/users/users.less b/ecomp-portal-FE/client/app/views/users/users.less new file mode 100644 index 00000000..ff2d815f --- /dev/null +++ b/ecomp-portal-FE/client/app/views/users/users.less @@ -0,0 +1,59 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .users-page-main{ + .bg_w; + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + overflow-y: @page-main-overflow-y; + padding-left: @padding-left-side; + + .users-table { + width: @table-width; + margin: @table-margin; + + } + + .error-text { + width: 1170px; + margin: auto; + padding: 20px; + left: 20px; + font-weight: bold; + font-size: 16px; + text-align: left; + color: @err; + background-color: @u; + + .error-help { + color: @o; + font-weight: normal; + } + + .error-help-bold { + color: @o; + font-weight: bold; + } + + } +} diff --git a/ecomp-portal-FE/client/app/views/users/users.tpl.html b/ecomp-portal-FE/client/app/views/users/users.tpl.html new file mode 100644 index 00000000..88a3b62d --- /dev/null +++ b/ecomp-portal-FE/client/app/views/users/users.tpl.html @@ -0,0 +1,105 @@ + +
+
+
+
Users
+
+ + +
+
+
+
+ + +
Add User
+
+
+
+

Attention:

+

 

+

Select "Add User" in order to add User and Roles to the '{{users.selectedApp.name}}' Application.

+
+
+ +
+ + + + + + + + + + + + + + + + + +
{{users.usersTableHeaders[0]}}{{users.usersTableHeaders[1]}}{{users.usersTableHeaders[2]}}{{users.usersTableHeaders[3]}}
+
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+

Attention:

+

 

+

It appears that you have not been added as an admin yet to an application.

+

 

+

Click on the Admins link to the left and check and see if you are listed as an admin for an application. + If not, you can add yourself to the appropriate application.

+
+
+
+ +
diff --git a/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.js b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.js new file mode 100644 index 00000000..f50e4683 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.js @@ -0,0 +1,202 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; +(function () { + class WidgetDetailsModalCtrl { + constructor($scope, $log, applicationsService, widgetsService, errorMessageByCode, ECOMP_URL_REGEX, $window,userProfileService,$cookies) { + + let newWidgetModel = { + name: null, + appId: null, + appName: null, + width: 360, + height: 300, + url: null + }; + + let getAvailableApps = () => { + applicationsService.getAppsForSuperAdminAndAccountAdmin().then(apps => { + this.availableApps=[]; + for(var i=0;i { + $log.error(err); + }); + }; + /**/ + + let init = () => { + $log.info('AppDetailsModalCtrl::init'); + this.isSaving = false; + if ($scope.ngDialogData && $scope.ngDialogData.widget) { + $log.debug('WidgetDetailsModalCtrl::getAvailableApps: Edit widget mode for', $scope.ngDialogData.widget); + this.isEditMode = true; + this.widget = _.clone($scope.ngDialogData.widget); + } else { + $log.debug('WidgetDetailsModalCtrl::init: New app mode'); + this.isEditMode = false; + this.widget = _.clone(newWidgetModel); + } + getAvailableApps(); + }; + + this.ECOMP_URL_REGEX = ECOMP_URL_REGEX; + + this.conflictMessages = {}; + this.scrollApi = {}; + let handleConflictErrors = err => { + if(!err.data){ + return; + } + if(!err.data.length){ + err.data = [err.data] + } + _.forEach(err.data, item => { + _.forEach(item.fields, field => { + this.conflictMessages[field.name] = errorMessageByCode[item.errorCode]; + $scope.widgetForm[field.name].$setValidity('conflict', false); + watchOnce[field.name](); + }); + }); + this.scrollApi.scrollTop(); + }; + + let resetConflict = fieldName => { + delete this.conflictMessages[fieldName]; + if($scope.widgetForm[fieldName]){ + $scope.widgetForm[fieldName].$setValidity('conflict', true); + } + }; + + let watchOnce = { + name: () => { + let unregisterName = $scope.$watchGroup(['widgetDetails.selectedApp','widgetDetails.widget.name'], (newVal, oldVal) => { + if(newVal.toLowerCase() !== oldVal.toLowerCase()){ + resetConflict('name'); + unregisterName(); + } + }); + }, + url: () => { + let unregisterUrl = $scope.$watch('widgetDetails.widget.url', (newVal, oldVal) => { + if(newVal.toLowerCase() !== oldVal.toLowerCase()) { + resetConflict('url'); + unregisterUrl(); + } + }); + } + }; + + this.updateSelectedApp = () => { + if (!this.selectedApp) { + return; + } + this.widget.appId = this.selectedApp.id; + this.widget.appName = this.selectedApp.name; + }; + + let emptyCookies = () => { + userProfileService.getUserProfile() + .then(profile=> { + $log.info('AppDetailsModalCtrl::emptyCookies profile: ', profile); + $scope.userId = profile.orgUserId; + $log.info('user has the following userId: ' + profile.userId); + if ($cookies.getObject($scope.userId + '_widget') != undefined && $cookies.getObject($scope.userId + '_widget') != null) { + $cookies.remove($scope.userId + '_widget'); + } + }); + }; + + this.saveChanges = () => { + if($scope.widgetForm.$invalid){ + return; + } + this.isSaving = true; + if(this.isEditMode){ + widgetsService.updateWidget(this.widget.id, this.widget) + .then(() => { + $log.debug('WidgetDetailsModalCtrl::saveChanges: Widget update succeeded!'); + $scope.closeThisDialog(true); + emptyCookies(); + }).catch(err => { + if(err.status === 409){ + handleConflictErrors(err); + } + $log.error(err); + }).finally(()=>{ + this.isSaving = false; + var objOffsetVersion = objAgent.indexOf("MSIE"); + if (objOffsetVersion != -1) { + $log.debug('WidgetDetailsModalCtrl::saveChanges: Browser is IE, forcing Refresh'); + $window.location.reload(); + } + }); + }else{ + widgetsService.createWidget(this.widget) + .then(() => { + $log.debug('WidgetDetailsModalCtrl::createWidget: Widget creation succeeded!'); + $scope.closeThisDialog(true); + emptyCookies(); + }).catch(err => { + if(err.status === 409){ + handleConflictErrors('WidgetDetailsModalCtrl::createWidget error: ',err); + } + $log.error('WidgetDetailsModalCtrl::createWidget error: ',err); + }).finally(()=>{ + this.isSaving = false; + var objOffsetVersion = objAgent.indexOf("MSIE"); + if (objOffsetVersion != -1) { + $log.debug('WidgetDetailsModalCtrl::createWidget: Browser is IE, forcing Refresh'); + $window.location.reload(); + } + }); + } + }; + + init(); + + $scope.$on('$stateChangeStart', e => { + e.preventDefault(); + }); + } + } + WidgetDetailsModalCtrl.$inject = ['$scope', '$log', 'applicationsService', 'widgetsService', 'errorMessageByCode', 'ECOMP_URL_REGEX', '$window','userProfileService','$cookies']; + angular.module('ecompApp').controller('WidgetDetailsModalCtrl', WidgetDetailsModalCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.spec.js b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.html b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.html new file mode 100644 index 00000000..87be6f15 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.html @@ -0,0 +1,132 @@ + +
+
Widget Details
+
+
+ +
+
Application Name
+
+ +
+
+
+ Application is required +
+
+
+
+
Widget Name
+ +
+ +
+
+
+ Widget Name is required + Widget Name must be letters, numbers, or underscore +
+
+
+
+
+
Width
+ +
+
+ Widget width is required + Minimum width is 300 +
+
+
+
+
Height
+ +
+
+ Widget height is required + Minimum height is 200 +
+
+
+
+
+
URL
+ +
+ +
+
+
+ Widget URL is required + Incorrect URL pattern +
+
+ +
+
+
+
+ +
Save
+
Cancel
+
+
diff --git a/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.less b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.less new file mode 100644 index 00000000..6e031b1d --- /dev/null +++ b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.less @@ -0,0 +1,94 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .widget-details-modal { + height: 430px; + + .title { + .n18r; + border-bottom: @a 3px solid; + + } + + .widget-properties-main { + padding: 16px; + height: 306px; + overflow-y: auto; + + .item{ + position: relative; + margin-bottom: 18px; + + .input-field{ + .custom-input-field; + width: 100%; + &.url{ + width: 78%; + display: inline-block; + } + } + + .select-field { + .custom-select-field; + } + + .item-label{ + .o14r; + } + + .right-item{ + position: relative; + display: inline-block; + width: 48%; + float: right; + } + .left-item{ + display: inline-block; + width: 48%; + } + + .url-validation-button{ + .btn-blue; + width: 20%; + display: inline-block; + float: right; + } + + .error-container{ + position: absolute; + width: 280px; + display: block; + height: 12px; + line-height: 12px; + + .err-message{ + color: red; + font-size: 9px; + } + .valid-message{ + color: green; + font-size: 9px; + } + } + + } + + } + +} diff --git a/ecomp-portal-FE/client/app/views/widgets/widgets.controller.js b/ecomp-portal-FE/client/app/views/widgets/widgets.controller.js new file mode 100644 index 00000000..c24f49cd --- /dev/null +++ b/ecomp-portal-FE/client/app/views/widgets/widgets.controller.js @@ -0,0 +1,151 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class WidgetsCtrl { + constructor($log, applicationsService, widgetsService, ngDialog, confirmBoxService, + userProfileService, $cookies, $scope) { + $log.info('WidgetsCtrl::init: Starting Up'); + $scope.infoMessage = true; + + let populateAvailableApps = widgets => { + let allPortalsFilterObject = {index: 0, title: 'All applications', value: ''}; + this.availableApps = [allPortalsFilterObject]; + this.filterByApp = this.availableApps[0]; + applicationsService.getAppsForSuperAdminAndAccountAdmin().then(myApps => { + var reSortedApp = myApps.sort(getSortOrder("name")); + var realAppIndex = 1; + for (let i = 1; i <= reSortedApp.length; i++) { + if (!reSortedApp[i-1].restrictedApp) { + $log.debug('WidgetsCtrl::populateAvailableApps: pushing {index: ', realAppIndex, 'title: ', reSortedApp[i - 1].name, + 'value: ', reSortedApp[i - 1].name, '}'); + this.availableApps.push({ + index: realAppIndex, + title: reSortedApp[i - 1].name, + value: reSortedApp[i - 1].name + }) + realAppIndex = realAppIndex + 1; + } + } + }).catch(err => { + $log.error(err); + }); + }; + + let getOnboardingWidgets = () => { + this.isLoadingTable = true; + widgetsService.getManagedWidgets().then(res => { + var reSortedWidget = res.sort(getSortOrder("name")); + this.widgetsList = reSortedWidget; + populateAvailableApps(reSortedWidget); + }).catch(err => { + $log.error('WidgetsCtrl::getOnboardingWidgets error: ' + err); + }).finally(()=> { + this.isLoadingTable = false; + }); + }; + + let getSortOrder = (prop) => { + return function(a, b) { + if (a[prop].toLowerCase() > b[prop].toLowerCase()) { + return 1; + } else if (a[prop].toLowerCase() < b[prop].toLowerCase()) { + return -1; + } + return 0; + } + } + + $scope.hideMe = function () { + $scope.infoMessage = false; + } + + let init = () => { + this.isLoadingTable = false; + getOnboardingWidgets(); + + + this.searchString = ''; + + this.widgetsTableHeaders = [ + {name: 'Widget Name', value: 'name', isSortable: false}, + {name: 'Application', value: 'appName', isSortable: true}, + {name: 'Width', value: 'width', isSortable: false}, + {name: 'Height', value: 'height', isSortable: false} + ]; + this.widgetsList = []; + }; + + this.filterByDropdownValue = item => { + if(this.filterByApp.value === ''){ + return true; + } + return item.appName === this.filterByApp.value; + }; + + this.openWidgetDetailsModal = (selectedWidget) => { + let data = null; + if(selectedWidget){ + if(!selectedWidget.id){ + $log.error('Widget id not found'); + return; + } + data = { + widget: selectedWidget + } + } + ngDialog.open({ + templateUrl: 'app/views/widgets/widget-details-dialog/widget-details.modal.html', + controller: 'WidgetDetailsModalCtrl', + controllerAs: 'widgetDetails', + data: data + }).closePromise.then(needUpdate => { + if(needUpdate.value === true){ + $log.debug('WidgetsCtrl::openWidgetDetailsModal: updating table data...'); + getOnboardingWidgets(); + } + }); + }; + + this.deleteWidget = widget => { + confirmBoxService.deleteItem(widget.name).then(isConfirmed => { + if(isConfirmed){ + if(!widget || !widget.id){ + $log.error('WidgetsCtrl::deleteWidget: No widget or ID... cannot delete'); + return; + } + widgetsService.deleteWidget(widget.id).then(() => { + this.widgetsList.splice(this.widgetsList.indexOf(widget), 1); + }).catch(err => { + $log.error('WidgetsCtrl::deleteWidget error:',err); + }); + } + }).catch(err => { + $log.error('WidgetsCtrl::deleteWidget error:',err); + }); + }; + + init(); + } + } + WidgetsCtrl.$inject = ['$log', 'applicationsService', 'widgetsService', 'ngDialog', 'confirmBoxService', + 'userProfileService','$cookies', '$scope']; + angular.module('ecompApp').controller('WidgetsCtrl', WidgetsCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/widgets/widgets.controller.spec.js b/ecomp-portal-FE/client/app/views/widgets/widgets.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/widgets/widgets.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/widgets/widgets.less b/ecomp-portal-FE/client/app/views/widgets/widgets.less new file mode 100644 index 00000000..4b83b66f --- /dev/null +++ b/ecomp-portal-FE/client/app/views/widgets/widgets.less @@ -0,0 +1,60 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .widgets-page-main{ + .bg_w; + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + overflow-y: @page-main-overflow-y; + padding-left: @padding-left-side; + + .widgets-table{ + width: @table-width; + margin: @table-margin; + + .table-control{ + + } + + .delete-widget{ + .ico_trash_default; + } + } + .error-text { + width: 1170px; + margin: auto; + padding: 20px; + left: 20px; + font-weight: bold; + font-size: 16px; + text-align: left; + color: @err; + background-color: @u; + + .error-help { + color: @o; + font-weight: normal; + } + } + +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/widgets/widgets.tpl.html b/ecomp-portal-FE/client/app/views/widgets/widgets.tpl.html new file mode 100644 index 00000000..d255174f --- /dev/null +++ b/ecomp-portal-FE/client/app/views/widgets/widgets.tpl.html @@ -0,0 +1,77 @@ + +
+
+
+
Widget Onboarding
+
+
+
+
+
+ + +
Add Widget
+
+
+ Only widgets for active applications are displayed. + +
+ + +
+ + + + + + + + + + + + + + + +
{{header.name}}URLDelete
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ecomp-portal-FE/client/assets/images/cloud.png b/ecomp-portal-FE/client/assets/images/cloud.png new file mode 100644 index 00000000..72e2f85c Binary files /dev/null and b/ecomp-portal-FE/client/assets/images/cloud.png differ diff --git a/ecomp-portal-FE/client/assets/images/ecomp_logo.png b/ecomp-portal-FE/client/assets/images/ecomp_logo.png new file mode 100644 index 00000000..609caa42 Binary files /dev/null and b/ecomp-portal-FE/client/assets/images/ecomp_logo.png differ diff --git a/ecomp-portal-FE/client/assets/images/grips.png b/ecomp-portal-FE/client/assets/images/grips.png new file mode 100644 index 00000000..29b92cc5 Binary files /dev/null and b/ecomp-portal-FE/client/assets/images/grips.png differ diff --git a/ecomp-portal-FE/client/assets/images/photo.png b/ecomp-portal-FE/client/assets/images/photo.png new file mode 100644 index 00000000..82603122 Binary files /dev/null and b/ecomp-portal-FE/client/assets/images/photo.png differ diff --git a/ecomp-portal-FE/client/assets/images/spinner.gif b/ecomp-portal-FE/client/assets/images/spinner.gif new file mode 100644 index 00000000..c97ec6ea Binary files /dev/null and b/ecomp-portal-FE/client/assets/images/spinner.gif differ diff --git a/ecomp-portal-FE/client/assets/images/sprite.png b/ecomp-portal-FE/client/assets/images/sprite.png new file mode 100644 index 00000000..92046804 Binary files /dev/null and b/ecomp-portal-FE/client/assets/images/sprite.png differ diff --git a/ecomp-portal-FE/client/bower_components/angular-animate/.bower.json b/ecomp-portal-FE/client/bower_components/angular-animate/.bower.json new file mode 100644 index 00000000..d74d0a71 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-animate/.bower.json @@ -0,0 +1,20 @@ +{ + "name": "angular-animate", + "version": "1.5.0", + "license": "MIT", + "main": "./angular-animate.js", + "ignore": [], + "dependencies": { + "angular": "1.5.0" + }, + "homepage": "https://github.com/angular/bower-angular-animate", + "_release": "1.5.0", + "_resolution": { + "type": "version", + "tag": "v1.5.0", + "commit": "19b035b16ccf4b1db231207b9453f4a8df0d4a09" + }, + "_source": "https://github.com/angular/bower-angular-animate.git", + "_target": "1.5.0", + "_originalSource": "angular-animate" +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-animate/README.md b/ecomp-portal-FE/client/bower_components/angular-animate/README.md new file mode 100644 index 00000000..8313da67 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-animate/README.md @@ -0,0 +1,68 @@ +# packaged angular-animate + +This repo is for distribution on `npm` and `bower`. The source for this module is in the +[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngAnimate). +Please file issues and pull requests against that repo. + +## Install + +You can install this package either with `npm` or with `bower`. + +### npm + +```shell +npm install angular-animate +``` + +Then add `ngAnimate` as a dependency for your app: + +```javascript +angular.module('myApp', [require('angular-animate')]); +``` + +### bower + +```shell +bower install angular-animate +``` + +Then add a ` +``` + +Then add `ngAnimate` as a dependency for your app: + +```javascript +angular.module('myApp', ['ngAnimate']); +``` + +## Documentation + +Documentation is available on the +[AngularJS docs site](http://docs.angularjs.org/api/ngAnimate). + +## License + +The MIT License + +Copyright (c) 2010-2015 Google, Inc. http://angularjs.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ecomp-portal-FE/client/bower_components/angular-animate/angular-animate.js b/ecomp-portal-FE/client/bower_components/angular-animate/angular-animate.js new file mode 100644 index 00000000..2778fc56 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-animate/angular-animate.js @@ -0,0 +1,4121 @@ +/** + * @license AngularJS v1.5.0 + * (c) 2010-2016 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, angular, undefined) {'use strict'; + +/* jshint ignore:start */ +var noop = angular.noop; +var copy = angular.copy; +var extend = angular.extend; +var jqLite = angular.element; +var forEach = angular.forEach; +var isArray = angular.isArray; +var isString = angular.isString; +var isObject = angular.isObject; +var isUndefined = angular.isUndefined; +var isDefined = angular.isDefined; +var isFunction = angular.isFunction; +var isElement = angular.isElement; + +var ELEMENT_NODE = 1; +var COMMENT_NODE = 8; + +var ADD_CLASS_SUFFIX = '-add'; +var REMOVE_CLASS_SUFFIX = '-remove'; +var EVENT_CLASS_PREFIX = 'ng-'; +var ACTIVE_CLASS_SUFFIX = '-active'; +var PREPARE_CLASS_SUFFIX = '-prepare'; + +var NG_ANIMATE_CLASSNAME = 'ng-animate'; +var NG_ANIMATE_CHILDREN_DATA = '$$ngAnimateChildren'; + +// Detect proper transitionend/animationend event names. +var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT; + +// If unprefixed events are not supported but webkit-prefixed are, use the latter. +// Otherwise, just use W3C names, browsers not supporting them at all will just ignore them. +// Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend` +// but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`. +// Register both events in case `window.onanimationend` is not supported because of that, +// do the same for `transitionend` as Safari is likely to exhibit similar behavior. +// Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit +// therefore there is no reason to test anymore for other vendor prefixes: +// http://caniuse.com/#search=transition +if (isUndefined(window.ontransitionend) && isDefined(window.onwebkittransitionend)) { + CSS_PREFIX = '-webkit-'; + TRANSITION_PROP = 'WebkitTransition'; + TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend'; +} else { + TRANSITION_PROP = 'transition'; + TRANSITIONEND_EVENT = 'transitionend'; +} + +if (isUndefined(window.onanimationend) && isDefined(window.onwebkitanimationend)) { + CSS_PREFIX = '-webkit-'; + ANIMATION_PROP = 'WebkitAnimation'; + ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend'; +} else { + ANIMATION_PROP = 'animation'; + ANIMATIONEND_EVENT = 'animationend'; +} + +var DURATION_KEY = 'Duration'; +var PROPERTY_KEY = 'Property'; +var DELAY_KEY = 'Delay'; +var TIMING_KEY = 'TimingFunction'; +var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount'; +var ANIMATION_PLAYSTATE_KEY = 'PlayState'; +var SAFE_FAST_FORWARD_DURATION_VALUE = 9999; + +var ANIMATION_DELAY_PROP = ANIMATION_PROP + DELAY_KEY; +var ANIMATION_DURATION_PROP = ANIMATION_PROP + DURATION_KEY; +var TRANSITION_DELAY_PROP = TRANSITION_PROP + DELAY_KEY; +var TRANSITION_DURATION_PROP = TRANSITION_PROP + DURATION_KEY; + +var isPromiseLike = function(p) { + return p && p.then ? true : false; +}; + +var ngMinErr = angular.$$minErr('ng'); +function assertArg(arg, name, reason) { + if (!arg) { + throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required")); + } + return arg; +} + +function mergeClasses(a,b) { + if (!a && !b) return ''; + if (!a) return b; + if (!b) return a; + if (isArray(a)) a = a.join(' '); + if (isArray(b)) b = b.join(' '); + return a + ' ' + b; +} + +function packageStyles(options) { + var styles = {}; + if (options && (options.to || options.from)) { + styles.to = options.to; + styles.from = options.from; + } + return styles; +} + +function pendClasses(classes, fix, isPrefix) { + var className = ''; + classes = isArray(classes) + ? classes + : classes && isString(classes) && classes.length + ? classes.split(/\s+/) + : []; + forEach(classes, function(klass, i) { + if (klass && klass.length > 0) { + className += (i > 0) ? ' ' : ''; + className += isPrefix ? fix + klass + : klass + fix; + } + }); + return className; +} + +function removeFromArray(arr, val) { + var index = arr.indexOf(val); + if (val >= 0) { + arr.splice(index, 1); + } +} + +function stripCommentsFromElement(element) { + if (element instanceof jqLite) { + switch (element.length) { + case 0: + return []; + break; + + case 1: + // there is no point of stripping anything if the element + // is the only element within the jqLite wrapper. + // (it's important that we retain the element instance.) + if (element[0].nodeType === ELEMENT_NODE) { + return element; + } + break; + + default: + return jqLite(extractElementNode(element)); + break; + } + } + + if (element.nodeType === ELEMENT_NODE) { + return jqLite(element); + } +} + +function extractElementNode(element) { + if (!element[0]) return element; + for (var i = 0; i < element.length; i++) { + var elm = element[i]; + if (elm.nodeType == ELEMENT_NODE) { + return elm; + } + } +} + +function $$addClass($$jqLite, element, className) { + forEach(element, function(elm) { + $$jqLite.addClass(elm, className); + }); +} + +function $$removeClass($$jqLite, element, className) { + forEach(element, function(elm) { + $$jqLite.removeClass(elm, className); + }); +} + +function applyAnimationClassesFactory($$jqLite) { + return function(element, options) { + if (options.addClass) { + $$addClass($$jqLite, element, options.addClass); + options.addClass = null; + } + if (options.removeClass) { + $$removeClass($$jqLite, element, options.removeClass); + options.removeClass = null; + } + } +} + +function prepareAnimationOptions(options) { + options = options || {}; + if (!options.$$prepared) { + var domOperation = options.domOperation || noop; + options.domOperation = function() { + options.$$domOperationFired = true; + domOperation(); + domOperation = noop; + }; + options.$$prepared = true; + } + return options; +} + +function applyAnimationStyles(element, options) { + applyAnimationFromStyles(element, options); + applyAnimationToStyles(element, options); +} + +function applyAnimationFromStyles(element, options) { + if (options.from) { + element.css(options.from); + options.from = null; + } +} + +function applyAnimationToStyles(element, options) { + if (options.to) { + element.css(options.to); + options.to = null; + } +} + +function mergeAnimationDetails(element, oldAnimation, newAnimation) { + var target = oldAnimation.options || {}; + var newOptions = newAnimation.options || {}; + + var toAdd = (target.addClass || '') + ' ' + (newOptions.addClass || ''); + var toRemove = (target.removeClass || '') + ' ' + (newOptions.removeClass || ''); + var classes = resolveElementClasses(element.attr('class'), toAdd, toRemove); + + if (newOptions.preparationClasses) { + target.preparationClasses = concatWithSpace(newOptions.preparationClasses, target.preparationClasses); + delete newOptions.preparationClasses; + } + + // noop is basically when there is no callback; otherwise something has been set + var realDomOperation = target.domOperation !== noop ? target.domOperation : null; + + extend(target, newOptions); + + // TODO(matsko or sreeramu): proper fix is to maintain all animation callback in array and call at last,but now only leave has the callback so no issue with this. + if (realDomOperation) { + target.domOperation = realDomOperation; + } + + if (classes.addClass) { + target.addClass = classes.addClass; + } else { + target.addClass = null; + } + + if (classes.removeClass) { + target.removeClass = classes.removeClass; + } else { + target.removeClass = null; + } + + oldAnimation.addClass = target.addClass; + oldAnimation.removeClass = target.removeClass; + + return target; +} + +function resolveElementClasses(existing, toAdd, toRemove) { + var ADD_CLASS = 1; + var REMOVE_CLASS = -1; + + var flags = {}; + existing = splitClassesToLookup(existing); + + toAdd = splitClassesToLookup(toAdd); + forEach(toAdd, function(value, key) { + flags[key] = ADD_CLASS; + }); + + toRemove = splitClassesToLookup(toRemove); + forEach(toRemove, function(value, key) { + flags[key] = flags[key] === ADD_CLASS ? null : REMOVE_CLASS; + }); + + var classes = { + addClass: '', + removeClass: '' + }; + + forEach(flags, function(val, klass) { + var prop, allow; + if (val === ADD_CLASS) { + prop = 'addClass'; + allow = !existing[klass]; + } else if (val === REMOVE_CLASS) { + prop = 'removeClass'; + allow = existing[klass]; + } + if (allow) { + if (classes[prop].length) { + classes[prop] += ' '; + } + classes[prop] += klass; + } + }); + + function splitClassesToLookup(classes) { + if (isString(classes)) { + classes = classes.split(' '); + } + + var obj = {}; + forEach(classes, function(klass) { + // sometimes the split leaves empty string values + // incase extra spaces were applied to the options + if (klass.length) { + obj[klass] = true; + } + }); + return obj; + } + + return classes; +} + +function getDomNode(element) { + return (element instanceof angular.element) ? element[0] : element; +} + +function applyGeneratedPreparationClasses(element, event, options) { + var classes = ''; + if (event) { + classes = pendClasses(event, EVENT_CLASS_PREFIX, true); + } + if (options.addClass) { + classes = concatWithSpace(classes, pendClasses(options.addClass, ADD_CLASS_SUFFIX)); + } + if (options.removeClass) { + classes = concatWithSpace(classes, pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX)); + } + if (classes.length) { + options.preparationClasses = classes; + element.addClass(classes); + } +} + +function clearGeneratedClasses(element, options) { + if (options.preparationClasses) { + element.removeClass(options.preparationClasses); + options.preparationClasses = null; + } + if (options.activeClasses) { + element.removeClass(options.activeClasses); + options.activeClasses = null; + } +} + +function blockTransitions(node, duration) { + // we use a negative delay value since it performs blocking + // yet it doesn't kill any existing transitions running on the + // same element which makes this safe for class-based animations + var value = duration ? '-' + duration + 's' : ''; + applyInlineStyle(node, [TRANSITION_DELAY_PROP, value]); + return [TRANSITION_DELAY_PROP, value]; +} + +function blockKeyframeAnimations(node, applyBlock) { + var value = applyBlock ? 'paused' : ''; + var key = ANIMATION_PROP + ANIMATION_PLAYSTATE_KEY; + applyInlineStyle(node, [key, value]); + return [key, value]; +} + +function applyInlineStyle(node, styleTuple) { + var prop = styleTuple[0]; + var value = styleTuple[1]; + node.style[prop] = value; +} + +function concatWithSpace(a,b) { + if (!a) return b; + if (!b) return a; + return a + ' ' + b; +} + +var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) { + var queue, cancelFn; + + function scheduler(tasks) { + // we make a copy since RAFScheduler mutates the state + // of the passed in array variable and this would be difficult + // to track down on the outside code + queue = queue.concat(tasks); + nextTick(); + } + + queue = scheduler.queue = []; + + /* waitUntilQuiet does two things: + * 1. It will run the FINAL `fn` value only when an uncanceled RAF has passed through + * 2. It will delay the next wave of tasks from running until the quiet `fn` has run. + * + * The motivation here is that animation code can request more time from the scheduler + * before the next wave runs. This allows for certain DOM properties such as classes to + * be resolved in time for the next animation to run. + */ + scheduler.waitUntilQuiet = function(fn) { + if (cancelFn) cancelFn(); + + cancelFn = $$rAF(function() { + cancelFn = null; + fn(); + nextTick(); + }); + }; + + return scheduler; + + function nextTick() { + if (!queue.length) return; + + var items = queue.shift(); + for (var i = 0; i < items.length; i++) { + items[i](); + } + + if (!cancelFn) { + $$rAF(function() { + if (!cancelFn) nextTick(); + }); + } + } +}]; + +/** + * @ngdoc directive + * @name ngAnimateChildren + * @restrict AE + * @element ANY + * + * @description + * + * ngAnimateChildren allows you to specify that children of this element should animate even if any + * of the children's parents are currently animating. By default, when an element has an active `enter`, `leave`, or `move` + * (structural) animation, child elements that also have an active structural animation are not animated. + * + * Note that even if `ngAnimteChildren` is set, no child animations will run when the parent element is removed from the DOM (`leave` animation). + * + * + * @param {string} ngAnimateChildren If the value is empty, `true` or `on`, + * then child animations are allowed. If the value is `false`, child animations are not allowed. + * + * @example + * + +
+ + +
+
+
+ List of items: +
Item {{item}}
+
+
+
+
+ + + .container.ng-enter, + .container.ng-leave { + transition: all ease 1.5s; + } + + .container.ng-enter, + .container.ng-leave-active { + opacity: 0; + } + + .container.ng-leave, + .container.ng-enter-active { + opacity: 1; + } + + .item { + background: firebrick; + color: #FFF; + margin-bottom: 10px; + } + + .item.ng-enter, + .item.ng-leave { + transition: transform 1.5s ease; + } + + .item.ng-enter { + transform: translateX(50px); + } + + .item.ng-enter-active { + transform: translateX(0); + } + + + angular.module('ngAnimateChildren', ['ngAnimate']) + .controller('mainController', function() { + this.animateChildren = false; + this.enterElement = false; + }); + +
+ */ +var $$AnimateChildrenDirective = ['$interpolate', function($interpolate) { + return { + link: function(scope, element, attrs) { + var val = attrs.ngAnimateChildren; + if (angular.isString(val) && val.length === 0) { //empty attribute + element.data(NG_ANIMATE_CHILDREN_DATA, true); + } else { + // Interpolate and set the value, so that it is available to + // animations that run right after compilation + setData($interpolate(val)(scope)); + attrs.$observe('ngAnimateChildren', setData); + } + + function setData(value) { + value = value === 'on' || value === 'true'; + element.data(NG_ANIMATE_CHILDREN_DATA, value); + } + } + }; +}]; + +var ANIMATE_TIMER_KEY = '$$animateCss'; + +/** + * @ngdoc service + * @name $animateCss + * @kind object + * + * @description + * The `$animateCss` service is a useful utility to trigger customized CSS-based transitions/keyframes + * from a JavaScript-based animation or directly from a directive. The purpose of `$animateCss` is NOT + * to side-step how `$animate` and ngAnimate work, but the goal is to allow pre-existing animations or + * directives to create more complex animations that can be purely driven using CSS code. + * + * Note that only browsers that support CSS transitions and/or keyframe animations are capable of + * rendering animations triggered via `$animateCss` (bad news for IE9 and lower). + * + * ## Usage + * Once again, `$animateCss` is designed to be used inside of a registered JavaScript animation that + * is powered by ngAnimate. It is possible to use `$animateCss` directly inside of a directive, however, + * any automatic control over cancelling animations and/or preventing animations from being run on + * child elements will not be handled by Angular. For this to work as expected, please use `$animate` to + * trigger the animation and then setup a JavaScript animation that injects `$animateCss` to trigger + * the CSS animation. + * + * The example below shows how we can create a folding animation on an element using `ng-if`: + * + * ```html + * + *
+ * This element will go BOOM + *
+ * + * ``` + * + * Now we create the **JavaScript animation** that will trigger the CSS transition: + * + * ```js + * ngModule.animation('.fold-animation', ['$animateCss', function($animateCss) { + * return { + * enter: function(element, doneFn) { + * var height = element[0].offsetHeight; + * return $animateCss(element, { + * from: { height:'0px' }, + * to: { height:height + 'px' }, + * duration: 1 // one second + * }); + * } + * } + * }]); + * ``` + * + * ## More Advanced Uses + * + * `$animateCss` is the underlying code that ngAnimate uses to power **CSS-based animations** behind the scenes. Therefore CSS hooks + * like `.ng-EVENT`, `.ng-EVENT-active`, `.ng-EVENT-stagger` are all features that can be triggered using `$animateCss` via JavaScript code. + * + * This also means that just about any combination of adding classes, removing classes, setting styles, dynamically setting a keyframe animation, + * applying a hardcoded duration or delay value, changing the animation easing or applying a stagger animation are all options that work with + * `$animateCss`. The service itself is smart enough to figure out the combination of options and examine the element styling properties in order + * to provide a working animation that will run in CSS. + * + * The example below showcases a more advanced version of the `.fold-animation` from the example above: + * + * ```js + * ngModule.animation('.fold-animation', ['$animateCss', function($animateCss) { + * return { + * enter: function(element, doneFn) { + * var height = element[0].offsetHeight; + * return $animateCss(element, { + * addClass: 'red large-text pulse-twice', + * easing: 'ease-out', + * from: { height:'0px' }, + * to: { height:height + 'px' }, + * duration: 1 // one second + * }); + * } + * } + * }]); + * ``` + * + * Since we're adding/removing CSS classes then the CSS transition will also pick those up: + * + * ```css + * /* since a hardcoded duration value of 1 was provided in the JavaScript animation code, + * the CSS classes below will be transitioned despite them being defined as regular CSS classes */ + * .red { background:red; } + * .large-text { font-size:20px; } + * + * /* we can also use a keyframe animation and $animateCss will make it work alongside the transition */ + * .pulse-twice { + * animation: 0.5s pulse linear 2; + * -webkit-animation: 0.5s pulse linear 2; + * } + * + * @keyframes pulse { + * from { transform: scale(0.5); } + * to { transform: scale(1.5); } + * } + * + * @-webkit-keyframes pulse { + * from { -webkit-transform: scale(0.5); } + * to { -webkit-transform: scale(1.5); } + * } + * ``` + * + * Given this complex combination of CSS classes, styles and options, `$animateCss` will figure everything out and make the animation happen. + * + * ## How the Options are handled + * + * `$animateCss` is very versatile and intelligent when it comes to figuring out what configurations to apply to the element to ensure the animation + * works with the options provided. Say for example we were adding a class that contained a keyframe value and we wanted to also animate some inline + * styles using the `from` and `to` properties. + * + * ```js + * var animator = $animateCss(element, { + * from: { background:'red' }, + * to: { background:'blue' } + * }); + * animator.start(); + * ``` + * + * ```css + * .rotating-animation { + * animation:0.5s rotate linear; + * -webkit-animation:0.5s rotate linear; + * } + * + * @keyframes rotate { + * from { transform: rotate(0deg); } + * to { transform: rotate(360deg); } + * } + * + * @-webkit-keyframes rotate { + * from { -webkit-transform: rotate(0deg); } + * to { -webkit-transform: rotate(360deg); } + * } + * ``` + * + * The missing pieces here are that we do not have a transition set (within the CSS code nor within the `$animateCss` options) and the duration of the animation is + * going to be detected from what the keyframe styles on the CSS class are. In this event, `$animateCss` will automatically create an inline transition + * style matching the duration detected from the keyframe style (which is present in the CSS class that is being added) and then prepare both the transition + * and keyframe animations to run in parallel on the element. Then when the animation is underway the provided `from` and `to` CSS styles will be applied + * and spread across the transition and keyframe animation. + * + * ## What is returned + * + * `$animateCss` works in two stages: a preparation phase and an animation phase. Therefore when `$animateCss` is first called it will NOT actually + * start the animation. All that is going on here is that the element is being prepared for the animation (which means that the generated CSS classes are + * added and removed on the element). Once `$animateCss` is called it will return an object with the following properties: + * + * ```js + * var animator = $animateCss(element, { ... }); + * ``` + * + * Now what do the contents of our `animator` variable look like: + * + * ```js + * { + * // starts the animation + * start: Function, + * + * // ends (aborts) the animation + * end: Function + * } + * ``` + * + * To actually start the animation we need to run `animation.start()` which will then return a promise that we can hook into to detect when the animation ends. + * If we choose not to run the animation then we MUST run `animation.end()` to perform a cleanup on the element (since some CSS classes and styles may have been + * applied to the element during the preparation phase). Note that all other properties such as duration, delay, transitions and keyframes are just properties + * and that changing them will not reconfigure the parameters of the animation. + * + * ### runner.done() vs runner.then() + * It is documented that `animation.start()` will return a promise object and this is true, however, there is also an additional method available on the + * runner called `.done(callbackFn)`. The done method works the same as `.finally(callbackFn)`, however, it does **not trigger a digest to occur**. + * Therefore, for performance reasons, it's always best to use `runner.done(callback)` instead of `runner.then()`, `runner.catch()` or `runner.finally()` + * unless you really need a digest to kick off afterwards. + * + * Keep in mind that, to make this easier, ngAnimate has tweaked the JS animations API to recognize when a runner instance is returned from $animateCss + * (so there is no need to call `runner.done(doneFn)` inside of your JavaScript animation code). + * Check the {@link ngAnimate.$animateCss#usage animation code above} to see how this works. + * + * @param {DOMElement} element the element that will be animated + * @param {object} options the animation-related options that will be applied during the animation + * + * * `event` - The DOM event (e.g. enter, leave, move). When used, a generated CSS class of `ng-EVENT` and `ng-EVENT-active` will be applied + * to the element during the animation. Multiple events can be provided when spaces are used as a separator. (Note that this will not perform any DOM operation.) + * * `structural` - Indicates that the `ng-` prefix will be added to the event class. Setting to `false` or omitting will turn `ng-EVENT` and + * `ng-EVENT-active` in `EVENT` and `EVENT-active`. Unused if `event` is omitted. + * * `easing` - The CSS easing value that will be applied to the transition or keyframe animation (or both). + * * `transitionStyle` - The raw CSS transition style that will be used (e.g. `1s linear all`). + * * `keyframeStyle` - The raw CSS keyframe animation style that will be used (e.g. `1s my_animation linear`). + * * `from` - The starting CSS styles (a key/value object) that will be applied at the start of the animation. + * * `to` - The ending CSS styles (a key/value object) that will be applied across the animation via a CSS transition. + * * `addClass` - A space separated list of CSS classes that will be added to the element and spread across the animation. + * * `removeClass` - A space separated list of CSS classes that will be removed from the element and spread across the animation. + * * `duration` - A number value representing the total duration of the transition and/or keyframe (note that a value of 1 is 1000ms). If a value of `0` + * is provided then the animation will be skipped entirely. + * * `delay` - A number value representing the total delay of the transition and/or keyframe (note that a value of 1 is 1000ms). If a value of `true` is + * used then whatever delay value is detected from the CSS classes will be mirrored on the elements styles (e.g. by setting delay true then the style value + * of the element will be `transition-delay: DETECTED_VALUE`). Using `true` is useful when you want the CSS classes and inline styles to all share the same + * CSS delay value. + * * `stagger` - A numeric time value representing the delay between successively animated elements + * ({@link ngAnimate#css-staggering-animations Click here to learn how CSS-based staggering works in ngAnimate.}) + * * `staggerIndex` - The numeric index representing the stagger item (e.g. a value of 5 is equal to the sixth item in the stagger; therefore when a + * `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`) + * * `applyClassesEarly` - Whether or not the classes being added or removed will be used when detecting the animation. This is set by `$animate` when enter/leave/move animations are fired to ensure that the CSS classes are resolved in time. (Note that this will prevent any transitions from occurring on the classes being added and removed.) + * * `cleanupStyles` - Whether or not the provided `from` and `to` styles will be removed once + * the animation is closed. This is useful for when the styles are used purely for the sake of + * the animation and do not have a lasting visual effect on the element (e.g. a collapse and open animation). + * By default this value is set to `false`. + * + * @return {object} an object with start and end methods and details about the animation. + * + * * `start` - The method to start the animation. This will return a `Promise` when called. + * * `end` - This method will cancel the animation and remove all applied CSS classes and styles. + */ +var ONE_SECOND = 1000; +var BASE_TEN = 10; + +var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3; +var CLOSING_TIME_BUFFER = 1.5; + +var DETECT_CSS_PROPERTIES = { + transitionDuration: TRANSITION_DURATION_PROP, + transitionDelay: TRANSITION_DELAY_PROP, + transitionProperty: TRANSITION_PROP + PROPERTY_KEY, + animationDuration: ANIMATION_DURATION_PROP, + animationDelay: ANIMATION_DELAY_PROP, + animationIterationCount: ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY +}; + +var DETECT_STAGGER_CSS_PROPERTIES = { + transitionDuration: TRANSITION_DURATION_PROP, + transitionDelay: TRANSITION_DELAY_PROP, + animationDuration: ANIMATION_DURATION_PROP, + animationDelay: ANIMATION_DELAY_PROP +}; + +function getCssKeyframeDurationStyle(duration) { + return [ANIMATION_DURATION_PROP, duration + 's']; +} + +function getCssDelayStyle(delay, isKeyframeAnimation) { + var prop = isKeyframeAnimation ? ANIMATION_DELAY_PROP : TRANSITION_DELAY_PROP; + return [prop, delay + 's']; +} + +function computeCssStyles($window, element, properties) { + var styles = Object.create(null); + var detectedStyles = $window.getComputedStyle(element) || {}; + forEach(properties, function(formalStyleName, actualStyleName) { + var val = detectedStyles[formalStyleName]; + if (val) { + var c = val.charAt(0); + + // only numerical-based values have a negative sign or digit as the first value + if (c === '-' || c === '+' || c >= 0) { + val = parseMaxTime(val); + } + + // by setting this to null in the event that the delay is not set or is set directly as 0 + // then we can still allow for negative values to be used later on and not mistake this + // value for being greater than any other negative value. + if (val === 0) { + val = null; + } + styles[actualStyleName] = val; + } + }); + + return styles; +} + +function parseMaxTime(str) { + var maxValue = 0; + var values = str.split(/\s*,\s*/); + forEach(values, function(value) { + // it's always safe to consider only second values and omit `ms` values since + // getComputedStyle will always handle the conversion for us + if (value.charAt(value.length - 1) == 's') { + value = value.substring(0, value.length - 1); + } + value = parseFloat(value) || 0; + maxValue = maxValue ? Math.max(value, maxValue) : value; + }); + return maxValue; +} + +function truthyTimingValue(val) { + return val === 0 || val != null; +} + +function getCssTransitionDurationStyle(duration, applyOnlyDuration) { + var style = TRANSITION_PROP; + var value = duration + 's'; + if (applyOnlyDuration) { + style += DURATION_KEY; + } else { + value += ' linear all'; + } + return [style, value]; +} + +function createLocalCacheLookup() { + var cache = Object.create(null); + return { + flush: function() { + cache = Object.create(null); + }, + + count: function(key) { + var entry = cache[key]; + return entry ? entry.total : 0; + }, + + get: function(key) { + var entry = cache[key]; + return entry && entry.value; + }, + + put: function(key, value) { + if (!cache[key]) { + cache[key] = { total: 1, value: value }; + } else { + cache[key].total++; + } + } + }; +} + +// we do not reassign an already present style value since +// if we detect the style property value again we may be +// detecting styles that were added via the `from` styles. +// We make use of `isDefined` here since an empty string +// or null value (which is what getPropertyValue will return +// for a non-existing style) will still be marked as a valid +// value for the style (a falsy value implies that the style +// is to be removed at the end of the animation). If we had a simple +// "OR" statement then it would not be enough to catch that. +function registerRestorableStyles(backup, node, properties) { + forEach(properties, function(prop) { + backup[prop] = isDefined(backup[prop]) + ? backup[prop] + : node.style.getPropertyValue(prop); + }); +} + +var $AnimateCssProvider = ['$animateProvider', function($animateProvider) { + var gcsLookup = createLocalCacheLookup(); + var gcsStaggerLookup = createLocalCacheLookup(); + + this.$get = ['$window', '$$jqLite', '$$AnimateRunner', '$timeout', + '$$forceReflow', '$sniffer', '$$rAFScheduler', '$$animateQueue', + function($window, $$jqLite, $$AnimateRunner, $timeout, + $$forceReflow, $sniffer, $$rAFScheduler, $$animateQueue) { + + var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); + + var parentCounter = 0; + function gcsHashFn(node, extraClasses) { + var KEY = "$$ngAnimateParentKey"; + var parentNode = node.parentNode; + var parentID = parentNode[KEY] || (parentNode[KEY] = ++parentCounter); + return parentID + '-' + node.getAttribute('class') + '-' + extraClasses; + } + + function computeCachedCssStyles(node, className, cacheKey, properties) { + var timings = gcsLookup.get(cacheKey); + + if (!timings) { + timings = computeCssStyles($window, node, properties); + if (timings.animationIterationCount === 'infinite') { + timings.animationIterationCount = 1; + } + } + + // we keep putting this in multiple times even though the value and the cacheKey are the same + // because we're keeping an internal tally of how many duplicate animations are detected. + gcsLookup.put(cacheKey, timings); + return timings; + } + + function computeCachedCssStaggerStyles(node, className, cacheKey, properties) { + var stagger; + + // if we have one or more existing matches of matching elements + // containing the same parent + CSS styles (which is how cacheKey works) + // then staggering is possible + if (gcsLookup.count(cacheKey) > 0) { + stagger = gcsStaggerLookup.get(cacheKey); + + if (!stagger) { + var staggerClassName = pendClasses(className, '-stagger'); + + $$jqLite.addClass(node, staggerClassName); + + stagger = computeCssStyles($window, node, properties); + + // force the conversion of a null value to zero incase not set + stagger.animationDuration = Math.max(stagger.animationDuration, 0); + stagger.transitionDuration = Math.max(stagger.transitionDuration, 0); + + $$jqLite.removeClass(node, staggerClassName); + + gcsStaggerLookup.put(cacheKey, stagger); + } + } + + return stagger || {}; + } + + var cancelLastRAFRequest; + var rafWaitQueue = []; + function waitUntilQuiet(callback) { + rafWaitQueue.push(callback); + $$rAFScheduler.waitUntilQuiet(function() { + gcsLookup.flush(); + gcsStaggerLookup.flush(); + + // DO NOT REMOVE THIS LINE OR REFACTOR OUT THE `pageWidth` variable. + // PLEASE EXAMINE THE `$$forceReflow` service to understand why. + var pageWidth = $$forceReflow(); + + // we use a for loop to ensure that if the queue is changed + // during this looping then it will consider new requests + for (var i = 0; i < rafWaitQueue.length; i++) { + rafWaitQueue[i](pageWidth); + } + rafWaitQueue.length = 0; + }); + } + + function computeTimings(node, className, cacheKey) { + var timings = computeCachedCssStyles(node, className, cacheKey, DETECT_CSS_PROPERTIES); + var aD = timings.animationDelay; + var tD = timings.transitionDelay; + timings.maxDelay = aD && tD + ? Math.max(aD, tD) + : (aD || tD); + timings.maxDuration = Math.max( + timings.animationDuration * timings.animationIterationCount, + timings.transitionDuration); + + return timings; + } + + return function init(element, initialOptions) { + // all of the animation functions should create + // a copy of the options data, however, if a + // parent service has already created a copy then + // we should stick to using that + var options = initialOptions || {}; + if (!options.$$prepared) { + options = prepareAnimationOptions(copy(options)); + } + + var restoreStyles = {}; + var node = getDomNode(element); + if (!node + || !node.parentNode + || !$$animateQueue.enabled()) { + return closeAndReturnNoopAnimator(); + } + + var temporaryStyles = []; + var classes = element.attr('class'); + var styles = packageStyles(options); + var animationClosed; + var animationPaused; + var animationCompleted; + var runner; + var runnerHost; + var maxDelay; + var maxDelayTime; + var maxDuration; + var maxDurationTime; + var startTime; + var events = []; + + if (options.duration === 0 || (!$sniffer.animations && !$sniffer.transitions)) { + return closeAndReturnNoopAnimator(); + } + + var method = options.event && isArray(options.event) + ? options.event.join(' ') + : options.event; + + var isStructural = method && options.structural; + var structuralClassName = ''; + var addRemoveClassName = ''; + + if (isStructural) { + structuralClassName = pendClasses(method, EVENT_CLASS_PREFIX, true); + } else if (method) { + structuralClassName = method; + } + + if (options.addClass) { + addRemoveClassName += pendClasses(options.addClass, ADD_CLASS_SUFFIX); + } + + if (options.removeClass) { + if (addRemoveClassName.length) { + addRemoveClassName += ' '; + } + addRemoveClassName += pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX); + } + + // there may be a situation where a structural animation is combined together + // with CSS classes that need to resolve before the animation is computed. + // However this means that there is no explicit CSS code to block the animation + // from happening (by setting 0s none in the class name). If this is the case + // we need to apply the classes before the first rAF so we know to continue if + // there actually is a detected transition or keyframe animation + if (options.applyClassesEarly && addRemoveClassName.length) { + applyAnimationClasses(element, options); + } + + var preparationClasses = [structuralClassName, addRemoveClassName].join(' ').trim(); + var fullClassName = classes + ' ' + preparationClasses; + var activeClasses = pendClasses(preparationClasses, ACTIVE_CLASS_SUFFIX); + var hasToStyles = styles.to && Object.keys(styles.to).length > 0; + var containsKeyframeAnimation = (options.keyframeStyle || '').length > 0; + + // there is no way we can trigger an animation if no styles and + // no classes are being applied which would then trigger a transition, + // unless there a is raw keyframe value that is applied to the element. + if (!containsKeyframeAnimation + && !hasToStyles + && !preparationClasses) { + return closeAndReturnNoopAnimator(); + } + + var cacheKey, stagger; + if (options.stagger > 0) { + var staggerVal = parseFloat(options.stagger); + stagger = { + transitionDelay: staggerVal, + animationDelay: staggerVal, + transitionDuration: 0, + animationDuration: 0 + }; + } else { + cacheKey = gcsHashFn(node, fullClassName); + stagger = computeCachedCssStaggerStyles(node, preparationClasses, cacheKey, DETECT_STAGGER_CSS_PROPERTIES); + } + + if (!options.$$skipPreparationClasses) { + $$jqLite.addClass(element, preparationClasses); + } + + var applyOnlyDuration; + + if (options.transitionStyle) { + var transitionStyle = [TRANSITION_PROP, options.transitionStyle]; + applyInlineStyle(node, transitionStyle); + temporaryStyles.push(transitionStyle); + } + + if (options.duration >= 0) { + applyOnlyDuration = node.style[TRANSITION_PROP].length > 0; + var durationStyle = getCssTransitionDurationStyle(options.duration, applyOnlyDuration); + + // we set the duration so that it will be picked up by getComputedStyle later + applyInlineStyle(node, durationStyle); + temporaryStyles.push(durationStyle); + } + + if (options.keyframeStyle) { + var keyframeStyle = [ANIMATION_PROP, options.keyframeStyle]; + applyInlineStyle(node, keyframeStyle); + temporaryStyles.push(keyframeStyle); + } + + var itemIndex = stagger + ? options.staggerIndex >= 0 + ? options.staggerIndex + : gcsLookup.count(cacheKey) + : 0; + + var isFirst = itemIndex === 0; + + // this is a pre-emptive way of forcing the setup classes to be added and applied INSTANTLY + // without causing any combination of transitions to kick in. By adding a negative delay value + // it forces the setup class' transition to end immediately. We later then remove the negative + // transition delay to allow for the transition to naturally do it's thing. The beauty here is + // that if there is no transition defined then nothing will happen and this will also allow + // other transitions to be stacked on top of each other without any chopping them out. + if (isFirst && !options.skipBlocking) { + blockTransitions(node, SAFE_FAST_FORWARD_DURATION_VALUE); + } + + var timings = computeTimings(node, fullClassName, cacheKey); + var relativeDelay = timings.maxDelay; + maxDelay = Math.max(relativeDelay, 0); + maxDuration = timings.maxDuration; + + var flags = {}; + flags.hasTransitions = timings.transitionDuration > 0; + flags.hasAnimations = timings.animationDuration > 0; + flags.hasTransitionAll = flags.hasTransitions && timings.transitionProperty == 'all'; + flags.applyTransitionDuration = hasToStyles && ( + (flags.hasTransitions && !flags.hasTransitionAll) + || (flags.hasAnimations && !flags.hasTransitions)); + flags.applyAnimationDuration = options.duration && flags.hasAnimations; + flags.applyTransitionDelay = truthyTimingValue(options.delay) && (flags.applyTransitionDuration || flags.hasTransitions); + flags.applyAnimationDelay = truthyTimingValue(options.delay) && flags.hasAnimations; + flags.recalculateTimingStyles = addRemoveClassName.length > 0; + + if (flags.applyTransitionDuration || flags.applyAnimationDuration) { + maxDuration = options.duration ? parseFloat(options.duration) : maxDuration; + + if (flags.applyTransitionDuration) { + flags.hasTransitions = true; + timings.transitionDuration = maxDuration; + applyOnlyDuration = node.style[TRANSITION_PROP + PROPERTY_KEY].length > 0; + temporaryStyles.push(getCssTransitionDurationStyle(maxDuration, applyOnlyDuration)); + } + + if (flags.applyAnimationDuration) { + flags.hasAnimations = true; + timings.animationDuration = maxDuration; + temporaryStyles.push(getCssKeyframeDurationStyle(maxDuration)); + } + } + + if (maxDuration === 0 && !flags.recalculateTimingStyles) { + return closeAndReturnNoopAnimator(); + } + + if (options.delay != null) { + var delayStyle; + if (typeof options.delay !== "boolean") { + delayStyle = parseFloat(options.delay); + // number in options.delay means we have to recalculate the delay for the closing timeout + maxDelay = Math.max(delayStyle, 0); + } + + if (flags.applyTransitionDelay) { + temporaryStyles.push(getCssDelayStyle(delayStyle)); + } + + if (flags.applyAnimationDelay) { + temporaryStyles.push(getCssDelayStyle(delayStyle, true)); + } + } + + // we need to recalculate the delay value since we used a pre-emptive negative + // delay value and the delay value is required for the final event checking. This + // property will ensure that this will happen after the RAF phase has passed. + if (options.duration == null && timings.transitionDuration > 0) { + flags.recalculateTimingStyles = flags.recalculateTimingStyles || isFirst; + } + + maxDelayTime = maxDelay * ONE_SECOND; + maxDurationTime = maxDuration * ONE_SECOND; + if (!options.skipBlocking) { + flags.blockTransition = timings.transitionDuration > 0; + flags.blockKeyframeAnimation = timings.animationDuration > 0 && + stagger.animationDelay > 0 && + stagger.animationDuration === 0; + } + + if (options.from) { + if (options.cleanupStyles) { + registerRestorableStyles(restoreStyles, node, Object.keys(options.from)); + } + applyAnimationFromStyles(element, options); + } + + if (flags.blockTransition || flags.blockKeyframeAnimation) { + applyBlocking(maxDuration); + } else if (!options.skipBlocking) { + blockTransitions(node, false); + } + + // TODO(matsko): for 1.5 change this code to have an animator object for better debugging + return { + $$willAnimate: true, + end: endFn, + start: function() { + if (animationClosed) return; + + runnerHost = { + end: endFn, + cancel: cancelFn, + resume: null, //this will be set during the start() phase + pause: null + }; + + runner = new $$AnimateRunner(runnerHost); + + waitUntilQuiet(start); + + // we don't have access to pause/resume the animation + // since it hasn't run yet. AnimateRunner will therefore + // set noop functions for resume and pause and they will + // later be overridden once the animation is triggered + return runner; + } + }; + + function endFn() { + close(); + } + + function cancelFn() { + close(true); + } + + function close(rejected) { // jshint ignore:line + // if the promise has been called already then we shouldn't close + // the animation again + if (animationClosed || (animationCompleted && animationPaused)) return; + animationClosed = true; + animationPaused = false; + + if (!options.$$skipPreparationClasses) { + $$jqLite.removeClass(element, preparationClasses); + } + $$jqLite.removeClass(element, activeClasses); + + blockKeyframeAnimations(node, false); + blockTransitions(node, false); + + forEach(temporaryStyles, function(entry) { + // There is only one way to remove inline style properties entirely from elements. + // By using `removeProperty` this works, but we need to convert camel-cased CSS + // styles down to hyphenated values. + node.style[entry[0]] = ''; + }); + + applyAnimationClasses(element, options); + applyAnimationStyles(element, options); + + if (Object.keys(restoreStyles).length) { + forEach(restoreStyles, function(value, prop) { + value ? node.style.setProperty(prop, value) + : node.style.removeProperty(prop); + }); + } + + // the reason why we have this option is to allow a synchronous closing callback + // that is fired as SOON as the animation ends (when the CSS is removed) or if + // the animation never takes off at all. A good example is a leave animation since + // the element must be removed just after the animation is over or else the element + // will appear on screen for one animation frame causing an overbearing flicker. + if (options.onDone) { + options.onDone(); + } + + if (events && events.length) { + // Remove the transitionend / animationend listener(s) + element.off(events.join(' '), onAnimationProgress); + } + + //Cancel the fallback closing timeout and remove the timer data + var animationTimerData = element.data(ANIMATE_TIMER_KEY); + if (animationTimerData) { + $timeout.cancel(animationTimerData[0].timer); + element.removeData(ANIMATE_TIMER_KEY); + } + + // if the preparation function fails then the promise is not setup + if (runner) { + runner.complete(!rejected); + } + } + + function applyBlocking(duration) { + if (flags.blockTransition) { + blockTransitions(node, duration); + } + + if (flags.blockKeyframeAnimation) { + blockKeyframeAnimations(node, !!duration); + } + } + + function closeAndReturnNoopAnimator() { + runner = new $$AnimateRunner({ + end: endFn, + cancel: cancelFn + }); + + // should flush the cache animation + waitUntilQuiet(noop); + close(); + + return { + $$willAnimate: false, + start: function() { + return runner; + }, + end: endFn + }; + } + + function onAnimationProgress(event) { + event.stopPropagation(); + var ev = event.originalEvent || event; + + // we now always use `Date.now()` due to the recent changes with + // event.timeStamp in Firefox, Webkit and Chrome (see #13494 for more info) + var timeStamp = ev.$manualTimeStamp || Date.now(); + + /* Firefox (or possibly just Gecko) likes to not round values up + * when a ms measurement is used for the animation */ + var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES)); + + /* $manualTimeStamp is a mocked timeStamp value which is set + * within browserTrigger(). This is only here so that tests can + * mock animations properly. Real events fallback to event.timeStamp, + * or, if they don't, then a timeStamp is automatically created for them. + * We're checking to see if the timeStamp surpasses the expected delay, + * but we're using elapsedTime instead of the timeStamp on the 2nd + * pre-condition since animationPauseds sometimes close off early */ + if (Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) { + // we set this flag to ensure that if the transition is paused then, when resumed, + // the animation will automatically close itself since transitions cannot be paused. + animationCompleted = true; + close(); + } + } + + function start() { + if (animationClosed) return; + if (!node.parentNode) { + close(); + return; + } + + // even though we only pause keyframe animations here the pause flag + // will still happen when transitions are used. Only the transition will + // not be paused since that is not possible. If the animation ends when + // paused then it will not complete until unpaused or cancelled. + var playPause = function(playAnimation) { + if (!animationCompleted) { + animationPaused = !playAnimation; + if (timings.animationDuration) { + var value = blockKeyframeAnimations(node, animationPaused); + animationPaused + ? temporaryStyles.push(value) + : removeFromArray(temporaryStyles, value); + } + } else if (animationPaused && playAnimation) { + animationPaused = false; + close(); + } + }; + + // checking the stagger duration prevents an accidentally cascade of the CSS delay style + // being inherited from the parent. If the transition duration is zero then we can safely + // rely that the delay value is an intentional stagger delay style. + var maxStagger = itemIndex > 0 + && ((timings.transitionDuration && stagger.transitionDuration === 0) || + (timings.animationDuration && stagger.animationDuration === 0)) + && Math.max(stagger.animationDelay, stagger.transitionDelay); + if (maxStagger) { + $timeout(triggerAnimationStart, + Math.floor(maxStagger * itemIndex * ONE_SECOND), + false); + } else { + triggerAnimationStart(); + } + + // this will decorate the existing promise runner with pause/resume methods + runnerHost.resume = function() { + playPause(true); + }; + + runnerHost.pause = function() { + playPause(false); + }; + + function triggerAnimationStart() { + // just incase a stagger animation kicks in when the animation + // itself was cancelled entirely + if (animationClosed) return; + + applyBlocking(false); + + forEach(temporaryStyles, function(entry) { + var key = entry[0]; + var value = entry[1]; + node.style[key] = value; + }); + + applyAnimationClasses(element, options); + $$jqLite.addClass(element, activeClasses); + + if (flags.recalculateTimingStyles) { + fullClassName = node.className + ' ' + preparationClasses; + cacheKey = gcsHashFn(node, fullClassName); + + timings = computeTimings(node, fullClassName, cacheKey); + relativeDelay = timings.maxDelay; + maxDelay = Math.max(relativeDelay, 0); + maxDuration = timings.maxDuration; + + if (maxDuration === 0) { + close(); + return; + } + + flags.hasTransitions = timings.transitionDuration > 0; + flags.hasAnimations = timings.animationDuration > 0; + } + + if (flags.applyAnimationDelay) { + relativeDelay = typeof options.delay !== "boolean" && truthyTimingValue(options.delay) + ? parseFloat(options.delay) + : relativeDelay; + + maxDelay = Math.max(relativeDelay, 0); + timings.animationDelay = relativeDelay; + delayStyle = getCssDelayStyle(relativeDelay, true); + temporaryStyles.push(delayStyle); + node.style[delayStyle[0]] = delayStyle[1]; + } + + maxDelayTime = maxDelay * ONE_SECOND; + maxDurationTime = maxDuration * ONE_SECOND; + + if (options.easing) { + var easeProp, easeVal = options.easing; + if (flags.hasTransitions) { + easeProp = TRANSITION_PROP + TIMING_KEY; + temporaryStyles.push([easeProp, easeVal]); + node.style[easeProp] = easeVal; + } + if (flags.hasAnimations) { + easeProp = ANIMATION_PROP + TIMING_KEY; + temporaryStyles.push([easeProp, easeVal]); + node.style[easeProp] = easeVal; + } + } + + if (timings.transitionDuration) { + events.push(TRANSITIONEND_EVENT); + } + + if (timings.animationDuration) { + events.push(ANIMATIONEND_EVENT); + } + + startTime = Date.now(); + var timerTime = maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime; + var endTime = startTime + timerTime; + + var animationsData = element.data(ANIMATE_TIMER_KEY) || []; + var setupFallbackTimer = true; + if (animationsData.length) { + var currentTimerData = animationsData[0]; + setupFallbackTimer = endTime > currentTimerData.expectedEndTime; + if (setupFallbackTimer) { + $timeout.cancel(currentTimerData.timer); + } else { + animationsData.push(close); + } + } + + if (setupFallbackTimer) { + var timer = $timeout(onAnimationExpired, timerTime, false); + animationsData[0] = { + timer: timer, + expectedEndTime: endTime + }; + animationsData.push(close); + element.data(ANIMATE_TIMER_KEY, animationsData); + } + + if (events.length) { + element.on(events.join(' '), onAnimationProgress); + } + + if (options.to) { + if (options.cleanupStyles) { + registerRestorableStyles(restoreStyles, node, Object.keys(options.to)); + } + applyAnimationToStyles(element, options); + } + } + + function onAnimationExpired() { + var animationsData = element.data(ANIMATE_TIMER_KEY); + + // this will be false in the event that the element was + // removed from the DOM (via a leave animation or something + // similar) + if (animationsData) { + for (var i = 1; i < animationsData.length; i++) { + animationsData[i](); + } + element.removeData(ANIMATE_TIMER_KEY); + } + } + } + }; + }]; +}]; + +var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationProvider) { + $$animationProvider.drivers.push('$$animateCssDriver'); + + var NG_ANIMATE_SHIM_CLASS_NAME = 'ng-animate-shim'; + var NG_ANIMATE_ANCHOR_CLASS_NAME = 'ng-anchor'; + + var NG_OUT_ANCHOR_CLASS_NAME = 'ng-anchor-out'; + var NG_IN_ANCHOR_CLASS_NAME = 'ng-anchor-in'; + + function isDocumentFragment(node) { + return node.parentNode && node.parentNode.nodeType === 11; + } + + this.$get = ['$animateCss', '$rootScope', '$$AnimateRunner', '$rootElement', '$sniffer', '$$jqLite', '$document', + function($animateCss, $rootScope, $$AnimateRunner, $rootElement, $sniffer, $$jqLite, $document) { + + // only browsers that support these properties can render animations + if (!$sniffer.animations && !$sniffer.transitions) return noop; + + var bodyNode = $document[0].body; + var rootNode = getDomNode($rootElement); + + var rootBodyElement = jqLite( + // this is to avoid using something that exists outside of the body + // we also special case the doc fragment case because our unit test code + // appends the $rootElement to the body after the app has been bootstrapped + isDocumentFragment(rootNode) || bodyNode.contains(rootNode) ? rootNode : bodyNode + ); + + var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); + + return function initDriverFn(animationDetails) { + return animationDetails.from && animationDetails.to + ? prepareFromToAnchorAnimation(animationDetails.from, + animationDetails.to, + animationDetails.classes, + animationDetails.anchors) + : prepareRegularAnimation(animationDetails); + }; + + function filterCssClasses(classes) { + //remove all the `ng-` stuff + return classes.replace(/\bng-\S+\b/g, ''); + } + + function getUniqueValues(a, b) { + if (isString(a)) a = a.split(' '); + if (isString(b)) b = b.split(' '); + return a.filter(function(val) { + return b.indexOf(val) === -1; + }).join(' '); + } + + function prepareAnchoredAnimation(classes, outAnchor, inAnchor) { + var clone = jqLite(getDomNode(outAnchor).cloneNode(true)); + var startingClasses = filterCssClasses(getClassVal(clone)); + + outAnchor.addClass(NG_ANIMATE_SHIM_CLASS_NAME); + inAnchor.addClass(NG_ANIMATE_SHIM_CLASS_NAME); + + clone.addClass(NG_ANIMATE_ANCHOR_CLASS_NAME); + + rootBodyElement.append(clone); + + var animatorIn, animatorOut = prepareOutAnimation(); + + // the user may not end up using the `out` animation and + // only making use of the `in` animation or vice-versa. + // In either case we should allow this and not assume the + // animation is over unless both animations are not used. + if (!animatorOut) { + animatorIn = prepareInAnimation(); + if (!animatorIn) { + return end(); + } + } + + var startingAnimator = animatorOut || animatorIn; + + return { + start: function() { + var runner; + + var currentAnimation = startingAnimator.start(); + currentAnimation.done(function() { + currentAnimation = null; + if (!animatorIn) { + animatorIn = prepareInAnimation(); + if (animatorIn) { + currentAnimation = animatorIn.start(); + currentAnimation.done(function() { + currentAnimation = null; + end(); + runner.complete(); + }); + return currentAnimation; + } + } + // in the event that there is no `in` animation + end(); + runner.complete(); + }); + + runner = new $$AnimateRunner({ + end: endFn, + cancel: endFn + }); + + return runner; + + function endFn() { + if (currentAnimation) { + currentAnimation.end(); + } + } + } + }; + + function calculateAnchorStyles(anchor) { + var styles = {}; + + var coords = getDomNode(anchor).getBoundingClientRect(); + + // we iterate directly since safari messes up and doesn't return + // all the keys for the coords object when iterated + forEach(['width','height','top','left'], function(key) { + var value = coords[key]; + switch (key) { + case 'top': + value += bodyNode.scrollTop; + break; + case 'left': + value += bodyNode.scrollLeft; + break; + } + styles[key] = Math.floor(value) + 'px'; + }); + return styles; + } + + function prepareOutAnimation() { + var animator = $animateCss(clone, { + addClass: NG_OUT_ANCHOR_CLASS_NAME, + delay: true, + from: calculateAnchorStyles(outAnchor) + }); + + // read the comment within `prepareRegularAnimation` to understand + // why this check is necessary + return animator.$$willAnimate ? animator : null; + } + + function getClassVal(element) { + return element.attr('class') || ''; + } + + function prepareInAnimation() { + var endingClasses = filterCssClasses(getClassVal(inAnchor)); + var toAdd = getUniqueValues(endingClasses, startingClasses); + var toRemove = getUniqueValues(startingClasses, endingClasses); + + var animator = $animateCss(clone, { + to: calculateAnchorStyles(inAnchor), + addClass: NG_IN_ANCHOR_CLASS_NAME + ' ' + toAdd, + removeClass: NG_OUT_ANCHOR_CLASS_NAME + ' ' + toRemove, + delay: true + }); + + // read the comment within `prepareRegularAnimation` to understand + // why this check is necessary + return animator.$$willAnimate ? animator : null; + } + + function end() { + clone.remove(); + outAnchor.removeClass(NG_ANIMATE_SHIM_CLASS_NAME); + inAnchor.removeClass(NG_ANIMATE_SHIM_CLASS_NAME); + } + } + + function prepareFromToAnchorAnimation(from, to, classes, anchors) { + var fromAnimation = prepareRegularAnimation(from, noop); + var toAnimation = prepareRegularAnimation(to, noop); + + var anchorAnimations = []; + forEach(anchors, function(anchor) { + var outElement = anchor['out']; + var inElement = anchor['in']; + var animator = prepareAnchoredAnimation(classes, outElement, inElement); + if (animator) { + anchorAnimations.push(animator); + } + }); + + // no point in doing anything when there are no elements to animate + if (!fromAnimation && !toAnimation && anchorAnimations.length === 0) return; + + return { + start: function() { + var animationRunners = []; + + if (fromAnimation) { + animationRunners.push(fromAnimation.start()); + } + + if (toAnimation) { + animationRunners.push(toAnimation.start()); + } + + forEach(anchorAnimations, function(animation) { + animationRunners.push(animation.start()); + }); + + var runner = new $$AnimateRunner({ + end: endFn, + cancel: endFn // CSS-driven animations cannot be cancelled, only ended + }); + + $$AnimateRunner.all(animationRunners, function(status) { + runner.complete(status); + }); + + return runner; + + function endFn() { + forEach(animationRunners, function(runner) { + runner.end(); + }); + } + } + }; + } + + function prepareRegularAnimation(animationDetails) { + var element = animationDetails.element; + var options = animationDetails.options || {}; + + if (animationDetails.structural) { + options.event = animationDetails.event; + options.structural = true; + options.applyClassesEarly = true; + + // we special case the leave animation since we want to ensure that + // the element is removed as soon as the animation is over. Otherwise + // a flicker might appear or the element may not be removed at all + if (animationDetails.event === 'leave') { + options.onDone = options.domOperation; + } + } + + // We assign the preparationClasses as the actual animation event since + // the internals of $animateCss will just suffix the event token values + // with `-active` to trigger the animation. + if (options.preparationClasses) { + options.event = concatWithSpace(options.event, options.preparationClasses); + } + + var animator = $animateCss(element, options); + + // the driver lookup code inside of $$animation attempts to spawn a + // driver one by one until a driver returns a.$$willAnimate animator object. + // $animateCss will always return an object, however, it will pass in + // a flag as a hint as to whether an animation was detected or not + return animator.$$willAnimate ? animator : null; + } + }]; +}]; + +// TODO(matsko): use caching here to speed things up for detection +// TODO(matsko): add documentation +// by the time... + +var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) { + this.$get = ['$injector', '$$AnimateRunner', '$$jqLite', + function($injector, $$AnimateRunner, $$jqLite) { + + var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); + // $animateJs(element, 'enter'); + return function(element, event, classes, options) { + var animationClosed = false; + + // the `classes` argument is optional and if it is not used + // then the classes will be resolved from the element's className + // property as well as options.addClass/options.removeClass. + if (arguments.length === 3 && isObject(classes)) { + options = classes; + classes = null; + } + + options = prepareAnimationOptions(options); + if (!classes) { + classes = element.attr('class') || ''; + if (options.addClass) { + classes += ' ' + options.addClass; + } + if (options.removeClass) { + classes += ' ' + options.removeClass; + } + } + + var classesToAdd = options.addClass; + var classesToRemove = options.removeClass; + + // the lookupAnimations function returns a series of animation objects that are + // matched up with one or more of the CSS classes. These animation objects are + // defined via the module.animation factory function. If nothing is detected then + // we don't return anything which then makes $animation query the next driver. + var animations = lookupAnimations(classes); + var before, after; + if (animations.length) { + var afterFn, beforeFn; + if (event == 'leave') { + beforeFn = 'leave'; + afterFn = 'afterLeave'; // TODO(matsko): get rid of this + } else { + beforeFn = 'before' + event.charAt(0).toUpperCase() + event.substr(1); + afterFn = event; + } + + if (event !== 'enter' && event !== 'move') { + before = packageAnimations(element, event, options, animations, beforeFn); + } + after = packageAnimations(element, event, options, animations, afterFn); + } + + // no matching animations + if (!before && !after) return; + + function applyOptions() { + options.domOperation(); + applyAnimationClasses(element, options); + } + + function close() { + animationClosed = true; + applyOptions(); + applyAnimationStyles(element, options); + } + + var runner; + + return { + $$willAnimate: true, + end: function() { + if (runner) { + runner.end(); + } else { + close(); + runner = new $$AnimateRunner(); + runner.complete(true); + } + return runner; + }, + start: function() { + if (runner) { + return runner; + } + + runner = new $$AnimateRunner(); + var closeActiveAnimations; + var chain = []; + + if (before) { + chain.push(function(fn) { + closeActiveAnimations = before(fn); + }); + } + + if (chain.length) { + chain.push(function(fn) { + applyOptions(); + fn(true); + }); + } else { + applyOptions(); + } + + if (after) { + chain.push(function(fn) { + closeActiveAnimations = after(fn); + }); + } + + runner.setHost({ + end: function() { + endAnimations(); + }, + cancel: function() { + endAnimations(true); + } + }); + + $$AnimateRunner.chain(chain, onComplete); + return runner; + + function onComplete(success) { + close(success); + runner.complete(success); + } + + function endAnimations(cancelled) { + if (!animationClosed) { + (closeActiveAnimations || noop)(cancelled); + onComplete(cancelled); + } + } + } + }; + + function executeAnimationFn(fn, element, event, options, onDone) { + var args; + switch (event) { + case 'animate': + args = [element, options.from, options.to, onDone]; + break; + + case 'setClass': + args = [element, classesToAdd, classesToRemove, onDone]; + break; + + case 'addClass': + args = [element, classesToAdd, onDone]; + break; + + case 'removeClass': + args = [element, classesToRemove, onDone]; + break; + + default: + args = [element, onDone]; + break; + } + + args.push(options); + + var value = fn.apply(fn, args); + if (value) { + if (isFunction(value.start)) { + value = value.start(); + } + + if (value instanceof $$AnimateRunner) { + value.done(onDone); + } else if (isFunction(value)) { + // optional onEnd / onCancel callback + return value; + } + } + + return noop; + } + + function groupEventedAnimations(element, event, options, animations, fnName) { + var operations = []; + forEach(animations, function(ani) { + var animation = ani[fnName]; + if (!animation) return; + + // note that all of these animations will run in parallel + operations.push(function() { + var runner; + var endProgressCb; + + var resolved = false; + var onAnimationComplete = function(rejected) { + if (!resolved) { + resolved = true; + (endProgressCb || noop)(rejected); + runner.complete(!rejected); + } + }; + + runner = new $$AnimateRunner({ + end: function() { + onAnimationComplete(); + }, + cancel: function() { + onAnimationComplete(true); + } + }); + + endProgressCb = executeAnimationFn(animation, element, event, options, function(result) { + var cancelled = result === false; + onAnimationComplete(cancelled); + }); + + return runner; + }); + }); + + return operations; + } + + function packageAnimations(element, event, options, animations, fnName) { + var operations = groupEventedAnimations(element, event, options, animations, fnName); + if (operations.length === 0) { + var a,b; + if (fnName === 'beforeSetClass') { + a = groupEventedAnimations(element, 'removeClass', options, animations, 'beforeRemoveClass'); + b = groupEventedAnimations(element, 'addClass', options, animations, 'beforeAddClass'); + } else if (fnName === 'setClass') { + a = groupEventedAnimations(element, 'removeClass', options, animations, 'removeClass'); + b = groupEventedAnimations(element, 'addClass', options, animations, 'addClass'); + } + + if (a) { + operations = operations.concat(a); + } + if (b) { + operations = operations.concat(b); + } + } + + if (operations.length === 0) return; + + // TODO(matsko): add documentation + return function startAnimation(callback) { + var runners = []; + if (operations.length) { + forEach(operations, function(animateFn) { + runners.push(animateFn()); + }); + } + + runners.length ? $$AnimateRunner.all(runners, callback) : callback(); + + return function endFn(reject) { + forEach(runners, function(runner) { + reject ? runner.cancel() : runner.end(); + }); + }; + }; + } + }; + + function lookupAnimations(classes) { + classes = isArray(classes) ? classes : classes.split(' '); + var matches = [], flagMap = {}; + for (var i=0; i < classes.length; i++) { + var klass = classes[i], + animationFactory = $animateProvider.$$registeredAnimations[klass]; + if (animationFactory && !flagMap[klass]) { + matches.push($injector.get(animationFactory)); + flagMap[klass] = true; + } + } + return matches; + } + }]; +}]; + +var $$AnimateJsDriverProvider = ['$$animationProvider', function($$animationProvider) { + $$animationProvider.drivers.push('$$animateJsDriver'); + this.$get = ['$$animateJs', '$$AnimateRunner', function($$animateJs, $$AnimateRunner) { + return function initDriverFn(animationDetails) { + if (animationDetails.from && animationDetails.to) { + var fromAnimation = prepareAnimation(animationDetails.from); + var toAnimation = prepareAnimation(animationDetails.to); + if (!fromAnimation && !toAnimation) return; + + return { + start: function() { + var animationRunners = []; + + if (fromAnimation) { + animationRunners.push(fromAnimation.start()); + } + + if (toAnimation) { + animationRunners.push(toAnimation.start()); + } + + $$AnimateRunner.all(animationRunners, done); + + var runner = new $$AnimateRunner({ + end: endFnFactory(), + cancel: endFnFactory() + }); + + return runner; + + function endFnFactory() { + return function() { + forEach(animationRunners, function(runner) { + // at this point we cannot cancel animations for groups just yet. 1.5+ + runner.end(); + }); + }; + } + + function done(status) { + runner.complete(status); + } + } + }; + } else { + return prepareAnimation(animationDetails); + } + }; + + function prepareAnimation(animationDetails) { + // TODO(matsko): make sure to check for grouped animations and delegate down to normal animations + var element = animationDetails.element; + var event = animationDetails.event; + var options = animationDetails.options; + var classes = animationDetails.classes; + return $$animateJs(element, event, classes, options); + } + }]; +}]; + +var NG_ANIMATE_ATTR_NAME = 'data-ng-animate'; +var NG_ANIMATE_PIN_DATA = '$ngAnimatePin'; +var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { + var PRE_DIGEST_STATE = 1; + var RUNNING_STATE = 2; + var ONE_SPACE = ' '; + + var rules = this.rules = { + skip: [], + cancel: [], + join: [] + }; + + function makeTruthyCssClassMap(classString) { + if (!classString) { + return null; + } + + var keys = classString.split(ONE_SPACE); + var map = Object.create(null); + + forEach(keys, function(key) { + map[key] = true; + }); + return map; + } + + function hasMatchingClasses(newClassString, currentClassString) { + if (newClassString && currentClassString) { + var currentClassMap = makeTruthyCssClassMap(currentClassString); + return newClassString.split(ONE_SPACE).some(function(className) { + return currentClassMap[className]; + }); + } + } + + function isAllowed(ruleType, element, currentAnimation, previousAnimation) { + return rules[ruleType].some(function(fn) { + return fn(element, currentAnimation, previousAnimation); + }); + } + + function hasAnimationClasses(animation, and) { + var a = (animation.addClass || '').length > 0; + var b = (animation.removeClass || '').length > 0; + return and ? a && b : a || b; + } + + rules.join.push(function(element, newAnimation, currentAnimation) { + // if the new animation is class-based then we can just tack that on + return !newAnimation.structural && hasAnimationClasses(newAnimation); + }); + + rules.skip.push(function(element, newAnimation, currentAnimation) { + // there is no need to animate anything if no classes are being added and + // there is no structural animation that will be triggered + return !newAnimation.structural && !hasAnimationClasses(newAnimation); + }); + + rules.skip.push(function(element, newAnimation, currentAnimation) { + // why should we trigger a new structural animation if the element will + // be removed from the DOM anyway? + return currentAnimation.event == 'leave' && newAnimation.structural; + }); + + rules.skip.push(function(element, newAnimation, currentAnimation) { + // if there is an ongoing current animation then don't even bother running the class-based animation + return currentAnimation.structural && currentAnimation.state === RUNNING_STATE && !newAnimation.structural; + }); + + rules.cancel.push(function(element, newAnimation, currentAnimation) { + // there can never be two structural animations running at the same time + return currentAnimation.structural && newAnimation.structural; + }); + + rules.cancel.push(function(element, newAnimation, currentAnimation) { + // if the previous animation is already running, but the new animation will + // be triggered, but the new animation is structural + return currentAnimation.state === RUNNING_STATE && newAnimation.structural; + }); + + rules.cancel.push(function(element, newAnimation, currentAnimation) { + var nA = newAnimation.addClass; + var nR = newAnimation.removeClass; + var cA = currentAnimation.addClass; + var cR = currentAnimation.removeClass; + + // early detection to save the global CPU shortage :) + if ((isUndefined(nA) && isUndefined(nR)) || (isUndefined(cA) && isUndefined(cR))) { + return false; + } + + return hasMatchingClasses(nA, cR) || hasMatchingClasses(nR, cA); + }); + + this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap', + '$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', '$$forceReflow', + function($$rAF, $rootScope, $rootElement, $document, $$HashMap, + $$animation, $$AnimateRunner, $templateRequest, $$jqLite, $$forceReflow) { + + var activeAnimationsLookup = new $$HashMap(); + var disabledElementsLookup = new $$HashMap(); + var animationsEnabled = null; + + function postDigestTaskFactory() { + var postDigestCalled = false; + return function(fn) { + // we only issue a call to postDigest before + // it has first passed. This prevents any callbacks + // from not firing once the animation has completed + // since it will be out of the digest cycle. + if (postDigestCalled) { + fn(); + } else { + $rootScope.$$postDigest(function() { + postDigestCalled = true; + fn(); + }); + } + }; + } + + // Wait until all directive and route-related templates are downloaded and + // compiled. The $templateRequest.totalPendingRequests variable keeps track of + // all of the remote templates being currently downloaded. If there are no + // templates currently downloading then the watcher will still fire anyway. + var deregisterWatch = $rootScope.$watch( + function() { return $templateRequest.totalPendingRequests === 0; }, + function(isEmpty) { + if (!isEmpty) return; + deregisterWatch(); + + // Now that all templates have been downloaded, $animate will wait until + // the post digest queue is empty before enabling animations. By having two + // calls to $postDigest calls we can ensure that the flag is enabled at the + // very end of the post digest queue. Since all of the animations in $animate + // use $postDigest, it's important that the code below executes at the end. + // This basically means that the page is fully downloaded and compiled before + // any animations are triggered. + $rootScope.$$postDigest(function() { + $rootScope.$$postDigest(function() { + // we check for null directly in the event that the application already called + // .enabled() with whatever arguments that it provided it with + if (animationsEnabled === null) { + animationsEnabled = true; + } + }); + }); + } + ); + + var callbackRegistry = {}; + + // remember that the classNameFilter is set during the provider/config + // stage therefore we can optimize here and setup a helper function + var classNameFilter = $animateProvider.classNameFilter(); + var isAnimatableClassName = !classNameFilter + ? function() { return true; } + : function(className) { + return classNameFilter.test(className); + }; + + var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); + + function normalizeAnimationDetails(element, animation) { + return mergeAnimationDetails(element, animation, {}); + } + + // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259. + var contains = Node.prototype.contains || function(arg) { + // jshint bitwise: false + return this === arg || !!(this.compareDocumentPosition(arg) & 16); + // jshint bitwise: true + }; + + function findCallbacks(parent, element, event) { + var targetNode = getDomNode(element); + var targetParentNode = getDomNode(parent); + + var matches = []; + var entries = callbackRegistry[event]; + if (entries) { + forEach(entries, function(entry) { + if (contains.call(entry.node, targetNode)) { + matches.push(entry.callback); + } else if (event === 'leave' && contains.call(entry.node, targetParentNode)) { + matches.push(entry.callback); + } + }); + } + + return matches; + } + + return { + on: function(event, container, callback) { + var node = extractElementNode(container); + callbackRegistry[event] = callbackRegistry[event] || []; + callbackRegistry[event].push({ + node: node, + callback: callback + }); + }, + + off: function(event, container, callback) { + var entries = callbackRegistry[event]; + if (!entries) return; + + callbackRegistry[event] = arguments.length === 1 + ? null + : filterFromRegistry(entries, container, callback); + + function filterFromRegistry(list, matchContainer, matchCallback) { + var containerNode = extractElementNode(matchContainer); + return list.filter(function(entry) { + var isMatch = entry.node === containerNode && + (!matchCallback || entry.callback === matchCallback); + return !isMatch; + }); + } + }, + + pin: function(element, parentElement) { + assertArg(isElement(element), 'element', 'not an element'); + assertArg(isElement(parentElement), 'parentElement', 'not an element'); + element.data(NG_ANIMATE_PIN_DATA, parentElement); + }, + + push: function(element, event, options, domOperation) { + options = options || {}; + options.domOperation = domOperation; + return queueAnimation(element, event, options); + }, + + // this method has four signatures: + // () - global getter + // (bool) - global setter + // (element) - element getter + // (element, bool) - element setter + enabled: function(element, bool) { + var argCount = arguments.length; + + if (argCount === 0) { + // () - Global getter + bool = !!animationsEnabled; + } else { + var hasElement = isElement(element); + + if (!hasElement) { + // (bool) - Global setter + bool = animationsEnabled = !!element; + } else { + var node = getDomNode(element); + var recordExists = disabledElementsLookup.get(node); + + if (argCount === 1) { + // (element) - Element getter + bool = !recordExists; + } else { + // (element, bool) - Element setter + disabledElementsLookup.put(node, !bool); + } + } + } + + return bool; + } + }; + + function queueAnimation(element, event, initialOptions) { + // we always make a copy of the options since + // there should never be any side effects on + // the input data when running `$animateCss`. + var options = copy(initialOptions); + + var node, parent; + element = stripCommentsFromElement(element); + if (element) { + node = getDomNode(element); + parent = element.parent(); + } + + options = prepareAnimationOptions(options); + + // we create a fake runner with a working promise. + // These methods will become available after the digest has passed + var runner = new $$AnimateRunner(); + + // this is used to trigger callbacks in postDigest mode + var runInNextPostDigestOrNow = postDigestTaskFactory(); + + if (isArray(options.addClass)) { + options.addClass = options.addClass.join(' '); + } + + if (options.addClass && !isString(options.addClass)) { + options.addClass = null; + } + + if (isArray(options.removeClass)) { + options.removeClass = options.removeClass.join(' '); + } + + if (options.removeClass && !isString(options.removeClass)) { + options.removeClass = null; + } + + if (options.from && !isObject(options.from)) { + options.from = null; + } + + if (options.to && !isObject(options.to)) { + options.to = null; + } + + // there are situations where a directive issues an animation for + // a jqLite wrapper that contains only comment nodes... If this + // happens then there is no way we can perform an animation + if (!node) { + close(); + return runner; + } + + var className = [node.className, options.addClass, options.removeClass].join(' '); + if (!isAnimatableClassName(className)) { + close(); + return runner; + } + + var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0; + + // this is a hard disable of all animations for the application or on + // the element itself, therefore there is no need to continue further + // past this point if not enabled + // Animations are also disabled if the document is currently hidden (page is not visible + // to the user), because browsers slow down or do not flush calls to requestAnimationFrame + var skipAnimations = !animationsEnabled || $document[0].hidden || disabledElementsLookup.get(node); + var existingAnimation = (!skipAnimations && activeAnimationsLookup.get(node)) || {}; + var hasExistingAnimation = !!existingAnimation.state; + + // there is no point in traversing the same collection of parent ancestors if a followup + // animation will be run on the same element that already did all that checking work + if (!skipAnimations && (!hasExistingAnimation || existingAnimation.state != PRE_DIGEST_STATE)) { + skipAnimations = !areAnimationsAllowed(element, parent, event); + } + + if (skipAnimations) { + close(); + return runner; + } + + if (isStructural) { + closeChildAnimations(element); + } + + var newAnimation = { + structural: isStructural, + element: element, + event: event, + addClass: options.addClass, + removeClass: options.removeClass, + close: close, + options: options, + runner: runner + }; + + if (hasExistingAnimation) { + var skipAnimationFlag = isAllowed('skip', element, newAnimation, existingAnimation); + if (skipAnimationFlag) { + if (existingAnimation.state === RUNNING_STATE) { + close(); + return runner; + } else { + mergeAnimationDetails(element, existingAnimation, newAnimation); + return existingAnimation.runner; + } + } + var cancelAnimationFlag = isAllowed('cancel', element, newAnimation, existingAnimation); + if (cancelAnimationFlag) { + if (existingAnimation.state === RUNNING_STATE) { + // this will end the animation right away and it is safe + // to do so since the animation is already running and the + // runner callback code will run in async + existingAnimation.runner.end(); + } else if (existingAnimation.structural) { + // this means that the animation is queued into a digest, but + // hasn't started yet. Therefore it is safe to run the close + // method which will call the runner methods in async. + existingAnimation.close(); + } else { + // this will merge the new animation options into existing animation options + mergeAnimationDetails(element, existingAnimation, newAnimation); + + return existingAnimation.runner; + } + } else { + // a joined animation means that this animation will take over the existing one + // so an example would involve a leave animation taking over an enter. Then when + // the postDigest kicks in the enter will be ignored. + var joinAnimationFlag = isAllowed('join', element, newAnimation, existingAnimation); + if (joinAnimationFlag) { + if (existingAnimation.state === RUNNING_STATE) { + normalizeAnimationDetails(element, newAnimation); + } else { + applyGeneratedPreparationClasses(element, isStructural ? event : null, options); + + event = newAnimation.event = existingAnimation.event; + options = mergeAnimationDetails(element, existingAnimation, newAnimation); + + //we return the same runner since only the option values of this animation will + //be fed into the `existingAnimation`. + return existingAnimation.runner; + } + } + } + } else { + // normalization in this case means that it removes redundant CSS classes that + // already exist (addClass) or do not exist (removeClass) on the element + normalizeAnimationDetails(element, newAnimation); + } + + // when the options are merged and cleaned up we may end up not having to do + // an animation at all, therefore we should check this before issuing a post + // digest callback. Structural animations will always run no matter what. + var isValidAnimation = newAnimation.structural; + if (!isValidAnimation) { + // animate (from/to) can be quickly checked first, otherwise we check if any classes are present + isValidAnimation = (newAnimation.event === 'animate' && Object.keys(newAnimation.options.to || {}).length > 0) + || hasAnimationClasses(newAnimation); + } + + if (!isValidAnimation) { + close(); + clearElementAnimationState(element); + return runner; + } + + // the counter keeps track of cancelled animations + var counter = (existingAnimation.counter || 0) + 1; + newAnimation.counter = counter; + + markElementAnimationState(element, PRE_DIGEST_STATE, newAnimation); + + $rootScope.$$postDigest(function() { + var animationDetails = activeAnimationsLookup.get(node); + var animationCancelled = !animationDetails; + animationDetails = animationDetails || {}; + + // if addClass/removeClass is called before something like enter then the + // registered parent element may not be present. The code below will ensure + // that a final value for parent element is obtained + var parentElement = element.parent() || []; + + // animate/structural/class-based animations all have requirements. Otherwise there + // is no point in performing an animation. The parent node must also be set. + var isValidAnimation = parentElement.length > 0 + && (animationDetails.event === 'animate' + || animationDetails.structural + || hasAnimationClasses(animationDetails)); + + // this means that the previous animation was cancelled + // even if the follow-up animation is the same event + if (animationCancelled || animationDetails.counter !== counter || !isValidAnimation) { + // if another animation did not take over then we need + // to make sure that the domOperation and options are + // handled accordingly + if (animationCancelled) { + applyAnimationClasses(element, options); + applyAnimationStyles(element, options); + } + + // if the event changed from something like enter to leave then we do + // it, otherwise if it's the same then the end result will be the same too + if (animationCancelled || (isStructural && animationDetails.event !== event)) { + options.domOperation(); + runner.end(); + } + + // in the event that the element animation was not cancelled or a follow-up animation + // isn't allowed to animate from here then we need to clear the state of the element + // so that any future animations won't read the expired animation data. + if (!isValidAnimation) { + clearElementAnimationState(element); + } + + return; + } + + // this combined multiple class to addClass / removeClass into a setClass event + // so long as a structural event did not take over the animation + event = !animationDetails.structural && hasAnimationClasses(animationDetails, true) + ? 'setClass' + : animationDetails.event; + + markElementAnimationState(element, RUNNING_STATE); + var realRunner = $$animation(element, event, animationDetails.options); + + realRunner.done(function(status) { + close(!status); + var animationDetails = activeAnimationsLookup.get(node); + if (animationDetails && animationDetails.counter === counter) { + clearElementAnimationState(getDomNode(element)); + } + notifyProgress(runner, event, 'close', {}); + }); + + // this will update the runner's flow-control events based on + // the `realRunner` object. + runner.setHost(realRunner); + notifyProgress(runner, event, 'start', {}); + }); + + return runner; + + function notifyProgress(runner, event, phase, data) { + runInNextPostDigestOrNow(function() { + var callbacks = findCallbacks(parent, element, event); + if (callbacks.length) { + // do not optimize this call here to RAF because + // we don't know how heavy the callback code here will + // be and if this code is buffered then this can + // lead to a performance regression. + $$rAF(function() { + forEach(callbacks, function(callback) { + callback(element, phase, data); + }); + }); + } + }); + runner.progress(event, phase, data); + } + + function close(reject) { // jshint ignore:line + clearGeneratedClasses(element, options); + applyAnimationClasses(element, options); + applyAnimationStyles(element, options); + options.domOperation(); + runner.complete(!reject); + } + } + + function closeChildAnimations(element) { + var node = getDomNode(element); + var children = node.querySelectorAll('[' + NG_ANIMATE_ATTR_NAME + ']'); + forEach(children, function(child) { + var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME)); + var animationDetails = activeAnimationsLookup.get(child); + if (animationDetails) { + switch (state) { + case RUNNING_STATE: + animationDetails.runner.end(); + /* falls through */ + case PRE_DIGEST_STATE: + activeAnimationsLookup.remove(child); + break; + } + } + }); + } + + function clearElementAnimationState(element) { + var node = getDomNode(element); + node.removeAttribute(NG_ANIMATE_ATTR_NAME); + activeAnimationsLookup.remove(node); + } + + function isMatchingElement(nodeOrElmA, nodeOrElmB) { + return getDomNode(nodeOrElmA) === getDomNode(nodeOrElmB); + } + + /** + * This fn returns false if any of the following is true: + * a) animations on any parent element are disabled, and animations on the element aren't explicitly allowed + * b) a parent element has an ongoing structural animation, and animateChildren is false + * c) the element is not a child of the body + * d) the element is not a child of the $rootElement + */ + function areAnimationsAllowed(element, parentElement, event) { + var bodyElement = jqLite($document[0].body); + var bodyElementDetected = isMatchingElement(element, bodyElement) || element[0].nodeName === 'HTML'; + var rootElementDetected = isMatchingElement(element, $rootElement); + var parentAnimationDetected = false; + var animateChildren; + var elementDisabled = disabledElementsLookup.get(getDomNode(element)); + + var parentHost = element.data(NG_ANIMATE_PIN_DATA); + if (parentHost) { + parentElement = parentHost; + } + + while (parentElement && parentElement.length) { + if (!rootElementDetected) { + // angular doesn't want to attempt to animate elements outside of the application + // therefore we need to ensure that the rootElement is an ancestor of the current element + rootElementDetected = isMatchingElement(parentElement, $rootElement); + } + + var parentNode = parentElement[0]; + if (parentNode.nodeType !== ELEMENT_NODE) { + // no point in inspecting the #document element + break; + } + + var details = activeAnimationsLookup.get(parentNode) || {}; + // either an enter, leave or move animation will commence + // therefore we can't allow any animations to take place + // but if a parent animation is class-based then that's ok + if (!parentAnimationDetected) { + var parentElementDisabled = disabledElementsLookup.get(parentNode); + + if (parentElementDisabled === true && elementDisabled !== false) { + // disable animations if the user hasn't explicitly enabled animations on the + // current element + elementDisabled = true; + // element is disabled via parent element, no need to check anything else + break; + } else if (parentElementDisabled === false) { + elementDisabled = false; + } + parentAnimationDetected = details.structural; + } + + if (isUndefined(animateChildren) || animateChildren === true) { + var value = parentElement.data(NG_ANIMATE_CHILDREN_DATA); + if (isDefined(value)) { + animateChildren = value; + } + } + + // there is no need to continue traversing at this point + if (parentAnimationDetected && animateChildren === false) break; + + if (!bodyElementDetected) { + // we also need to ensure that the element is or will be a part of the body element + // otherwise it is pointless to even issue an animation to be rendered + bodyElementDetected = isMatchingElement(parentElement, bodyElement); + } + + if (bodyElementDetected && rootElementDetected) { + // If both body and root have been found, any other checks are pointless, + // as no animation data should live outside the application + break; + } + + if (!rootElementDetected) { + // If no rootElement is detected, check if the parentElement is pinned to another element + parentHost = parentElement.data(NG_ANIMATE_PIN_DATA); + if (parentHost) { + // The pin target element becomes the next parent element + parentElement = parentHost; + continue; + } + } + + parentElement = parentElement.parent(); + } + + var allowAnimation = (!parentAnimationDetected || animateChildren) && elementDisabled !== true; + return allowAnimation && rootElementDetected && bodyElementDetected; + } + + function markElementAnimationState(element, state, details) { + details = details || {}; + details.state = state; + + var node = getDomNode(element); + node.setAttribute(NG_ANIMATE_ATTR_NAME, state); + + var oldValue = activeAnimationsLookup.get(node); + var newValue = oldValue + ? extend(oldValue, details) + : details; + activeAnimationsLookup.put(node, newValue); + } + }]; +}]; + +var $$AnimationProvider = ['$animateProvider', function($animateProvider) { + var NG_ANIMATE_REF_ATTR = 'ng-animate-ref'; + + var drivers = this.drivers = []; + + var RUNNER_STORAGE_KEY = '$$animationRunner'; + + function setRunner(element, runner) { + element.data(RUNNER_STORAGE_KEY, runner); + } + + function removeRunner(element) { + element.removeData(RUNNER_STORAGE_KEY); + } + + function getRunner(element) { + return element.data(RUNNER_STORAGE_KEY); + } + + this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$HashMap', '$$rAFScheduler', + function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$HashMap, $$rAFScheduler) { + + var animationQueue = []; + var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); + + function sortAnimations(animations) { + var tree = { children: [] }; + var i, lookup = new $$HashMap(); + + // this is done first beforehand so that the hashmap + // is filled with a list of the elements that will be animated + for (i = 0; i < animations.length; i++) { + var animation = animations[i]; + lookup.put(animation.domNode, animations[i] = { + domNode: animation.domNode, + fn: animation.fn, + children: [] + }); + } + + for (i = 0; i < animations.length; i++) { + processNode(animations[i]); + } + + return flatten(tree); + + function processNode(entry) { + if (entry.processed) return entry; + entry.processed = true; + + var elementNode = entry.domNode; + var parentNode = elementNode.parentNode; + lookup.put(elementNode, entry); + + var parentEntry; + while (parentNode) { + parentEntry = lookup.get(parentNode); + if (parentEntry) { + if (!parentEntry.processed) { + parentEntry = processNode(parentEntry); + } + break; + } + parentNode = parentNode.parentNode; + } + + (parentEntry || tree).children.push(entry); + return entry; + } + + function flatten(tree) { + var result = []; + var queue = []; + var i; + + for (i = 0; i < tree.children.length; i++) { + queue.push(tree.children[i]); + } + + var remainingLevelEntries = queue.length; + var nextLevelEntries = 0; + var row = []; + + for (i = 0; i < queue.length; i++) { + var entry = queue[i]; + if (remainingLevelEntries <= 0) { + remainingLevelEntries = nextLevelEntries; + nextLevelEntries = 0; + result.push(row); + row = []; + } + row.push(entry.fn); + entry.children.forEach(function(childEntry) { + nextLevelEntries++; + queue.push(childEntry); + }); + remainingLevelEntries--; + } + + if (row.length) { + result.push(row); + } + + return result; + } + } + + // TODO(matsko): document the signature in a better way + return function(element, event, options) { + options = prepareAnimationOptions(options); + var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0; + + // there is no animation at the current moment, however + // these runner methods will get later updated with the + // methods leading into the driver's end/cancel methods + // for now they just stop the animation from starting + var runner = new $$AnimateRunner({ + end: function() { close(); }, + cancel: function() { close(true); } + }); + + if (!drivers.length) { + close(); + return runner; + } + + setRunner(element, runner); + + var classes = mergeClasses(element.attr('class'), mergeClasses(options.addClass, options.removeClass)); + var tempClasses = options.tempClasses; + if (tempClasses) { + classes += ' ' + tempClasses; + options.tempClasses = null; + } + + var prepareClassName; + if (isStructural) { + prepareClassName = 'ng-' + event + PREPARE_CLASS_SUFFIX; + $$jqLite.addClass(element, prepareClassName); + } + + animationQueue.push({ + // this data is used by the postDigest code and passed into + // the driver step function + element: element, + classes: classes, + event: event, + structural: isStructural, + options: options, + beforeStart: beforeStart, + close: close + }); + + element.on('$destroy', handleDestroyedElement); + + // we only want there to be one function called within the post digest + // block. This way we can group animations for all the animations that + // were apart of the same postDigest flush call. + if (animationQueue.length > 1) return runner; + + $rootScope.$$postDigest(function() { + var animations = []; + forEach(animationQueue, function(entry) { + // the element was destroyed early on which removed the runner + // form its storage. This means we can't animate this element + // at all and it already has been closed due to destruction. + if (getRunner(entry.element)) { + animations.push(entry); + } else { + entry.close(); + } + }); + + // now any future animations will be in another postDigest + animationQueue.length = 0; + + var groupedAnimations = groupAnimations(animations); + var toBeSortedAnimations = []; + + forEach(groupedAnimations, function(animationEntry) { + toBeSortedAnimations.push({ + domNode: getDomNode(animationEntry.from ? animationEntry.from.element : animationEntry.element), + fn: function triggerAnimationStart() { + // it's important that we apply the `ng-animate` CSS class and the + // temporary classes before we do any driver invoking since these + // CSS classes may be required for proper CSS detection. + animationEntry.beforeStart(); + + var startAnimationFn, closeFn = animationEntry.close; + + // in the event that the element was removed before the digest runs or + // during the RAF sequencing then we should not trigger the animation. + var targetElement = animationEntry.anchors + ? (animationEntry.from.element || animationEntry.to.element) + : animationEntry.element; + + if (getRunner(targetElement)) { + var operation = invokeFirstDriver(animationEntry); + if (operation) { + startAnimationFn = operation.start; + } + } + + if (!startAnimationFn) { + closeFn(); + } else { + var animationRunner = startAnimationFn(); + animationRunner.done(function(status) { + closeFn(!status); + }); + updateAnimationRunners(animationEntry, animationRunner); + } + } + }); + }); + + // we need to sort each of the animations in order of parent to child + // relationships. This ensures that the child classes are applied at the + // right time. + $$rAFScheduler(sortAnimations(toBeSortedAnimations)); + }); + + return runner; + + // TODO(matsko): change to reference nodes + function getAnchorNodes(node) { + var SELECTOR = '[' + NG_ANIMATE_REF_ATTR + ']'; + var items = node.hasAttribute(NG_ANIMATE_REF_ATTR) + ? [node] + : node.querySelectorAll(SELECTOR); + var anchors = []; + forEach(items, function(node) { + var attr = node.getAttribute(NG_ANIMATE_REF_ATTR); + if (attr && attr.length) { + anchors.push(node); + } + }); + return anchors; + } + + function groupAnimations(animations) { + var preparedAnimations = []; + var refLookup = {}; + forEach(animations, function(animation, index) { + var element = animation.element; + var node = getDomNode(element); + var event = animation.event; + var enterOrMove = ['enter', 'move'].indexOf(event) >= 0; + var anchorNodes = animation.structural ? getAnchorNodes(node) : []; + + if (anchorNodes.length) { + var direction = enterOrMove ? 'to' : 'from'; + + forEach(anchorNodes, function(anchor) { + var key = anchor.getAttribute(NG_ANIMATE_REF_ATTR); + refLookup[key] = refLookup[key] || {}; + refLookup[key][direction] = { + animationID: index, + element: jqLite(anchor) + }; + }); + } else { + preparedAnimations.push(animation); + } + }); + + var usedIndicesLookup = {}; + var anchorGroups = {}; + forEach(refLookup, function(operations, key) { + var from = operations.from; + var to = operations.to; + + if (!from || !to) { + // only one of these is set therefore we can't have an + // anchor animation since all three pieces are required + var index = from ? from.animationID : to.animationID; + var indexKey = index.toString(); + if (!usedIndicesLookup[indexKey]) { + usedIndicesLookup[indexKey] = true; + preparedAnimations.push(animations[index]); + } + return; + } + + var fromAnimation = animations[from.animationID]; + var toAnimation = animations[to.animationID]; + var lookupKey = from.animationID.toString(); + if (!anchorGroups[lookupKey]) { + var group = anchorGroups[lookupKey] = { + structural: true, + beforeStart: function() { + fromAnimation.beforeStart(); + toAnimation.beforeStart(); + }, + close: function() { + fromAnimation.close(); + toAnimation.close(); + }, + classes: cssClassesIntersection(fromAnimation.classes, toAnimation.classes), + from: fromAnimation, + to: toAnimation, + anchors: [] // TODO(matsko): change to reference nodes + }; + + // the anchor animations require that the from and to elements both have at least + // one shared CSS class which effectively marries the two elements together to use + // the same animation driver and to properly sequence the anchor animation. + if (group.classes.length) { + preparedAnimations.push(group); + } else { + preparedAnimations.push(fromAnimation); + preparedAnimations.push(toAnimation); + } + } + + anchorGroups[lookupKey].anchors.push({ + 'out': from.element, 'in': to.element + }); + }); + + return preparedAnimations; + } + + function cssClassesIntersection(a,b) { + a = a.split(' '); + b = b.split(' '); + var matches = []; + + for (var i = 0; i < a.length; i++) { + var aa = a[i]; + if (aa.substring(0,3) === 'ng-') continue; + + for (var j = 0; j < b.length; j++) { + if (aa === b[j]) { + matches.push(aa); + break; + } + } + } + + return matches.join(' '); + } + + function invokeFirstDriver(animationDetails) { + // we loop in reverse order since the more general drivers (like CSS and JS) + // may attempt more elements, but custom drivers are more particular + for (var i = drivers.length - 1; i >= 0; i--) { + var driverName = drivers[i]; + if (!$injector.has(driverName)) continue; // TODO(matsko): remove this check + + var factory = $injector.get(driverName); + var driver = factory(animationDetails); + if (driver) { + return driver; + } + } + } + + function beforeStart() { + element.addClass(NG_ANIMATE_CLASSNAME); + if (tempClasses) { + $$jqLite.addClass(element, tempClasses); + } + if (prepareClassName) { + $$jqLite.removeClass(element, prepareClassName); + prepareClassName = null; + } + } + + function updateAnimationRunners(animation, newRunner) { + if (animation.from && animation.to) { + update(animation.from.element); + update(animation.to.element); + } else { + update(animation.element); + } + + function update(element) { + getRunner(element).setHost(newRunner); + } + } + + function handleDestroyedElement() { + var runner = getRunner(element); + if (runner && (event !== 'leave' || !options.$$domOperationFired)) { + runner.end(); + } + } + + function close(rejected) { // jshint ignore:line + element.off('$destroy', handleDestroyedElement); + removeRunner(element); + + applyAnimationClasses(element, options); + applyAnimationStyles(element, options); + options.domOperation(); + + if (tempClasses) { + $$jqLite.removeClass(element, tempClasses); + } + + element.removeClass(NG_ANIMATE_CLASSNAME); + runner.complete(!rejected); + } + }; + }]; +}]; + +/** + * @ngdoc directive + * @name ngAnimateSwap + * @restrict A + * @scope + * + * @description + * + * ngAnimateSwap is a animation-oriented directive that allows for the container to + * be removed and entered in whenever the associated expression changes. A + * common usecase for this directive is a rotating banner component which + * contains one image being present at a time. When the active image changes + * then the old image will perform a `leave` animation and the new element + * will be inserted via an `enter` animation. + * + * @example + * + * + *
+ *
+ * {{ number }} + *
+ *
+ *
+ * + * angular.module('ngAnimateSwapExample', ['ngAnimate']) + * .controller('AppCtrl', ['$scope', '$interval', function($scope, $interval) { + * $scope.number = 0; + * $interval(function() { + * $scope.number++; + * }, 1000); + * + * var colors = ['red','blue','green','yellow','orange']; + * $scope.colorClass = function(number) { + * return colors[number % colors.length]; + * }; + * }]); + * + * + * .container { + * height:250px; + * width:250px; + * position:relative; + * overflow:hidden; + * border:2px solid black; + * } + * .container .cell { + * font-size:150px; + * text-align:center; + * line-height:250px; + * position:absolute; + * top:0; + * left:0; + * right:0; + * border-bottom:2px solid black; + * } + * .swap-animation.ng-enter, .swap-animation.ng-leave { + * transition:0.5s linear all; + * } + * .swap-animation.ng-enter { + * top:-250px; + * } + * .swap-animation.ng-enter-active { + * top:0px; + * } + * .swap-animation.ng-leave { + * top:0px; + * } + * .swap-animation.ng-leave-active { + * top:250px; + * } + * .red { background:red; } + * .green { background:green; } + * .blue { background:blue; } + * .yellow { background:yellow; } + * .orange { background:orange; } + * + *
+ */ +var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $rootScope) { + return { + restrict: 'A', + transclude: 'element', + terminal: true, + priority: 600, // we use 600 here to ensure that the directive is caught before others + link: function(scope, $element, attrs, ctrl, $transclude) { + var previousElement, previousScope; + scope.$watchCollection(attrs.ngAnimateSwap || attrs['for'], function(value) { + if (previousElement) { + $animate.leave(previousElement); + } + if (previousScope) { + previousScope.$destroy(); + previousScope = null; + } + if (value || value === 0) { + previousScope = scope.$new(); + $transclude(previousScope, function(element) { + previousElement = element; + $animate.enter(element, null, $element); + }); + } + }); + } + }; +}]; + +/* global angularAnimateModule: true, + + ngAnimateSwapDirective, + $$AnimateAsyncRunFactory, + $$rAFSchedulerFactory, + $$AnimateChildrenDirective, + $$AnimateQueueProvider, + $$AnimationProvider, + $AnimateCssProvider, + $$AnimateCssDriverProvider, + $$AnimateJsProvider, + $$AnimateJsDriverProvider, +*/ + +/** + * @ngdoc module + * @name ngAnimate + * @description + * + * The `ngAnimate` module provides support for CSS-based animations (keyframes and transitions) as well as JavaScript-based animations via + * callback hooks. Animations are not enabled by default, however, by including `ngAnimate` the animation hooks are enabled for an Angular app. + * + *
+ * + * # Usage + * Simply put, there are two ways to make use of animations when ngAnimate is used: by using **CSS** and **JavaScript**. The former works purely based + * using CSS (by using matching CSS selectors/styles) and the latter triggers animations that are registered via `module.animation()`. For + * both CSS and JS animations the sole requirement is to have a matching `CSS class` that exists both in the registered animation and within + * the HTML element that the animation will be triggered on. + * + * ## Directive Support + * The following directives are "animation aware": + * + * | Directive | Supported Animations | + * |----------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------| + * | {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave and move | + * | {@link ngRoute.directive:ngView#animations ngView} | enter and leave | + * | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave | + * | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave | + * | {@link ng.directive:ngIf#animations ngIf} | enter and leave | + * | {@link ng.directive:ngClass#animations ngClass} | add and remove (the CSS class(es) present) | + * | {@link ng.directive:ngShow#animations ngShow} & {@link ng.directive:ngHide#animations ngHide} | add and remove (the ng-hide class value) | + * | {@link ng.directive:form#animation-hooks form} & {@link ng.directive:ngModel#animation-hooks ngModel} | add and remove (dirty, pristine, valid, invalid & all other validations) | + * | {@link module:ngMessages#animations ngMessages} | add and remove (ng-active & ng-inactive) | + * | {@link module:ngMessages#animations ngMessage} | enter and leave | + * + * (More information can be found by visiting each the documentation associated with each directive.) + * + * ## CSS-based Animations + * + * CSS-based animations with ngAnimate are unique since they require no JavaScript code at all. By using a CSS class that we reference between our HTML + * and CSS code we can create an animation that will be picked up by Angular when an the underlying directive performs an operation. + * + * The example below shows how an `enter` animation can be made possible on an element using `ng-if`: + * + * ```html + *
+ * Fade me in out + *
+ * + * + * ``` + * + * Notice the CSS class **fade**? We can now create the CSS transition code that references this class: + * + * ```css + * /* The starting CSS styles for the enter animation */ + * .fade.ng-enter { + * transition:0.5s linear all; + * opacity:0; + * } + * + * /* The finishing CSS styles for the enter animation */ + * .fade.ng-enter.ng-enter-active { + * opacity:1; + * } + * ``` + * + * The key thing to remember here is that, depending on the animation event (which each of the directives above trigger depending on what's going on) two + * generated CSS classes will be applied to the element; in the example above we have `.ng-enter` and `.ng-enter-active`. For CSS transitions, the transition + * code **must** be defined within the starting CSS class (in this case `.ng-enter`). The destination class is what the transition will animate towards. + * + * If for example we wanted to create animations for `leave` and `move` (ngRepeat triggers move) then we can do so using the same CSS naming conventions: + * + * ```css + * /* now the element will fade out before it is removed from the DOM */ + * .fade.ng-leave { + * transition:0.5s linear all; + * opacity:1; + * } + * .fade.ng-leave.ng-leave-active { + * opacity:0; + * } + * ``` + * + * We can also make use of **CSS Keyframes** by referencing the keyframe animation within the starting CSS class: + * + * ```css + * /* there is no need to define anything inside of the destination + * CSS class since the keyframe will take charge of the animation */ + * .fade.ng-leave { + * animation: my_fade_animation 0.5s linear; + * -webkit-animation: my_fade_animation 0.5s linear; + * } + * + * @keyframes my_fade_animation { + * from { opacity:1; } + * to { opacity:0; } + * } + * + * @-webkit-keyframes my_fade_animation { + * from { opacity:1; } + * to { opacity:0; } + * } + * ``` + * + * Feel free also mix transitions and keyframes together as well as any other CSS classes on the same element. + * + * ### CSS Class-based Animations + * + * Class-based animations (animations that are triggered via `ngClass`, `ngShow`, `ngHide` and some other directives) have a slightly different + * naming convention. Class-based animations are basic enough that a standard transition or keyframe can be referenced on the class being added + * and removed. + * + * For example if we wanted to do a CSS animation for `ngHide` then we place an animation on the `.ng-hide` CSS class: + * + * ```html + *
+ * Show and hide me + *
+ * + * + * + * ``` + * + * All that is going on here with ngShow/ngHide behind the scenes is the `.ng-hide` class is added/removed (when the hidden state is valid). Since + * ngShow and ngHide are animation aware then we can match up a transition and ngAnimate handles the rest. + * + * In addition the addition and removal of the CSS class, ngAnimate also provides two helper methods that we can use to further decorate the animation + * with CSS styles. + * + * ```html + *
+ * Highlight this box + *
+ * + * + * + * ``` + * + * We can also make use of CSS keyframes by placing them within the CSS classes. + * + * + * ### CSS Staggering Animations + * A Staggering animation is a collection of animations that are issued with a slight delay in between each successive operation resulting in a + * curtain-like effect. The ngAnimate module (versions >=1.2) supports staggering animations and the stagger effect can be + * performed by creating a **ng-EVENT-stagger** CSS class and attaching that class to the base CSS class used for + * the animation. The style property expected within the stagger class can either be a **transition-delay** or an + * **animation-delay** property (or both if your animation contains both transitions and keyframe animations). + * + * ```css + * .my-animation.ng-enter { + * /* standard transition code */ + * transition: 1s linear all; + * opacity:0; + * } + * .my-animation.ng-enter-stagger { + * /* this will have a 100ms delay between each successive leave animation */ + * transition-delay: 0.1s; + * + * /* As of 1.4.4, this must always be set: it signals ngAnimate + * to not accidentally inherit a delay property from another CSS class */ + * transition-duration: 0s; + * } + * .my-animation.ng-enter.ng-enter-active { + * /* standard transition styles */ + * opacity:1; + * } + * ``` + * + * Staggering animations work by default in ngRepeat (so long as the CSS class is defined). Outside of ngRepeat, to use staggering animations + * on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this + * are that each of the elements must have the same CSS className value as well as the same parent element. A stagger operation + * will also be reset if one or more animation frames have passed since the multiple calls to `$animate` were fired. + * + * The following code will issue the **ng-leave-stagger** event on the element provided: + * + * ```js + * var kids = parent.children(); + * + * $animate.leave(kids[0]); //stagger index=0 + * $animate.leave(kids[1]); //stagger index=1 + * $animate.leave(kids[2]); //stagger index=2 + * $animate.leave(kids[3]); //stagger index=3 + * $animate.leave(kids[4]); //stagger index=4 + * + * window.requestAnimationFrame(function() { + * //stagger has reset itself + * $animate.leave(kids[5]); //stagger index=0 + * $animate.leave(kids[6]); //stagger index=1 + * + * $scope.$digest(); + * }); + * ``` + * + * Stagger animations are currently only supported within CSS-defined animations. + * + * ### The `ng-animate` CSS class + * + * When ngAnimate is animating an element it will apply the `ng-animate` CSS class to the element for the duration of the animation. + * This is a temporary CSS class and it will be removed once the animation is over (for both JavaScript and CSS-based animations). + * + * Therefore, animations can be applied to an element using this temporary class directly via CSS. + * + * ```css + * .zipper.ng-animate { + * transition:0.5s linear all; + * } + * .zipper.ng-enter { + * opacity:0; + * } + * .zipper.ng-enter.ng-enter-active { + * opacity:1; + * } + * .zipper.ng-leave { + * opacity:1; + * } + * .zipper.ng-leave.ng-leave-active { + * opacity:0; + * } + * ``` + * + * (Note that the `ng-animate` CSS class is reserved and it cannot be applied on an element directly since ngAnimate will always remove + * the CSS class once an animation has completed.) + * + * + * ### The `ng-[event]-prepare` class + * + * This is a special class that can be used to prevent unwanted flickering / flash of content before + * the actual animation starts. The class is added as soon as an animation is initialized, but removed + * before the actual animation starts (after waiting for a $digest). + * It is also only added for *structural* animations (`enter`, `move`, and `leave`). + * + * In practice, flickering can appear when nesting elements with structural animations such as `ngIf` + * into elements that have class-based animations such as `ngClass`. + * + * ```html + *
+ *
+ *
+ *
+ *
+ * ``` + * + * It is possible that during the `enter` animation, the `.message` div will be briefly visible before it starts animating. + * In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts: + * + * ```css + * .message.ng-enter-prepare { + * opacity: 0; + * } + * + * ``` + * + * ## JavaScript-based Animations + * + * ngAnimate also allows for animations to be consumed by JavaScript code. The approach is similar to CSS-based animations (where there is a shared + * CSS class that is referenced in our HTML code) but in addition we need to register the JavaScript animation on the module. By making use of the + * `module.animation()` module function we can register the animation. + * + * Let's see an example of a enter/leave animation using `ngRepeat`: + * + * ```html + *
+ * {{ item }} + *
+ * ``` + * + * See the **slide** CSS class? Let's use that class to define an animation that we'll structure in our module code by using `module.animation`: + * + * ```js + * myModule.animation('.slide', [function() { + * return { + * // make note that other events (like addClass/removeClass) + * // have different function input parameters + * enter: function(element, doneFn) { + * jQuery(element).fadeIn(1000, doneFn); + * + * // remember to call doneFn so that angular + * // knows that the animation has concluded + * }, + * + * move: function(element, doneFn) { + * jQuery(element).fadeIn(1000, doneFn); + * }, + * + * leave: function(element, doneFn) { + * jQuery(element).fadeOut(1000, doneFn); + * } + * } + * }]); + * ``` + * + * The nice thing about JS-based animations is that we can inject other services and make use of advanced animation libraries such as + * greensock.js and velocity.js. + * + * If our animation code class-based (meaning that something like `ngClass`, `ngHide` and `ngShow` triggers it) then we can still define + * our animations inside of the same registered animation, however, the function input arguments are a bit different: + * + * ```html + *
+ * this box is moody + *
+ * + * + * + * ``` + * + * ```js + * myModule.animation('.colorful', [function() { + * return { + * addClass: function(element, className, doneFn) { + * // do some cool animation and call the doneFn + * }, + * removeClass: function(element, className, doneFn) { + * // do some cool animation and call the doneFn + * }, + * setClass: function(element, addedClass, removedClass, doneFn) { + * // do some cool animation and call the doneFn + * } + * } + * }]); + * ``` + * + * ## CSS + JS Animations Together + * + * AngularJS 1.4 and higher has taken steps to make the amalgamation of CSS and JS animations more flexible. However, unlike earlier versions of Angular, + * defining CSS and JS animations to work off of the same CSS class will not work anymore. Therefore the example below will only result in **JS animations taking + * charge of the animation**: + * + * ```html + *
+ * Slide in and out + *
+ * ``` + * + * ```js + * myModule.animation('.slide', [function() { + * return { + * enter: function(element, doneFn) { + * jQuery(element).slideIn(1000, doneFn); + * } + * } + * }]); + * ``` + * + * ```css + * .slide.ng-enter { + * transition:0.5s linear all; + * transform:translateY(-100px); + * } + * .slide.ng-enter.ng-enter-active { + * transform:translateY(0); + * } + * ``` + * + * Does this mean that CSS and JS animations cannot be used together? Do JS-based animations always have higher priority? We can make up for the + * lack of CSS animations by using the `$animateCss` service to trigger our own tweaked-out, CSS-based animations directly from + * our own JS-based animation code: + * + * ```js + * myModule.animation('.slide', ['$animateCss', function($animateCss) { + * return { + * enter: function(element) { +* // this will trigger `.slide.ng-enter` and `.slide.ng-enter-active`. + * return $animateCss(element, { + * event: 'enter', + * structural: true + * }); + * } + * } + * }]); + * ``` + * + * The nice thing here is that we can save bandwidth by sticking to our CSS-based animation code and we don't need to rely on a 3rd-party animation framework. + * + * The `$animateCss` service is very powerful since we can feed in all kinds of extra properties that will be evaluated and fed into a CSS transition or + * keyframe animation. For example if we wanted to animate the height of an element while adding and removing classes then we can do so by providing that + * data into `$animateCss` directly: + * + * ```js + * myModule.animation('.slide', ['$animateCss', function($animateCss) { + * return { + * enter: function(element) { + * return $animateCss(element, { + * event: 'enter', + * structural: true, + * addClass: 'maroon-setting', + * from: { height:0 }, + * to: { height: 200 } + * }); + * } + * } + * }]); + * ``` + * + * Now we can fill in the rest via our transition CSS code: + * + * ```css + * /* the transition tells ngAnimate to make the animation happen */ + * .slide.ng-enter { transition:0.5s linear all; } + * + * /* this extra CSS class will be absorbed into the transition + * since the $animateCss code is adding the class */ + * .maroon-setting { background:red; } + * ``` + * + * And `$animateCss` will figure out the rest. Just make sure to have the `done()` callback fire the `doneFn` function to signal when the animation is over. + * + * To learn more about what's possible be sure to visit the {@link ngAnimate.$animateCss $animateCss service}. + * + * ## Animation Anchoring (via `ng-animate-ref`) + * + * ngAnimate in AngularJS 1.4 comes packed with the ability to cross-animate elements between + * structural areas of an application (like views) by pairing up elements using an attribute + * called `ng-animate-ref`. + * + * Let's say for example we have two views that are managed by `ng-view` and we want to show + * that there is a relationship between two components situated in within these views. By using the + * `ng-animate-ref` attribute we can identify that the two components are paired together and we + * can then attach an animation, which is triggered when the view changes. + * + * Say for example we have the following template code: + * + * ```html + * + *
+ *
+ * + * + * + * + * + * + * + * + * ``` + * + * Now, when the view changes (once the link is clicked), ngAnimate will examine the + * HTML contents to see if there is a match reference between any components in the view + * that is leaving and the view that is entering. It will scan both the view which is being + * removed (leave) and inserted (enter) to see if there are any paired DOM elements that + * contain a matching ref value. + * + * The two images match since they share the same ref value. ngAnimate will now create a + * transport element (which is a clone of the first image element) and it will then attempt + * to animate to the position of the second image element in the next view. For the animation to + * work a special CSS class called `ng-anchor` will be added to the transported element. + * + * We can now attach a transition onto the `.banner.ng-anchor` CSS class and then + * ngAnimate will handle the entire transition for us as well as the addition and removal of + * any changes of CSS classes between the elements: + * + * ```css + * .banner.ng-anchor { + * /* this animation will last for 1 second since there are + * two phases to the animation (an `in` and an `out` phase) */ + * transition:0.5s linear all; + * } + * ``` + * + * We also **must** include animations for the views that are being entered and removed + * (otherwise anchoring wouldn't be possible since the new view would be inserted right away). + * + * ```css + * .view-animation.ng-enter, .view-animation.ng-leave { + * transition:0.5s linear all; + * position:fixed; + * left:0; + * top:0; + * width:100%; + * } + * .view-animation.ng-enter { + * transform:translateX(100%); + * } + * .view-animation.ng-leave, + * .view-animation.ng-enter.ng-enter-active { + * transform:translateX(0%); + * } + * .view-animation.ng-leave.ng-leave-active { + * transform:translateX(-100%); + * } + * ``` + * + * Now we can jump back to the anchor animation. When the animation happens, there are two stages that occur: + * an `out` and an `in` stage. The `out` stage happens first and that is when the element is animated away + * from its origin. Once that animation is over then the `in` stage occurs which animates the + * element to its destination. The reason why there are two animations is to give enough time + * for the enter animation on the new element to be ready. + * + * The example above sets up a transition for both the in and out phases, but we can also target the out or + * in phases directly via `ng-anchor-out` and `ng-anchor-in`. + * + * ```css + * .banner.ng-anchor-out { + * transition: 0.5s linear all; + * + * /* the scale will be applied during the out animation, + * but will be animated away when the in animation runs */ + * transform: scale(1.2); + * } + * + * .banner.ng-anchor-in { + * transition: 1s linear all; + * } + * ``` + * + * + * + * + * ### Anchoring Demo + * + + + Home +
+
+
+
+
+ + angular.module('anchoringExample', ['ngAnimate', 'ngRoute']) + .config(['$routeProvider', function($routeProvider) { + $routeProvider.when('/', { + templateUrl: 'home.html', + controller: 'HomeController as home' + }); + $routeProvider.when('/profile/:id', { + templateUrl: 'profile.html', + controller: 'ProfileController as profile' + }); + }]) + .run(['$rootScope', function($rootScope) { + $rootScope.records = [ + { id:1, title: "Miss Beulah Roob" }, + { id:2, title: "Trent Morissette" }, + { id:3, title: "Miss Ava Pouros" }, + { id:4, title: "Rod Pouros" }, + { id:5, title: "Abdul Rice" }, + { id:6, title: "Laurie Rutherford Sr." }, + { id:7, title: "Nakia McLaughlin" }, + { id:8, title: "Jordon Blanda DVM" }, + { id:9, title: "Rhoda Hand" }, + { id:10, title: "Alexandrea Sauer" } + ]; + }]) + .controller('HomeController', [function() { + //empty + }]) + .controller('ProfileController', ['$rootScope', '$routeParams', function($rootScope, $routeParams) { + var index = parseInt($routeParams.id, 10); + var record = $rootScope.records[index - 1]; + + this.title = record.title; + this.id = record.id; + }]); + + +

Welcome to the home page

+

Please click on an element

+ + {{ record.title }} + +
+ +
+ {{ profile.title }} +
+
+ + .record { + display:block; + font-size:20px; + } + .profile { + background:black; + color:white; + font-size:100px; + } + .view-container { + position:relative; + } + .view-container > .view.ng-animate { + position:absolute; + top:0; + left:0; + width:100%; + min-height:500px; + } + .view.ng-enter, .view.ng-leave, + .record.ng-anchor { + transition:0.5s linear all; + } + .view.ng-enter { + transform:translateX(100%); + } + .view.ng-enter.ng-enter-active, .view.ng-leave { + transform:translateX(0%); + } + .view.ng-leave.ng-leave-active { + transform:translateX(-100%); + } + .record.ng-anchor-out { + background:red; + } + +
+ * + * ### How is the element transported? + * + * When an anchor animation occurs, ngAnimate will clone the starting element and position it exactly where the starting + * element is located on screen via absolute positioning. The cloned element will be placed inside of the root element + * of the application (where ng-app was defined) and all of the CSS classes of the starting element will be applied. The + * element will then animate into the `out` and `in` animations and will eventually reach the coordinates and match + * the dimensions of the destination element. During the entire animation a CSS class of `.ng-animate-shim` will be applied + * to both the starting and destination elements in order to hide them from being visible (the CSS styling for the class + * is: `visibility:hidden`). Once the anchor reaches its destination then it will be removed and the destination element + * will become visible since the shim class will be removed. + * + * ### How is the morphing handled? + * + * CSS Anchoring relies on transitions and keyframes and the internal code is intelligent enough to figure out + * what CSS classes differ between the starting element and the destination element. These different CSS classes + * will be added/removed on the anchor element and a transition will be applied (the transition that is provided + * in the anchor class). Long story short, ngAnimate will figure out what classes to add and remove which will + * make the transition of the element as smooth and automatic as possible. Be sure to use simple CSS classes that + * do not rely on DOM nesting structure so that the anchor element appears the same as the starting element (since + * the cloned element is placed inside of root element which is likely close to the body element). + * + * Note that if the root element is on the `` element then the cloned node will be placed inside of body. + * + * + * ## Using $animate in your directive code + * + * So far we've explored how to feed in animations into an Angular application, but how do we trigger animations within our own directives in our application? + * By injecting the `$animate` service into our directive code, we can trigger structural and class-based hooks which can then be consumed by animations. Let's + * imagine we have a greeting box that shows and hides itself when the data changes + * + * ```html + * Hi there + * ``` + * + * ```js + * ngModule.directive('greetingBox', ['$animate', function($animate) { + * return function(scope, element, attrs) { + * attrs.$observe('active', function(value) { + * value ? $animate.addClass(element, 'on') : $animate.removeClass(element, 'on'); + * }); + * }); + * }]); + * ``` + * + * Now the `on` CSS class is added and removed on the greeting box component. Now if we add a CSS class on top of the greeting box element + * in our HTML code then we can trigger a CSS or JS animation to happen. + * + * ```css + * /* normally we would create a CSS class to reference on the element */ + * greeting-box.on { transition:0.5s linear all; background:green; color:white; } + * ``` + * + * The `$animate` service contains a variety of other methods like `enter`, `leave`, `animate` and `setClass`. To learn more about what's + * possible be sure to visit the {@link ng.$animate $animate service API page}. + * + * + * ### Preventing Collisions With Third Party Libraries + * + * Some third-party frameworks place animation duration defaults across many element or className + * selectors in order to make their code small and reuseable. This can lead to issues with ngAnimate, which + * is expecting actual animations on these elements and has to wait for their completion. + * + * You can prevent this unwanted behavior by using a prefix on all your animation classes: + * + * ```css + * /* prefixed with animate- */ + * .animate-fade-add.animate-fade-add-active { + * transition:1s linear all; + * opacity:0; + * } + * ``` + * + * You then configure `$animate` to enforce this prefix: + * + * ```js + * $animateProvider.classNameFilter(/animate-/); + * ``` + * + * This also may provide your application with a speed boost since only specific elements containing CSS class prefix + * will be evaluated for animation when any DOM changes occur in the application. + * + * ## Callbacks and Promises + * + * When `$animate` is called it returns a promise that can be used to capture when the animation has ended. Therefore if we were to trigger + * an animation (within our directive code) then we can continue performing directive and scope related activities after the animation has + * ended by chaining onto the returned promise that animation method returns. + * + * ```js + * // somewhere within the depths of the directive + * $animate.enter(element, parent).then(function() { + * //the animation has completed + * }); + * ``` + * + * (Note that earlier versions of Angular prior to v1.4 required the promise code to be wrapped using `$scope.$apply(...)`. This is not the case + * anymore.) + * + * In addition to the animation promise, we can also make use of animation-related callbacks within our directives and controller code by registering + * an event listener using the `$animate` service. Let's say for example that an animation was triggered on our view + * routing controller to hook into that: + * + * ```js + * ngModule.controller('HomePageController', ['$animate', function($animate) { + * $animate.on('enter', ngViewElement, function(element) { + * // the animation for this route has completed + * }]); + * }]) + * ``` + * + * (Note that you will need to trigger a digest within the callback to get angular to notice any scope-related changes.) + */ + +/** + * @ngdoc service + * @name $animate + * @kind object + * + * @description + * The ngAnimate `$animate` service documentation is the same for the core `$animate` service. + * + * Click here {@link ng.$animate to learn more about animations with `$animate`}. + */ +angular.module('ngAnimate', []) + .directive('ngAnimateSwap', ngAnimateSwapDirective) + + .directive('ngAnimateChildren', $$AnimateChildrenDirective) + .factory('$$rAFScheduler', $$rAFSchedulerFactory) + + .provider('$$animateQueue', $$AnimateQueueProvider) + .provider('$$animation', $$AnimationProvider) + + .provider('$animateCss', $AnimateCssProvider) + .provider('$$animateCssDriver', $$AnimateCssDriverProvider) + + .provider('$$animateJs', $$AnimateJsProvider) + .provider('$$animateJsDriver', $$AnimateJsDriverProvider); + + +})(window, window.angular); diff --git a/ecomp-portal-FE/client/bower_components/angular-animate/angular-animate.min.js b/ecomp-portal-FE/client/bower_components/angular-animate/angular-animate.min.js new file mode 100644 index 00000000..8e8e41df --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-animate/angular-animate.min.js @@ -0,0 +1,56 @@ +/* + AngularJS v1.5.0 + (c) 2010-2016 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(D,r,Va){'use strict';function ya(a,b,c){if(!a)throw Ka("areq",b||"?",c||"required");return a}function za(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;ba(a)&&(a=a.join(" "));ba(b)&&(b=b.join(" "));return a+" "+b}function La(a){var b={};a&&(a.to||a.from)&&(b.to=a.to,b.from=a.from);return b}function X(a,b,c){var d="";a=ba(a)?a:a&&R(a)&&a.length?a.split(/\s+/):[];s(a,function(a,g){a&&0=a&&(a=e,e=0,b.push(t),t=[]);t.push(g.fn);g.children.forEach(function(a){e++; +c.push(a)});a--}t.length&&b.push(t);return b}(c)}var M=[],r=U(a);return function(u,A,v){function z(a){a=a.hasAttribute("ng-animate-ref")?[a]:a.querySelectorAll("[ng-animate-ref]");var b=[];s(a,function(a){var c=a.getAttribute("ng-animate-ref");c&&c.length&&b.push(a)});return b}function K(a){var b=[],c={};s(a,function(a,f){var d=G(a.element),h=0<=["enter","move"].indexOf(a.event),d=a.structural?z(d):[];if(d.length){var e=h?"to":"from";s(d,function(a){var b=a.getAttribute("ng-animate-ref");c[b]=c[b]|| +{};c[b][e]={animationID:f,element:I(a)}})}else b.push(a)});var d={},h={};s(c,function(c,e){var l=c.from,t=c.to;if(l&&t){var g=a[l.animationID],E=a[t.animationID],k=l.animationID.toString();if(!h[k]){var z=h[k]={structural:!0,beforeStart:function(){g.beforeStart();E.beforeStart()},close:function(){g.close();E.close()},classes:J(g.classes,E.classes),from:g,to:E,anchors:[]};z.classes.length?b.push(z):(b.push(g),b.push(E))}h[k].anchors.push({out:l.element,"in":t.element})}else l=l?l.animationID:t.animationID, +t=l.toString(),d[t]||(d[t]=!0,b.push(a[l]))});return b}function J(a,b){a=a.split(" ");b=b.split(" ");for(var c=[],d=0;d=P&&b>=O&&(wa=!0,q())}function L(){function b(){if(!A){t(!1);s(m, +function(a){l.style[a[0]]=a[1]});z(a,f);e.addClass(a,ca);if(p.recalculateTimingStyles){ja=l.className+" "+da;ga=r(l,ja);F=v(l,ja,ga);$=F.maxDelay;n=Math.max($,0);O=F.maxDuration;if(0===O){q();return}p.hasTransitions=0B.expectedEndTime)?H.cancel(B.timer):g.push(q)}L&&(k=H(c,k,!1),g[0]={timer:k,expectedEndTime:d},g.push(q),a.data("$$animateCss",g));if(ea.length)a.on(ea.join(" "),E);f.to&&(f.cleanupStyles&&Ga(x,l,Object.keys(f.to)),Ba(a, +f))}}function c(){var b=a.data("$$animateCss");if(b){for(var d=1;d", + "license": "MIT", + "bugs": { + "url": "https://github.com/angular/angular.js/issues" + }, + "homepage": "http://angularjs.org" +} diff --git a/ecomp-portal-FE/client/bower_components/angular-aria/.bower.json b/ecomp-portal-FE/client/bower_components/angular-aria/.bower.json new file mode 100644 index 00000000..ee454ab5 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-aria/.bower.json @@ -0,0 +1,20 @@ +{ + "name": "angular-aria", + "version": "1.5.0", + "license": "MIT", + "main": "./angular-aria.js", + "ignore": [], + "dependencies": { + "angular": "1.5.0" + }, + "homepage": "https://github.com/angular/bower-angular-aria", + "_release": "1.5.0", + "_resolution": { + "type": "version", + "tag": "v1.5.0", + "commit": "39f42383b9607ba9750da5212ec66998a0f5577e" + }, + "_source": "https://github.com/angular/bower-angular-aria.git", + "_target": "1.5.0", + "_originalSource": "angular-aria" +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-aria/README.md b/ecomp-portal-FE/client/bower_components/angular-aria/README.md new file mode 100644 index 00000000..04c5a8f9 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-aria/README.md @@ -0,0 +1,67 @@ +# packaged angular-aria + +This repo is for distribution on `npm` and `bower`. The source for this module is in the +[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngAria). +Please file issues and pull requests against that repo. + +## Install + +You can install this package either with `npm` or with `bower`. + +### npm + +```shell +npm install angular-aria +``` +Then add `ngAria` as a dependency for your app: + +```javascript +angular.module('myApp', [require('angular-aria')]); +``` + +### bower + +```shell +bower install angular-aria +``` + +Add a ` +``` + +Then add `ngAria` as a dependency for your app: + +```javascript +angular.module('myApp', ['ngAria']); +``` + +## Documentation + +Documentation is available on the +[AngularJS docs site](http://docs.angularjs.org/api/ngAria). + +## License + +The MIT License + +Copyright (c) 2010-2015 Google, Inc. http://angularjs.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ecomp-portal-FE/client/bower_components/angular-aria/angular-aria.js b/ecomp-portal-FE/client/bower_components/angular-aria/angular-aria.js new file mode 100644 index 00000000..99f5cc42 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-aria/angular-aria.js @@ -0,0 +1,398 @@ +/** + * @license AngularJS v1.5.0 + * (c) 2010-2016 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, angular, undefined) {'use strict'; + +/** + * @ngdoc module + * @name ngAria + * @description + * + * The `ngAria` module provides support for common + * [ARIA](http://www.w3.org/TR/wai-aria/) + * attributes that convey state or semantic information about the application for users + * of assistive technologies, such as screen readers. + * + *
+ * + * ## Usage + * + * For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following + * directives are supported: + * `ngModel`, `ngChecked`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`, + * `ngDblClick`, and `ngMessages`. + * + * Below is a more detailed breakdown of the attributes handled by ngAria: + * + * | Directive | Supported Attributes | + * |---------------------------------------------|----------------------------------------------------------------------------------------| + * | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles | + * | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled | + * | {@link ng.directive:ngRequired ngRequired} | aria-required | + * | {@link ng.directive:ngChecked ngChecked} | aria-checked | + * | {@link ng.directive:ngValue ngValue} | aria-checked | + * | {@link ng.directive:ngShow ngShow} | aria-hidden | + * | {@link ng.directive:ngHide ngHide} | aria-hidden | + * | {@link ng.directive:ngDblclick ngDblclick} | tabindex | + * | {@link module:ngMessages ngMessages} | aria-live | + * | {@link ng.directive:ngClick ngClick} | tabindex, keypress event, button role | + * + * Find out more information about each directive by reading the + * {@link guide/accessibility ngAria Developer Guide}. + * + * ##Example + * Using ngDisabled with ngAria: + * ```html + * + * ``` + * Becomes: + * ```html + * + * ``` + * + * ##Disabling Attributes + * It's possible to disable individual attributes added by ngAria with the + * {@link ngAria.$ariaProvider#config config} method. For more details, see the + * {@link guide/accessibility Developer Guide}. + */ + /* global -ngAriaModule */ +var ngAriaModule = angular.module('ngAria', ['ng']). + provider('$aria', $AriaProvider); + +/** +* Internal Utilities +*/ +var nodeBlackList = ['BUTTON', 'A', 'INPUT', 'TEXTAREA', 'SELECT', 'DETAILS', 'SUMMARY']; + +var isNodeOneOf = function(elem, nodeTypeArray) { + if (nodeTypeArray.indexOf(elem[0].nodeName) !== -1) { + return true; + } +}; +/** + * @ngdoc provider + * @name $ariaProvider + * + * @description + * + * Used for configuring the ARIA attributes injected and managed by ngAria. + * + * ```js + * angular.module('myApp', ['ngAria'], function config($ariaProvider) { + * $ariaProvider.config({ + * ariaValue: true, + * tabindex: false + * }); + * }); + *``` + * + * ## Dependencies + * Requires the {@link ngAria} module to be installed. + * + */ +function $AriaProvider() { + var config = { + ariaHidden: true, + ariaChecked: true, + ariaDisabled: true, + ariaRequired: true, + ariaInvalid: true, + ariaValue: true, + tabindex: true, + bindKeypress: true, + bindRoleForClick: true + }; + + /** + * @ngdoc method + * @name $ariaProvider#config + * + * @param {object} config object to enable/disable specific ARIA attributes + * + * - **ariaHidden** – `{boolean}` – Enables/disables aria-hidden tags + * - **ariaChecked** – `{boolean}` – Enables/disables aria-checked tags + * - **ariaDisabled** – `{boolean}` – Enables/disables aria-disabled tags + * - **ariaRequired** – `{boolean}` – Enables/disables aria-required tags + * - **ariaInvalid** – `{boolean}` – Enables/disables aria-invalid tags + * - **ariaValue** – `{boolean}` – Enables/disables aria-valuemin, aria-valuemax and aria-valuenow tags + * - **tabindex** – `{boolean}` – Enables/disables tabindex tags + * - **bindKeypress** – `{boolean}` – Enables/disables keypress event binding on `div` and + * `li` elements with ng-click + * - **bindRoleForClick** – `{boolean}` – Adds role=button to non-interactive elements like `div` + * using ng-click, making them more accessible to users of assistive technologies + * + * @description + * Enables/disables various ARIA attributes + */ + this.config = function(newConfig) { + config = angular.extend(config, newConfig); + }; + + function watchExpr(attrName, ariaAttr, nodeBlackList, negate) { + return function(scope, elem, attr) { + var ariaCamelName = attr.$normalize(ariaAttr); + if (config[ariaCamelName] && !isNodeOneOf(elem, nodeBlackList) && !attr[ariaCamelName]) { + scope.$watch(attr[attrName], function(boolVal) { + // ensure boolean value + boolVal = negate ? !boolVal : !!boolVal; + elem.attr(ariaAttr, boolVal); + }); + } + }; + } + /** + * @ngdoc service + * @name $aria + * + * @description + * @priority 200 + * + * The $aria service contains helper methods for applying common + * [ARIA](http://www.w3.org/TR/wai-aria/) attributes to HTML directives. + * + * ngAria injects common accessibility attributes that tell assistive technologies when HTML + * elements are enabled, selected, hidden, and more. To see how this is performed with ngAria, + * let's review a code snippet from ngAria itself: + * + *```js + * ngAriaModule.directive('ngDisabled', ['$aria', function($aria) { + * return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false); + * }]) + *``` + * Shown above, the ngAria module creates a directive with the same signature as the + * traditional `ng-disabled` directive. But this ngAria version is dedicated to + * solely managing accessibility attributes on custom elements. The internal `$aria` service is + * used to watch the boolean attribute `ngDisabled`. If it has not been explicitly set by the + * developer, `aria-disabled` is injected as an attribute with its value synchronized to the + * value in `ngDisabled`. + * + * Because ngAria hooks into the `ng-disabled` directive, developers do not have to do + * anything to enable this feature. The `aria-disabled` attribute is automatically managed + * simply as a silent side-effect of using `ng-disabled` with the ngAria module. + * + * The full list of directives that interface with ngAria: + * * **ngModel** + * * **ngChecked** + * * **ngRequired** + * * **ngDisabled** + * * **ngValue** + * * **ngShow** + * * **ngHide** + * * **ngClick** + * * **ngDblclick** + * * **ngMessages** + * + * Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each + * directive. + * + * + * ## Dependencies + * Requires the {@link ngAria} module to be installed. + */ + this.$get = function() { + return { + config: function(key) { + return config[key]; + }, + $$watchExpr: watchExpr + }; + }; +} + + +ngAriaModule.directive('ngShow', ['$aria', function($aria) { + return $aria.$$watchExpr('ngShow', 'aria-hidden', [], true); +}]) +.directive('ngHide', ['$aria', function($aria) { + return $aria.$$watchExpr('ngHide', 'aria-hidden', [], false); +}]) +.directive('ngValue', ['$aria', function($aria) { + return $aria.$$watchExpr('ngValue', 'aria-checked', nodeBlackList, false); +}]) +.directive('ngChecked', ['$aria', function($aria) { + return $aria.$$watchExpr('ngChecked', 'aria-checked', nodeBlackList, false); +}]) +.directive('ngRequired', ['$aria', function($aria) { + return $aria.$$watchExpr('ngRequired', 'aria-required', nodeBlackList, false); +}]) +.directive('ngModel', ['$aria', function($aria) { + + function shouldAttachAttr(attr, normalizedAttr, elem, allowBlacklistEls) { + return $aria.config(normalizedAttr) && !elem.attr(attr) && (allowBlacklistEls || !isNodeOneOf(elem, nodeBlackList)); + } + + function shouldAttachRole(role, elem) { + // if element does not have role attribute + // AND element type is equal to role (if custom element has a type equaling shape) <-- remove? + // AND element is not INPUT + return !elem.attr('role') && (elem.attr('type') === role) && (elem[0].nodeName !== 'INPUT'); + } + + function getShape(attr, elem) { + var type = attr.type, + role = attr.role; + + return ((type || role) === 'checkbox' || role === 'menuitemcheckbox') ? 'checkbox' : + ((type || role) === 'radio' || role === 'menuitemradio') ? 'radio' : + (type === 'range' || role === 'progressbar' || role === 'slider') ? 'range' : ''; + } + + return { + restrict: 'A', + require: 'ngModel', + priority: 200, //Make sure watches are fired after any other directives that affect the ngModel value + compile: function(elem, attr) { + var shape = getShape(attr, elem); + + return { + pre: function(scope, elem, attr, ngModel) { + if (shape === 'checkbox') { + //Use the input[checkbox] $isEmpty implementation for elements with checkbox roles + ngModel.$isEmpty = function(value) { + return value === false; + }; + } + }, + post: function(scope, elem, attr, ngModel) { + var needsTabIndex = shouldAttachAttr('tabindex', 'tabindex', elem, false); + + function ngAriaWatchModelValue() { + return ngModel.$modelValue; + } + + function getRadioReaction(newVal) { + var boolVal = (attr.value == ngModel.$viewValue); + elem.attr('aria-checked', boolVal); + } + + function getCheckboxReaction() { + elem.attr('aria-checked', !ngModel.$isEmpty(ngModel.$viewValue)); + } + + switch (shape) { + case 'radio': + case 'checkbox': + if (shouldAttachRole(shape, elem)) { + elem.attr('role', shape); + } + if (shouldAttachAttr('aria-checked', 'ariaChecked', elem, false)) { + scope.$watch(ngAriaWatchModelValue, shape === 'radio' ? + getRadioReaction : getCheckboxReaction); + } + if (needsTabIndex) { + elem.attr('tabindex', 0); + } + break; + case 'range': + if (shouldAttachRole(shape, elem)) { + elem.attr('role', 'slider'); + } + if ($aria.config('ariaValue')) { + var needsAriaValuemin = !elem.attr('aria-valuemin') && + (attr.hasOwnProperty('min') || attr.hasOwnProperty('ngMin')); + var needsAriaValuemax = !elem.attr('aria-valuemax') && + (attr.hasOwnProperty('max') || attr.hasOwnProperty('ngMax')); + var needsAriaValuenow = !elem.attr('aria-valuenow'); + + if (needsAriaValuemin) { + attr.$observe('min', function ngAriaValueMinReaction(newVal) { + elem.attr('aria-valuemin', newVal); + }); + } + if (needsAriaValuemax) { + attr.$observe('max', function ngAriaValueMinReaction(newVal) { + elem.attr('aria-valuemax', newVal); + }); + } + if (needsAriaValuenow) { + scope.$watch(ngAriaWatchModelValue, function ngAriaValueNowReaction(newVal) { + elem.attr('aria-valuenow', newVal); + }); + } + } + if (needsTabIndex) { + elem.attr('tabindex', 0); + } + break; + } + + if (!attr.hasOwnProperty('ngRequired') && ngModel.$validators.required + && shouldAttachAttr('aria-required', 'ariaRequired', elem, false)) { + // ngModel.$error.required is undefined on custom controls + attr.$observe('required', function() { + elem.attr('aria-required', !!attr['required']); + }); + } + + if (shouldAttachAttr('aria-invalid', 'ariaInvalid', elem, true)) { + scope.$watch(function ngAriaInvalidWatch() { + return ngModel.$invalid; + }, function ngAriaInvalidReaction(newVal) { + elem.attr('aria-invalid', !!newVal); + }); + } + } + }; + } + }; +}]) +.directive('ngDisabled', ['$aria', function($aria) { + return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false); +}]) +.directive('ngMessages', function() { + return { + restrict: 'A', + require: '?ngMessages', + link: function(scope, elem, attr, ngMessages) { + if (!elem.attr('aria-live')) { + elem.attr('aria-live', 'assertive'); + } + } + }; +}) +.directive('ngClick',['$aria', '$parse', function($aria, $parse) { + return { + restrict: 'A', + compile: function(elem, attr) { + var fn = $parse(attr.ngClick, /* interceptorFn */ null, /* expensiveChecks */ true); + return function(scope, elem, attr) { + + if (!isNodeOneOf(elem, nodeBlackList)) { + + if ($aria.config('bindRoleForClick') && !elem.attr('role')) { + elem.attr('role', 'button'); + } + + if ($aria.config('tabindex') && !elem.attr('tabindex')) { + elem.attr('tabindex', 0); + } + + if ($aria.config('bindKeypress') && !attr.ngKeypress) { + elem.on('keypress', function(event) { + var keyCode = event.which || event.keyCode; + if (keyCode === 32 || keyCode === 13) { + scope.$apply(callback); + } + + function callback() { + fn(scope, { $event: event }); + } + }); + } + } + }; + } + }; +}]) +.directive('ngDblclick', ['$aria', function($aria) { + return function(scope, elem, attr) { + if ($aria.config('tabindex') && !elem.attr('tabindex') && !isNodeOneOf(elem, nodeBlackList)) { + elem.attr('tabindex', 0); + } + }; +}]); + + +})(window, window.angular); diff --git a/ecomp-portal-FE/client/bower_components/angular-aria/angular-aria.min.js b/ecomp-portal-FE/client/bower_components/angular-aria/angular-aria.min.js new file mode 100644 index 00000000..cf0fd742 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-aria/angular-aria.min.js @@ -0,0 +1,14 @@ +/* + AngularJS v1.5.0 + (c) 2010-2016 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(s,q,t){'use strict';var f="BUTTON A INPUT TEXTAREA SELECT DETAILS SUMMARY".split(" "),l=function(a,c){if(-1!==c.indexOf(a[0].nodeName))return!0};q.module("ngAria",["ng"]).provider("$aria",function(){function a(a,h,p,n){return function(d,e,b){var g=b.$normalize(h);!c[g]||l(e,p)||b[g]||d.$watch(b[a],function(b){b=n?!b:!!b;e.attr(h,b)})}}var c={ariaHidden:!0,ariaChecked:!0,ariaDisabled:!0,ariaRequired:!0,ariaInvalid:!0,ariaValue:!0,tabindex:!0,bindKeypress:!0,bindRoleForClick:!0};this.config= +function(a){c=q.extend(c,a)};this.$get=function(){return{config:function(a){return c[a]},$$watchExpr:a}}}).directive("ngShow",["$aria",function(a){return a.$$watchExpr("ngShow","aria-hidden",[],!0)}]).directive("ngHide",["$aria",function(a){return a.$$watchExpr("ngHide","aria-hidden",[],!1)}]).directive("ngValue",["$aria",function(a){return a.$$watchExpr("ngValue","aria-checked",f,!1)}]).directive("ngChecked",["$aria",function(a){return a.$$watchExpr("ngChecked","aria-checked",f,!1)}]).directive("ngRequired", +["$aria",function(a){return a.$$watchExpr("ngRequired","aria-required",f,!1)}]).directive("ngModel",["$aria",function(a){function c(c,n,d,e){return a.config(n)&&!d.attr(c)&&(e||!l(d,f))}function m(a,c){return!c.attr("role")&&c.attr("type")===a&&"INPUT"!==c[0].nodeName}function h(a,c){var d=a.type,e=a.role;return"checkbox"===(d||e)||"menuitemcheckbox"===e?"checkbox":"radio"===(d||e)||"menuitemradio"===e?"radio":"range"===d||"progressbar"===e||"slider"===e?"range":""}return{restrict:"A",require:"ngModel", +priority:200,compile:function(f,n){var d=h(n,f);return{pre:function(a,b,c,k){"checkbox"===d&&(k.$isEmpty=function(a){return!1===a})},post:function(e,b,g,k){function f(){return k.$modelValue}function h(a){b.attr("aria-checked",g.value==k.$viewValue)}function n(){b.attr("aria-checked",!k.$isEmpty(k.$viewValue))}var l=c("tabindex","tabindex",b,!1);switch(d){case "radio":case "checkbox":m(d,b)&&b.attr("role",d);c("aria-checked","ariaChecked",b,!1)&&e.$watch(f,"radio"===d?h:n);l&&b.attr("tabindex",0); +break;case "range":m(d,b)&&b.attr("role","slider");if(a.config("ariaValue")){var p=!b.attr("aria-valuemin")&&(g.hasOwnProperty("min")||g.hasOwnProperty("ngMin")),q=!b.attr("aria-valuemax")&&(g.hasOwnProperty("max")||g.hasOwnProperty("ngMax")),r=!b.attr("aria-valuenow");p&&g.$observe("min",function(a){b.attr("aria-valuemin",a)});q&&g.$observe("max",function(a){b.attr("aria-valuemax",a)});r&&e.$watch(f,function(a){b.attr("aria-valuenow",a)})}l&&b.attr("tabindex",0)}!g.hasOwnProperty("ngRequired")&& +k.$validators.required&&c("aria-required","ariaRequired",b,!1)&&g.$observe("required",function(){b.attr("aria-required",!!g.required)});c("aria-invalid","ariaInvalid",b,!0)&&e.$watch(function(){return k.$invalid},function(a){b.attr("aria-invalid",!!a)})}}}}}]).directive("ngDisabled",["$aria",function(a){return a.$$watchExpr("ngDisabled","aria-disabled",f,!1)}]).directive("ngMessages",function(){return{restrict:"A",require:"?ngMessages",link:function(a,c,f,h){c.attr("aria-live")||c.attr("aria-live", +"assertive")}}}).directive("ngClick",["$aria","$parse",function(a,c){return{restrict:"A",compile:function(m,h){var p=c(h.ngClick,null,!0);return function(c,d,e){if(!l(d,f)&&(a.config("bindRoleForClick")&&!d.attr("role")&&d.attr("role","button"),a.config("tabindex")&&!d.attr("tabindex")&&d.attr("tabindex",0),a.config("bindKeypress")&&!e.ngKeypress))d.on("keypress",function(a){function d(){p(c,{$event:a})}var e=a.which||a.keyCode;32!==e&&13!==e||c.$apply(d)})}}}}]).directive("ngDblclick",["$aria",function(a){return function(c, +m,h){!a.config("tabindex")||m.attr("tabindex")||l(m,f)||m.attr("tabindex",0)}}])})(window,window.angular); +//# sourceMappingURL=angular-aria.min.js.map diff --git a/ecomp-portal-FE/client/bower_components/angular-aria/angular-aria.min.js.map b/ecomp-portal-FE/client/bower_components/angular-aria/angular-aria.min.js.map new file mode 100644 index 00000000..b1db15b5 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-aria/angular-aria.min.js.map @@ -0,0 +1,8 @@ +{ +"version":3, +"file":"angular-aria.min.js", +"lineCount":13, +"mappings":"A;;;;;aAKC,SAAQ,CAACA,CAAD,CAASC,CAAT,CAAkBC,CAAlB,CAA6B,CA6DtC,IAAIC,EAAgB,gDAAA,MAAA,CAAA,GAAA,CAApB,CAEIC,EAAcA,QAAQ,CAACC,CAAD,CAAOC,CAAP,CAAsB,CAC9C,GAAiD,EAAjD,GAAIA,CAAAC,QAAA,CAAsBF,CAAA,CAAK,CAAL,CAAAG,SAAtB,CAAJ,CACE,MAAO,CAAA,CAFqC,CAR7BP,EAAAQ,OAAA,CAAe,QAAf,CAAyB,CAAC,IAAD,CAAzB,CAAAC,SAAAC,CACc,OADdA,CAkCnBC,QAAsB,EAAG,CAsCvBC,QAASA,EAAS,CAACC,CAAD,CAAWC,CAAX,CAAqBZ,CAArB,CAAoCa,CAApC,CAA4C,CAC5D,MAAO,SAAQ,CAACC,CAAD,CAAQZ,CAAR,CAAca,CAAd,CAAoB,CACjC,IAAIC,EAAgBD,CAAAE,WAAA,CAAgBL,CAAhB,CAChB,EAAAM,CAAA,CAAOF,CAAP,CAAJ,EAA8Bf,CAAA,CAAYC,CAAZ,CAAkBF,CAAlB,CAA9B,EAAmEe,CAAA,CAAKC,CAAL,CAAnE,EACEF,CAAAK,OAAA,CAAaJ,CAAA,CAAKJ,CAAL,CAAb,CAA6B,QAAQ,CAACS,CAAD,CAAU,CAE7CA,CAAA,CAAUP,CAAA,CAAS,CAACO,CAAV,CAAoB,CAAEA,CAAAA,CAChClB,EAAAa,KAAA,CAAUH,CAAV,CAAoBQ,CAApB,CAH6C,CAA/C,CAH+B,CADyB,CArC9D,IAAIF,EAAS,CACXG,WAAY,CAAA,CADD,CAEXC,YAAa,CAAA,CAFF,CAGXC,aAAc,CAAA,CAHH,CAIXC,aAAc,CAAA,CAJH,CAKXC,YAAa,CAAA,CALF,CAMXC,UAAW,CAAA,CANA,CAOXC,SAAU,CAAA,CAPC,CAQXC,aAAc,CAAA,CARH,CASXC,iBAAkB,CAAA,CATP,CAiCb,KAAAX,OAAA;AAAcY,QAAQ,CAACC,CAAD,CAAY,CAChCb,CAAA,CAASpB,CAAAkC,OAAA,CAAed,CAAf,CAAuBa,CAAvB,CADuB,CAiElC,KAAAE,KAAA,CAAYC,QAAQ,EAAG,CACrB,MAAO,CACLhB,OAAQA,QAAQ,CAACiB,CAAD,CAAM,CACpB,MAAOjB,EAAA,CAAOiB,CAAP,CADa,CADjB,CAILC,YAAa1B,CAJR,CADc,CAnGA,CAlCNF,CAgJnB6B,UAAA,CAAuB,QAAvB,CAAiC,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CACzD,MAAOA,EAAAF,YAAA,CAAkB,QAAlB,CAA4B,aAA5B,CAA2C,EAA3C,CAA+C,CAAA,CAA/C,CADkD,CAA1B,CAAjC,CAAAC,UAAA,CAGW,QAHX,CAGqB,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CAC7C,MAAOA,EAAAF,YAAA,CAAkB,QAAlB,CAA4B,aAA5B,CAA2C,EAA3C,CAA+C,CAAA,CAA/C,CADsC,CAA1B,CAHrB,CAAAC,UAAA,CAMW,SANX,CAMsB,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CAC9C,MAAOA,EAAAF,YAAA,CAAkB,SAAlB,CAA6B,cAA7B,CAA6CpC,CAA7C,CAA4D,CAAA,CAA5D,CADuC,CAA1B,CANtB,CAAAqC,UAAA,CASW,WATX,CASwB,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CAChD,MAAOA,EAAAF,YAAA,CAAkB,WAAlB,CAA+B,cAA/B,CAA+CpC,CAA/C,CAA8D,CAAA,CAA9D,CADyC,CAA1B,CATxB,CAAAqC,UAAA,CAYW,YAZX;AAYyB,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CACjD,MAAOA,EAAAF,YAAA,CAAkB,YAAlB,CAAgC,eAAhC,CAAiDpC,CAAjD,CAAgE,CAAA,CAAhE,CAD0C,CAA1B,CAZzB,CAAAqC,UAAA,CAeW,SAfX,CAesB,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CAE9CC,QAASA,EAAgB,CAACxB,CAAD,CAAOyB,CAAP,CAAuBtC,CAAvB,CAA6BuC,CAA7B,CAAgD,CACvE,MAAOH,EAAApB,OAAA,CAAasB,CAAb,CAAP,EAAuC,CAACtC,CAAAa,KAAA,CAAUA,CAAV,CAAxC,GAA4D0B,CAA5D,EAAiF,CAACxC,CAAA,CAAYC,CAAZ,CAAkBF,CAAlB,CAAlF,CADuE,CAIzE0C,QAASA,EAAgB,CAACC,CAAD,CAAOzC,CAAP,CAAa,CAIpC,MAAO,CAACA,CAAAa,KAAA,CAAU,MAAV,CAAR,EAA8Bb,CAAAa,KAAA,CAAU,MAAV,CAA9B,GAAoD4B,CAApD,EAAmF,OAAnF,GAA8DzC,CAAA,CAAK,CAAL,CAAAG,SAJ1B,CAOtCuC,QAASA,EAAQ,CAAC7B,CAAD,CAAOb,CAAP,CAAa,CAAA,IACxB2C,EAAO9B,CAAA8B,KADiB,CAExBF,EAAO5B,CAAA4B,KAEX,OAA2B,UAApB,IAAEE,CAAF,EAAUF,CAAV,GAA2C,kBAA3C,GAAkCA,CAAlC,CAAiE,UAAjE,CACoB,OAApB,IAAEE,CAAF,EAAUF,CAAV,GAA2C,eAA3C,GAAkCA,CAAlC,CAA8D,OAA9D,CACU,OAAV,GAACE,CAAD,EAA2C,aAA3C,GAAkCF,CAAlC,EAAqE,QAArE,GAA4DA,CAA5D,CAAiF,OAAjF,CAA2F,EANtE,CAS9B,MAAO,CACLG,SAAU,GADL,CAELC,QAAS,SAFJ;AAGLC,SAAU,GAHL,CAILC,QAASA,QAAQ,CAAC/C,CAAD,CAAOa,CAAP,CAAa,CAC5B,IAAImC,EAAQN,CAAA,CAAS7B,CAAT,CAAeb,CAAf,CAEZ,OAAO,CACLiD,IAAKA,QAAQ,CAACrC,CAAD,CAAQZ,CAAR,CAAca,CAAd,CAAoBqC,CAApB,CAA6B,CAC1B,UAAd,GAAIF,CAAJ,GAEEE,CAAAC,SAFF,CAEqBC,QAAQ,CAACC,CAAD,CAAQ,CACjC,MAAiB,CAAA,CAAjB,GAAOA,CAD0B,CAFrC,CADwC,CADrC,CASLC,KAAMA,QAAQ,CAAC1C,CAAD,CAAQZ,CAAR,CAAca,CAAd,CAAoBqC,CAApB,CAA6B,CAGzCK,QAASA,EAAqB,EAAG,CAC/B,MAAOL,EAAAM,YADwB,CAIjCC,QAASA,EAAgB,CAACC,CAAD,CAAS,CAEhC1D,CAAAa,KAAA,CAAU,cAAV,CADeA,CAAAwC,MACf,EAD6BH,CAAAS,WAC7B,CAFgC,CAKlCC,QAASA,EAAmB,EAAG,CAC7B5D,CAAAa,KAAA,CAAU,cAAV,CAA0B,CAACqC,CAAAC,SAAA,CAAiBD,CAAAS,WAAjB,CAA3B,CAD6B,CAX/B,IAAIE,EAAgBxB,CAAA,CAAiB,UAAjB,CAA6B,UAA7B,CAAyCrC,CAAzC,CAA+C,CAAA,CAA/C,CAepB,QAAQgD,CAAR,EACE,KAAK,OAAL,CACA,KAAK,UAAL,CACMR,CAAA,CAAiBQ,CAAjB,CAAwBhD,CAAxB,CAAJ,EACEA,CAAAa,KAAA,CAAU,MAAV,CAAkBmC,CAAlB,CAEEX,EAAA,CAAiB,cAAjB,CAAiC,aAAjC,CAAgDrC,CAAhD,CAAsD,CAAA,CAAtD,CAAJ,EACEY,CAAAK,OAAA,CAAasC,CAAb,CAA8C,OAAV,GAAAP,CAAA,CAChCS,CADgC,CACbG,CADvB,CAGEC,EAAJ,EACE7D,CAAAa,KAAA,CAAU,UAAV,CAAsB,CAAtB,CAEF;KACF,MAAK,OAAL,CACM2B,CAAA,CAAiBQ,CAAjB,CAAwBhD,CAAxB,CAAJ,EACEA,CAAAa,KAAA,CAAU,MAAV,CAAkB,QAAlB,CAEF,IAAIuB,CAAApB,OAAA,CAAa,WAAb,CAAJ,CAA+B,CAC7B,IAAI8C,EAAoB,CAAC9D,CAAAa,KAAA,CAAU,eAAV,CAArBiD,GACCjD,CAAAkD,eAAA,CAAoB,KAApB,CADDD,EAC+BjD,CAAAkD,eAAA,CAAoB,OAApB,CAD/BD,CAAJ,CAEIE,EAAoB,CAAChE,CAAAa,KAAA,CAAU,eAAV,CAArBmD,GACCnD,CAAAkD,eAAA,CAAoB,KAApB,CADDC,EAC+BnD,CAAAkD,eAAA,CAAoB,OAApB,CAD/BC,CAFJ,CAIIC,EAAoB,CAACjE,CAAAa,KAAA,CAAU,eAAV,CAErBiD,EAAJ,EACEjD,CAAAqD,SAAA,CAAc,KAAd,CAAqBC,QAA+B,CAACT,CAAD,CAAS,CAC3D1D,CAAAa,KAAA,CAAU,eAAV,CAA2B6C,CAA3B,CAD2D,CAA7D,CAIEM,EAAJ,EACEnD,CAAAqD,SAAA,CAAc,KAAd,CAAqBC,QAA+B,CAACT,CAAD,CAAS,CAC3D1D,CAAAa,KAAA,CAAU,eAAV,CAA2B6C,CAA3B,CAD2D,CAA7D,CAIEO,EAAJ,EACErD,CAAAK,OAAA,CAAasC,CAAb,CAAoCa,QAA+B,CAACV,CAAD,CAAS,CAC1E1D,CAAAa,KAAA,CAAU,eAAV,CAA2B6C,CAA3B,CAD0E,CAA5E,CAlB2B,CAuB3BG,CAAJ,EACE7D,CAAAa,KAAA,CAAU,UAAV,CAAsB,CAAtB,CA1CN,CA+CK,CAAAA,CAAAkD,eAAA,CAAoB,YAApB,CAAL;AAA0Cb,CAAAmB,YAAAC,SAA1C,EACKjC,CAAA,CAAiB,eAAjB,CAAkC,cAAlC,CAAkDrC,CAAlD,CAAwD,CAAA,CAAxD,CADL,EAGEa,CAAAqD,SAAA,CAAc,UAAd,CAA0B,QAAQ,EAAG,CACnClE,CAAAa,KAAA,CAAU,eAAV,CAA2B,CAAE,CAAAA,CAAA,SAA7B,CADmC,CAArC,CAKEwB,EAAA,CAAiB,cAAjB,CAAiC,aAAjC,CAAgDrC,CAAhD,CAAsD,CAAA,CAAtD,CAAJ,EACEY,CAAAK,OAAA,CAAasD,QAA2B,EAAG,CACzC,MAAOrB,EAAAsB,SADkC,CAA3C,CAEGC,QAA8B,CAACf,CAAD,CAAS,CACxC1D,CAAAa,KAAA,CAAU,cAAV,CAA0B,CAAE6C,CAAAA,CAA5B,CADwC,CAF1C,CAxEuC,CATtC,CAHqB,CAJzB,CAtBuC,CAA1B,CAftB,CAAAvB,UAAA,CAwIW,YAxIX,CAwIyB,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CACjD,MAAOA,EAAAF,YAAA,CAAkB,YAAlB,CAAgC,eAAhC,CAAiDpC,CAAjD,CAAgE,CAAA,CAAhE,CAD0C,CAA1B,CAxIzB,CAAAqC,UAAA,CA2IW,YA3IX,CA2IyB,QAAQ,EAAG,CAClC,MAAO,CACLS,SAAU,GADL,CAELC,QAAS,aAFJ,CAGL6B,KAAMA,QAAQ,CAAC9D,CAAD,CAAQZ,CAAR,CAAca,CAAd,CAAoB8D,CAApB,CAAgC,CACvC3E,CAAAa,KAAA,CAAU,WAAV,CAAL,EACEb,CAAAa,KAAA,CAAU,WAAV;AAAuB,WAAvB,CAF0C,CAHzC,CAD2B,CA3IpC,CAAAsB,UAAA,CAsJW,SAtJX,CAsJqB,CAAC,OAAD,CAAU,QAAV,CAAoB,QAAQ,CAACC,CAAD,CAAQwC,CAAR,CAAgB,CAC/D,MAAO,CACLhC,SAAU,GADL,CAELG,QAASA,QAAQ,CAAC/C,CAAD,CAAOa,CAAP,CAAa,CAC5B,IAAIgE,EAAKD,CAAA,CAAO/D,CAAAiE,QAAP,CAAyC,IAAzC,CAAqE,CAAA,CAArE,CACT,OAAO,SAAQ,CAAClE,CAAD,CAAQZ,CAAR,CAAca,CAAd,CAAoB,CAEjC,GAAK,CAAAd,CAAA,CAAYC,CAAZ,CAAkBF,CAAlB,CAAL,GAEMsC,CAAApB,OAAA,CAAa,kBAAb,CAQA,EARqC,CAAAhB,CAAAa,KAAA,CAAU,MAAV,CAQrC,EAPFb,CAAAa,KAAA,CAAU,MAAV,CAAkB,QAAlB,CAOE,CAJAuB,CAAApB,OAAA,CAAa,UAAb,CAIA,EAJ6B,CAAAhB,CAAAa,KAAA,CAAU,UAAV,CAI7B,EAHFb,CAAAa,KAAA,CAAU,UAAV,CAAsB,CAAtB,CAGE,CAAAuB,CAAApB,OAAA,CAAa,cAAb,CAAA,EAAiC+D,CAAAlE,CAAAkE,WAVvC,EAWI/E,CAAAgF,GAAA,CAAQ,UAAR,CAAoB,QAAQ,CAACC,CAAD,CAAQ,CAMlCC,QAASA,EAAQ,EAAG,CAClBL,CAAA,CAAGjE,CAAH,CAAU,CAAEuE,OAAQF,CAAV,CAAV,CADkB,CALpB,IAAIG,EAAUH,CAAAI,MAAVD,EAAyBH,CAAAG,QACb,GAAhB,GAAIA,CAAJ,EAAkC,EAAlC,GAAsBA,CAAtB,EACExE,CAAA0E,OAAA,CAAaJ,CAAb,CAHgC,CAApC,CAb6B,CAFP,CAFzB,CADwD,CAA5C,CAtJrB,CAAA/C,UAAA,CAwLW,YAxLX,CAwLyB,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CACjD,MAAO,SAAQ,CAACxB,CAAD;AAAQZ,CAAR,CAAca,CAAd,CAAoB,CAC7B,CAAAuB,CAAApB,OAAA,CAAa,UAAb,CAAJ,EAAiChB,CAAAa,KAAA,CAAU,UAAV,CAAjC,EAA2Dd,CAAA,CAAYC,CAAZ,CAAkBF,CAAlB,CAA3D,EACEE,CAAAa,KAAA,CAAU,UAAV,CAAsB,CAAtB,CAF+B,CADc,CAA1B,CAxLzB,CAvMsC,CAArC,CAAD,CAwYGlB,MAxYH,CAwYWA,MAAAC,QAxYX;", +"sources":["angular-aria.js"], +"names":["window","angular","undefined","nodeBlackList","isNodeOneOf","elem","nodeTypeArray","indexOf","nodeName","module","provider","ngAriaModule","$AriaProvider","watchExpr","attrName","ariaAttr","negate","scope","attr","ariaCamelName","$normalize","config","$watch","boolVal","ariaHidden","ariaChecked","ariaDisabled","ariaRequired","ariaInvalid","ariaValue","tabindex","bindKeypress","bindRoleForClick","this.config","newConfig","extend","$get","this.$get","key","$$watchExpr","directive","$aria","shouldAttachAttr","normalizedAttr","allowBlacklistEls","shouldAttachRole","role","getShape","type","restrict","require","priority","compile","shape","pre","ngModel","$isEmpty","ngModel.$isEmpty","value","post","ngAriaWatchModelValue","$modelValue","getRadioReaction","newVal","$viewValue","getCheckboxReaction","needsTabIndex","needsAriaValuemin","hasOwnProperty","needsAriaValuemax","needsAriaValuenow","$observe","ngAriaValueMinReaction","ngAriaValueNowReaction","$validators","required","ngAriaInvalidWatch","$invalid","ngAriaInvalidReaction","link","ngMessages","$parse","fn","ngClick","ngKeypress","on","event","callback","$event","keyCode","which","$apply"] +} diff --git a/ecomp-portal-FE/client/bower_components/angular-aria/bower.json b/ecomp-portal-FE/client/bower_components/angular-aria/bower.json new file mode 100644 index 00000000..8a11f57c --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-aria/bower.json @@ -0,0 +1,10 @@ +{ + "name": "angular-aria", + "version": "1.5.0", + "license": "MIT", + "main": "./angular-aria.js", + "ignore": [], + "dependencies": { + "angular": "1.5.0" + } +} diff --git a/ecomp-portal-FE/client/bower_components/angular-aria/index.js b/ecomp-portal-FE/client/bower_components/angular-aria/index.js new file mode 100644 index 00000000..0a8f0d9b --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-aria/index.js @@ -0,0 +1,2 @@ +require('./angular-aria'); +module.exports = 'ngAria'; diff --git a/ecomp-portal-FE/client/bower_components/angular-aria/package.json b/ecomp-portal-FE/client/bower_components/angular-aria/package.json new file mode 100644 index 00000000..eeb93e13 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-aria/package.json @@ -0,0 +1,27 @@ +{ + "name": "angular-aria", + "version": "1.5.0", + "description": "AngularJS module for making accessibility easy", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/angular/angular.js.git" + }, + "keywords": [ + "angular", + "framework", + "browser", + "accessibility", + "a11y", + "client-side" + ], + "author": "Angular Core Team ", + "license": "MIT", + "bugs": { + "url": "https://github.com/angular/angular.js/issues" + }, + "homepage": "http://angularjs.org" +} diff --git a/ecomp-portal-FE/client/bower_components/angular-bootstrap/.bower.json b/ecomp-portal-FE/client/bower_components/angular-bootstrap/.bower.json new file mode 100644 index 00000000..de959412 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-bootstrap/.bower.json @@ -0,0 +1,31 @@ +{ + "author": { + "name": "https://github.com/angular-ui/bootstrap/graphs/contributors" + }, + "name": "angular-bootstrap", + "keywords": [ + "angular", + "angular-ui", + "bootstrap" + ], + "license": "MIT", + "ignore": [], + "description": "Native AngularJS (Angular) directives for Bootstrap.", + "version": "2.4.0", + "main": [ + "./ui-bootstrap-tpls.js" + ], + "dependencies": { + "angular": ">=1.4.0" + }, + "homepage": "https://github.com/angular-ui/bootstrap-bower", + "_release": "2.4.0", + "_resolution": { + "type": "version", + "tag": "2.4.0", + "commit": "4c3176f472a3f986ff7ee78b258af520a78ae604" + }, + "_source": "https://github.com/angular-ui/bootstrap-bower.git", + "_target": "^2.0.0", + "_originalSource": "angular-bootstrap" +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-bootstrap/.gitignore b/ecomp-portal-FE/client/bower_components/angular-bootstrap/.gitignore new file mode 100644 index 00000000..496ee2ca --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-bootstrap/.gitignore @@ -0,0 +1 @@ +.DS_Store \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-bootstrap/.npmignore b/ecomp-portal-FE/client/bower_components/angular-bootstrap/.npmignore new file mode 100644 index 00000000..d62f9b6e --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-bootstrap/.npmignore @@ -0,0 +1 @@ +bower.json \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-bootstrap/README.md b/ecomp-portal-FE/client/bower_components/angular-bootstrap/README.md new file mode 100644 index 00000000..9607c65f --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-bootstrap/README.md @@ -0,0 +1,120 @@ +### UI Bootstrap - [AngularJS](http://angularjs.org/) directives specific to [Bootstrap](http://getbootstrap.com) + +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angular-ui/bootstrap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Build Status](https://secure.travis-ci.org/angular-ui/bootstrap.svg)](http://travis-ci.org/angular-ui/bootstrap) +[![devDependency Status](https://david-dm.org/angular-ui/bootstrap/dev-status.svg?branch=master)](https://david-dm.org/angular-ui/bootstrap#info=devDependencies) + +### Quick links +- [Demo](#demo) +- [Installation](#installation) + - [NPM](#install-with-npm) + - [Bower](#install-with-bower) + - [NuGet](#install-with-nuget) + - [Custom](#custom-build) + - [Manual](#manual-download) +- [Support](#support) + - [FAQ](#faq) + - [Supported browsers](#supported-browsers) + - [Need help?](#need-help) + - [Found a bug?](#found-a-bug) +- [Contributing to the project](#contributing-to-the-project) +- [Development, meeting minutes, roadmap and more.](#development-meeting-minutes-roadmap-and-more) + + +# Demo + +Do you want to see directives in action? Visit http://angular-ui.github.io/bootstrap/! + +# Installation + +Installation is easy as UI Bootstrap has minimal dependencies - only the AngularJS and Twitter Bootstrap's CSS are required. +Note: Since version 0.13.0, UI Bootstrap depends on [ngAnimate](https://docs.angularjs.org/api/ngAnimate) for transitions and animations, such as the accordion, carousel, etc. Include `ngAnimate` in the module dependencies for your app in order to enable animation. + +#### Install with NPM + +```sh +$ npm install angular-ui-bootstrap +``` + +This will install AngularJS and Bootstrap NPM packages. + +#### Install with Bower +```sh +$ bower install angular-bootstrap +``` + +Note: do not install 'angular-ui-bootstrap'. A separate repository - [bootstrap-bower](https://github.com/angular-ui/bootstrap-bower) - hosts the compiled javascript file and bower.json. + +#### Install with NuGet +To install AngularJS UI Bootstrap, run the following command in the Package Manager Console + +```sh +PM> Install-Package Angular.UI.Bootstrap +``` + +#### Custom build + +Head over to http://angular-ui.github.io/bootstrap/ and hit the *Custom build* button to create your own custom UI Bootstrap build, just the way you like it. + +#### Manual download + +After downloading dependencies (or better yet, referencing them from your favorite CDN) you need to download build version of this project. All the files and their purposes are described here: +https://github.com/angular-ui/bootstrap/tree/gh-pages#build-files +Don't worry, if you are not sure which file to take, opt for `ui-bootstrap-tpls-[version].min.js`. + +### Adding dependency to your project + +When you are done downloading all the dependencies and project files the only remaining part is to add dependencies on the `ui.bootstrap` AngularJS module: + +```js +angular.module('myModule', ['ui.bootstrap']); +``` + +If you're a Browserify or Webpack user, you can do: + +```js +var uibs = require('angular-ui-bootstrap'); + +angular.module('myModule', [uibs]); +``` + +# Support + +## FAQ + +https://github.com/angular-ui/bootstrap/wiki/FAQ + +## Supported browsers + +Directives from this repository are automatically tested with the following browsers: +* Chrome (stable and canary channel) +* Firefox +* IE 9 and 10 +* Opera +* Safari + +Modern mobile browsers should work without problems. + + +## Need help? +Need help using UI Bootstrap? + +* Live help in the IRC (`#angularjs` channel at the `freenode` network). Use this [webchat](https://webchat.freenode.net/) or your own IRC client. +* Ask a question in [StackOverflow](http://stackoverflow.com/) under the [angular-ui-bootstrap](http://stackoverflow.com/questions/tagged/angular-ui-bootstrap) tag. + +**Please do not create new issues in this repository to ask questions about using UI Bootstrap** + +## Found a bug? +Please take a look at [CONTRIBUTING.md](CONTRIBUTING.md#you-think-youve-found-a-bug) and submit your issue [here](https://github.com/angular-ui/bootstrap/issues/new). + + +---- + + +# Contributing to the project + +We are always looking for the quality contributions! Please check the [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution guidelines. + +# Development, meeting minutes, roadmap and more. + +Head over to the [Wiki](https://github.com/angular-ui/bootstrap/wiki) for notes on development for UI Bootstrap, meeting minutes from the UI Bootstrap team, roadmap plans, project philosophy and more. diff --git a/ecomp-portal-FE/client/bower_components/angular-bootstrap/bower.json b/ecomp-portal-FE/client/bower_components/angular-bootstrap/bower.json new file mode 100644 index 00000000..e0b38c33 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-bootstrap/bower.json @@ -0,0 +1,19 @@ +{ + "author": { + "name": "https://github.com/angular-ui/bootstrap/graphs/contributors" + }, + "name": "angular-bootstrap", + "keywords": [ + "angular", + "angular-ui", + "bootstrap" + ], + "license": "MIT", + "ignore": [], + "description": "Native AngularJS (Angular) directives for Bootstrap.", + "version": "2.4.0", + "main": ["./ui-bootstrap-tpls.js"], + "dependencies": { + "angular": ">=1.4.0" + } +} diff --git a/ecomp-portal-FE/client/bower_components/angular-bootstrap/index.js b/ecomp-portal-FE/client/bower_components/angular-bootstrap/index.js new file mode 100644 index 00000000..a174f26d --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-bootstrap/index.js @@ -0,0 +1,2 @@ +require('./ui-bootstrap-tpls'); +module.exports = 'ui.bootstrap'; diff --git a/ecomp-portal-FE/client/bower_components/angular-bootstrap/package.json b/ecomp-portal-FE/client/bower_components/angular-bootstrap/package.json new file mode 100644 index 00000000..f42e8710 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-bootstrap/package.json @@ -0,0 +1,23 @@ +{ + "name": "angular-ui-bootstrap", + "version": "2.4.0", + "description": "Bootstrap widgets for Angular", + "main": "index.js", + "homepage": "http://angular-ui.github.io/bootstrap/", + "repository": { + "type": "git", + "url": "https://github.com/angular-ui/bootstrap.git" + }, + "keywords": [ + "angular", + "bootstrap", + "angular-ui", + "components", + "client-side" + ], + "author": "https://github.com/angular-ui/bootstrap/graphs/contributors", + "peerDependencies": { + "angular": ">= 1.4.0-beta.0 || >= 1.5.0-beta.0" + }, + "license": "MIT" +} diff --git a/ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap-csp.css b/ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap-csp.css new file mode 100644 index 00000000..3b69cf64 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap-csp.css @@ -0,0 +1,115 @@ +/* Include this file in your html if you are using the CSP mode. */ + +.ng-animate.item:not(.left):not(.right) { + -webkit-transition: 0s ease-in-out left; + transition: 0s ease-in-out left +} +.uib-datepicker .uib-title { + width: 100%; +} + +.uib-day button, .uib-month button, .uib-year button { + min-width: 100%; +} + +.uib-left, .uib-right { + width: 100% +} + +.uib-position-measure { + display: block !important; + visibility: hidden !important; + position: absolute !important; + top: -9999px !important; + left: -9999px !important; +} + +.uib-position-scrollbar-measure { + position: absolute !important; + top: -9999px !important; + width: 50px !important; + height: 50px !important; + overflow: scroll !important; +} + +.uib-position-body-scrollbar-measure { + overflow: scroll !important; +} +.uib-datepicker-popup.dropdown-menu { + display: block; + float: none; + margin: 0; +} + +.uib-button-bar { + padding: 10px 9px 2px; +} + +[uib-tooltip-popup].tooltip.top-left > .tooltip-arrow, +[uib-tooltip-popup].tooltip.top-right > .tooltip-arrow, +[uib-tooltip-popup].tooltip.bottom-left > .tooltip-arrow, +[uib-tooltip-popup].tooltip.bottom-right > .tooltip-arrow, +[uib-tooltip-popup].tooltip.left-top > .tooltip-arrow, +[uib-tooltip-popup].tooltip.left-bottom > .tooltip-arrow, +[uib-tooltip-popup].tooltip.right-top > .tooltip-arrow, +[uib-tooltip-popup].tooltip.right-bottom > .tooltip-arrow, +[uib-tooltip-html-popup].tooltip.top-left > .tooltip-arrow, +[uib-tooltip-html-popup].tooltip.top-right > .tooltip-arrow, +[uib-tooltip-html-popup].tooltip.bottom-left > .tooltip-arrow, +[uib-tooltip-html-popup].tooltip.bottom-right > .tooltip-arrow, +[uib-tooltip-html-popup].tooltip.left-top > .tooltip-arrow, +[uib-tooltip-html-popup].tooltip.left-bottom > .tooltip-arrow, +[uib-tooltip-html-popup].tooltip.right-top > .tooltip-arrow, +[uib-tooltip-html-popup].tooltip.right-bottom > .tooltip-arrow, +[uib-tooltip-template-popup].tooltip.top-left > .tooltip-arrow, +[uib-tooltip-template-popup].tooltip.top-right > .tooltip-arrow, +[uib-tooltip-template-popup].tooltip.bottom-left > .tooltip-arrow, +[uib-tooltip-template-popup].tooltip.bottom-right > .tooltip-arrow, +[uib-tooltip-template-popup].tooltip.left-top > .tooltip-arrow, +[uib-tooltip-template-popup].tooltip.left-bottom > .tooltip-arrow, +[uib-tooltip-template-popup].tooltip.right-top > .tooltip-arrow, +[uib-tooltip-template-popup].tooltip.right-bottom > .tooltip-arrow, +[uib-popover-popup].popover.top-left > .arrow, +[uib-popover-popup].popover.top-right > .arrow, +[uib-popover-popup].popover.bottom-left > .arrow, +[uib-popover-popup].popover.bottom-right > .arrow, +[uib-popover-popup].popover.left-top > .arrow, +[uib-popover-popup].popover.left-bottom > .arrow, +[uib-popover-popup].popover.right-top > .arrow, +[uib-popover-popup].popover.right-bottom > .arrow, +[uib-popover-html-popup].popover.top-left > .arrow, +[uib-popover-html-popup].popover.top-right > .arrow, +[uib-popover-html-popup].popover.bottom-left > .arrow, +[uib-popover-html-popup].popover.bottom-right > .arrow, +[uib-popover-html-popup].popover.left-top > .arrow, +[uib-popover-html-popup].popover.left-bottom > .arrow, +[uib-popover-html-popup].popover.right-top > .arrow, +[uib-popover-html-popup].popover.right-bottom > .arrow, +[uib-popover-template-popup].popover.top-left > .arrow, +[uib-popover-template-popup].popover.top-right > .arrow, +[uib-popover-template-popup].popover.bottom-left > .arrow, +[uib-popover-template-popup].popover.bottom-right > .arrow, +[uib-popover-template-popup].popover.left-top > .arrow, +[uib-popover-template-popup].popover.left-bottom > .arrow, +[uib-popover-template-popup].popover.right-top > .arrow, +[uib-popover-template-popup].popover.right-bottom > .arrow { + top: auto; + bottom: auto; + left: auto; + right: auto; + margin: 0; +} + +[uib-popover-popup].popover, +[uib-popover-html-popup].popover, +[uib-popover-template-popup].popover { + display: block !important; +} + +.uib-time input { + width: 50px; +} + +[uib-typeahead-popup].dropdown-menu { + display: block; +} diff --git a/ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap-tpls.js b/ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap-tpls.js new file mode 100644 index 00000000..91ddae90 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap-tpls.js @@ -0,0 +1,7715 @@ +/* + * angular-ui-bootstrap + * http://angular-ui.github.io/bootstrap/ + + * Version: 2.4.0 - 2016-12-29 + * License: MIT + */angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.collapse","ui.bootstrap.tabindex","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.datepicker","ui.bootstrap.position","ui.bootstrap.datepickerPopup","ui.bootstrap.debounce","ui.bootstrap.multiMap","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]); +angular.module("ui.bootstrap.tpls", ["uib/template/accordion/accordion-group.html","uib/template/accordion/accordion.html","uib/template/alert/alert.html","uib/template/carousel/carousel.html","uib/template/carousel/slide.html","uib/template/datepicker/datepicker.html","uib/template/datepicker/day.html","uib/template/datepicker/month.html","uib/template/datepicker/year.html","uib/template/datepickerPopup/popup.html","uib/template/modal/window.html","uib/template/pager/pager.html","uib/template/pagination/pagination.html","uib/template/tooltip/tooltip-html-popup.html","uib/template/tooltip/tooltip-popup.html","uib/template/tooltip/tooltip-template-popup.html","uib/template/popover/popover-html.html","uib/template/popover/popover-template.html","uib/template/popover/popover.html","uib/template/progressbar/bar.html","uib/template/progressbar/progress.html","uib/template/progressbar/progressbar.html","uib/template/rating/rating.html","uib/template/tabs/tab.html","uib/template/tabs/tabset.html","uib/template/timepicker/timepicker.html","uib/template/typeahead/typeahead-match.html","uib/template/typeahead/typeahead-popup.html"]); +angular.module('ui.bootstrap.collapse', []) + + .directive('uibCollapse', ['$animate', '$q', '$parse', '$injector', function($animate, $q, $parse, $injector) { + var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null; + return { + link: function(scope, element, attrs) { + var expandingExpr = $parse(attrs.expanding), + expandedExpr = $parse(attrs.expanded), + collapsingExpr = $parse(attrs.collapsing), + collapsedExpr = $parse(attrs.collapsed), + horizontal = false, + css = {}, + cssTo = {}; + + init(); + + function init() { + horizontal = !!('horizontal' in attrs); + if (horizontal) { + css = { + width: '' + }; + cssTo = {width: '0'}; + } else { + css = { + height: '' + }; + cssTo = {height: '0'}; + } + if (!scope.$eval(attrs.uibCollapse)) { + element.addClass('in') + .addClass('collapse') + .attr('aria-expanded', true) + .attr('aria-hidden', false) + .css(css); + } + } + + function getScrollFromElement(element) { + if (horizontal) { + return {width: element.scrollWidth + 'px'}; + } + return {height: element.scrollHeight + 'px'}; + } + + function expand() { + if (element.hasClass('collapse') && element.hasClass('in')) { + return; + } + + $q.resolve(expandingExpr(scope)) + .then(function() { + element.removeClass('collapse') + .addClass('collapsing') + .attr('aria-expanded', true) + .attr('aria-hidden', false); + + if ($animateCss) { + $animateCss(element, { + addClass: 'in', + easing: 'ease', + css: { + overflow: 'hidden' + }, + to: getScrollFromElement(element[0]) + }).start()['finally'](expandDone); + } else { + $animate.addClass(element, 'in', { + css: { + overflow: 'hidden' + }, + to: getScrollFromElement(element[0]) + }).then(expandDone); + } + }); + } + + function expandDone() { + element.removeClass('collapsing') + .addClass('collapse') + .css(css); + expandedExpr(scope); + } + + function collapse() { + if (!element.hasClass('collapse') && !element.hasClass('in')) { + return collapseDone(); + } + + $q.resolve(collapsingExpr(scope)) + .then(function() { + element + // IMPORTANT: The width must be set before adding "collapsing" class. + // Otherwise, the browser attempts to animate from width 0 (in + // collapsing class) to the given width here. + .css(getScrollFromElement(element[0])) + // initially all panel collapse have the collapse class, this removal + // prevents the animation from jumping to collapsed state + .removeClass('collapse') + .addClass('collapsing') + .attr('aria-expanded', false) + .attr('aria-hidden', true); + + if ($animateCss) { + $animateCss(element, { + removeClass: 'in', + to: cssTo + }).start()['finally'](collapseDone); + } else { + $animate.removeClass(element, 'in', { + to: cssTo + }).then(collapseDone); + } + }); + } + + function collapseDone() { + element.css(cssTo); // Required so that collapse works when animation is disabled + element.removeClass('collapsing') + .addClass('collapse'); + collapsedExpr(scope); + } + + scope.$watch(attrs.uibCollapse, function(shouldCollapse) { + if (shouldCollapse) { + collapse(); + } else { + expand(); + } + }); + } + }; + }]); + +angular.module('ui.bootstrap.tabindex', []) + +.directive('uibTabindexToggle', function() { + return { + restrict: 'A', + link: function(scope, elem, attrs) { + attrs.$observe('disabled', function(disabled) { + attrs.$set('tabindex', disabled ? -1 : null); + }); + } + }; +}); + +angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse', 'ui.bootstrap.tabindex']) + +.constant('uibAccordionConfig', { + closeOthers: true +}) + +.controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) { + // This array keeps track of the accordion groups + this.groups = []; + + // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to + this.closeOthers = function(openGroup) { + var closeOthers = angular.isDefined($attrs.closeOthers) ? + $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers; + if (closeOthers) { + angular.forEach(this.groups, function(group) { + if (group !== openGroup) { + group.isOpen = false; + } + }); + } + }; + + // This is called from the accordion-group directive to add itself to the accordion + this.addGroup = function(groupScope) { + var that = this; + this.groups.push(groupScope); + + groupScope.$on('$destroy', function(event) { + that.removeGroup(groupScope); + }); + }; + + // This is called from the accordion-group directive when to remove itself + this.removeGroup = function(group) { + var index = this.groups.indexOf(group); + if (index !== -1) { + this.groups.splice(index, 1); + } + }; +}]) + +// The accordion directive simply sets up the directive controller +// and adds an accordion CSS class to itself element. +.directive('uibAccordion', function() { + return { + controller: 'UibAccordionController', + controllerAs: 'accordion', + transclude: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/accordion/accordion.html'; + } + }; +}) + +// The accordion-group directive indicates a block of html that will expand and collapse in an accordion +.directive('uibAccordionGroup', function() { + return { + require: '^uibAccordion', // We need this directive to be inside an accordion + transclude: true, // It transcludes the contents of the directive into the template + restrict: 'A', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/accordion/accordion-group.html'; + }, + scope: { + heading: '@', // Interpolate the heading attribute onto this scope + panelClass: '@?', // Ditto with panelClass + isOpen: '=?', + isDisabled: '=?' + }, + controller: function() { + this.setHeading = function(element) { + this.heading = element; + }; + }, + link: function(scope, element, attrs, accordionCtrl) { + element.addClass('panel'); + accordionCtrl.addGroup(scope); + + scope.openClass = attrs.openClass || 'panel-open'; + scope.panelClass = attrs.panelClass || 'panel-default'; + scope.$watch('isOpen', function(value) { + element.toggleClass(scope.openClass, !!value); + if (value) { + accordionCtrl.closeOthers(scope); + } + }); + + scope.toggleOpen = function($event) { + if (!scope.isDisabled) { + if (!$event || $event.which === 32) { + scope.isOpen = !scope.isOpen; + } + } + }; + + var id = 'accordiongroup-' + scope.$id + '-' + Math.floor(Math.random() * 10000); + scope.headingId = id + '-tab'; + scope.panelId = id + '-panel'; + } + }; +}) + +// Use accordion-heading below an accordion-group to provide a heading containing HTML +.directive('uibAccordionHeading', function() { + return { + transclude: true, // Grab the contents to be used as the heading + template: '', // In effect remove this element! + replace: true, + require: '^uibAccordionGroup', + link: function(scope, element, attrs, accordionGroupCtrl, transclude) { + // Pass the heading to the accordion-group controller + // so that it can be transcluded into the right place in the template + // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat] + accordionGroupCtrl.setHeading(transclude(scope, angular.noop)); + } + }; +}) + +// Use in the accordion-group template to indicate where you want the heading to be transcluded +// You must provide the property on the accordion-group controller that will hold the transcluded element +.directive('uibAccordionTransclude', function() { + return { + require: '^uibAccordionGroup', + link: function(scope, element, attrs, controller) { + scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) { + if (heading) { + var elem = angular.element(element[0].querySelector(getHeaderSelectors())); + elem.html(''); + elem.append(heading); + } + }); + } + }; + + function getHeaderSelectors() { + return 'uib-accordion-header,' + + 'data-uib-accordion-header,' + + 'x-uib-accordion-header,' + + 'uib\\:accordion-header,' + + '[uib-accordion-header],' + + '[data-uib-accordion-header],' + + '[x-uib-accordion-header]'; + } +}); + +angular.module('ui.bootstrap.alert', []) + +.controller('UibAlertController', ['$scope', '$element', '$attrs', '$interpolate', '$timeout', function($scope, $element, $attrs, $interpolate, $timeout) { + $scope.closeable = !!$attrs.close; + $element.addClass('alert'); + $attrs.$set('role', 'alert'); + if ($scope.closeable) { + $element.addClass('alert-dismissible'); + } + + var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ? + $interpolate($attrs.dismissOnTimeout)($scope.$parent) : null; + + if (dismissOnTimeout) { + $timeout(function() { + $scope.close(); + }, parseInt(dismissOnTimeout, 10)); + } +}]) + +.directive('uibAlert', function() { + return { + controller: 'UibAlertController', + controllerAs: 'alert', + restrict: 'A', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/alert/alert.html'; + }, + transclude: true, + scope: { + close: '&' + } + }; +}); + +angular.module('ui.bootstrap.buttons', []) + +.constant('uibButtonConfig', { + activeClass: 'active', + toggleEvent: 'click' +}) + +.controller('UibButtonsController', ['uibButtonConfig', function(buttonConfig) { + this.activeClass = buttonConfig.activeClass || 'active'; + this.toggleEvent = buttonConfig.toggleEvent || 'click'; +}]) + +.directive('uibBtnRadio', ['$parse', function($parse) { + return { + require: ['uibBtnRadio', 'ngModel'], + controller: 'UibButtonsController', + controllerAs: 'buttons', + link: function(scope, element, attrs, ctrls) { + var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + var uncheckableExpr = $parse(attrs.uibUncheckable); + + element.find('input').css({display: 'none'}); + + //model -> UI + ngModelCtrl.$render = function() { + element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio))); + }; + + //ui->model + element.on(buttonsCtrl.toggleEvent, function() { + if (attrs.disabled) { + return; + } + + var isActive = element.hasClass(buttonsCtrl.activeClass); + + if (!isActive || angular.isDefined(attrs.uncheckable)) { + scope.$apply(function() { + ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio)); + ngModelCtrl.$render(); + }); + } + }); + + if (attrs.uibUncheckable) { + scope.$watch(uncheckableExpr, function(uncheckable) { + attrs.$set('uncheckable', uncheckable ? '' : undefined); + }); + } + } + }; +}]) + +.directive('uibBtnCheckbox', function() { + return { + require: ['uibBtnCheckbox', 'ngModel'], + controller: 'UibButtonsController', + controllerAs: 'button', + link: function(scope, element, attrs, ctrls) { + var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + element.find('input').css({display: 'none'}); + + function getTrueValue() { + return getCheckboxValue(attrs.btnCheckboxTrue, true); + } + + function getFalseValue() { + return getCheckboxValue(attrs.btnCheckboxFalse, false); + } + + function getCheckboxValue(attribute, defaultValue) { + return angular.isDefined(attribute) ? scope.$eval(attribute) : defaultValue; + } + + //model -> UI + ngModelCtrl.$render = function() { + element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue())); + }; + + //ui->model + element.on(buttonsCtrl.toggleEvent, function() { + if (attrs.disabled) { + return; + } + + scope.$apply(function() { + ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue()); + ngModelCtrl.$render(); + }); + }); + } + }; +}); + +angular.module('ui.bootstrap.carousel', []) + +.controller('UibCarouselController', ['$scope', '$element', '$interval', '$timeout', '$animate', function($scope, $element, $interval, $timeout, $animate) { + var self = this, + slides = self.slides = $scope.slides = [], + SLIDE_DIRECTION = 'uib-slideDirection', + currentIndex = $scope.active, + currentInterval, isPlaying, bufferedTransitions = []; + + var destroyed = false; + $element.addClass('carousel'); + + self.addSlide = function(slide, element) { + slides.push({ + slide: slide, + element: element + }); + slides.sort(function(a, b) { + return +a.slide.index - +b.slide.index; + }); + //if this is the first slide or the slide is set to active, select it + if (slide.index === $scope.active || slides.length === 1 && !angular.isNumber($scope.active)) { + if ($scope.$currentTransition) { + $scope.$currentTransition = null; + } + + currentIndex = slide.index; + $scope.active = slide.index; + setActive(currentIndex); + self.select(slides[findSlideIndex(slide)]); + if (slides.length === 1) { + $scope.play(); + } + } + }; + + self.getCurrentIndex = function() { + for (var i = 0; i < slides.length; i++) { + if (slides[i].slide.index === currentIndex) { + return i; + } + } + }; + + self.next = $scope.next = function() { + var newIndex = (self.getCurrentIndex() + 1) % slides.length; + + if (newIndex === 0 && $scope.noWrap()) { + $scope.pause(); + return; + } + + return self.select(slides[newIndex], 'next'); + }; + + self.prev = $scope.prev = function() { + var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1; + + if ($scope.noWrap() && newIndex === slides.length - 1) { + $scope.pause(); + return; + } + + return self.select(slides[newIndex], 'prev'); + }; + + self.removeSlide = function(slide) { + var index = findSlideIndex(slide); + + var bufferedIndex = bufferedTransitions.indexOf(slides[index]); + if (bufferedIndex !== -1) { + bufferedTransitions.splice(bufferedIndex, 1); + } + + //get the index of the slide inside the carousel + slides.splice(index, 1); + if (slides.length > 0 && currentIndex === index) { + if (index >= slides.length) { + currentIndex = slides.length - 1; + $scope.active = currentIndex; + setActive(currentIndex); + self.select(slides[slides.length - 1]); + } else { + currentIndex = index; + $scope.active = currentIndex; + setActive(currentIndex); + self.select(slides[index]); + } + } else if (currentIndex > index) { + currentIndex--; + $scope.active = currentIndex; + } + + //clean the active value when no more slide + if (slides.length === 0) { + currentIndex = null; + $scope.active = null; + clearBufferedTransitions(); + } + }; + + /* direction: "prev" or "next" */ + self.select = $scope.select = function(nextSlide, direction) { + var nextIndex = findSlideIndex(nextSlide.slide); + //Decide direction if it's not given + if (direction === undefined) { + direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev'; + } + //Prevent this user-triggered transition from occurring if there is already one in progress + if (nextSlide.slide.index !== currentIndex && + !$scope.$currentTransition) { + goNext(nextSlide.slide, nextIndex, direction); + } else if (nextSlide && nextSlide.slide.index !== currentIndex && $scope.$currentTransition) { + bufferedTransitions.push(slides[nextIndex]); + } + }; + + /* Allow outside people to call indexOf on slides array */ + $scope.indexOfSlide = function(slide) { + return +slide.slide.index; + }; + + $scope.isActive = function(slide) { + return $scope.active === slide.slide.index; + }; + + $scope.isPrevDisabled = function() { + return $scope.active === 0 && $scope.noWrap(); + }; + + $scope.isNextDisabled = function() { + return $scope.active === slides.length - 1 && $scope.noWrap(); + }; + + $scope.pause = function() { + if (!$scope.noPause) { + isPlaying = false; + resetTimer(); + } + }; + + $scope.play = function() { + if (!isPlaying) { + isPlaying = true; + restartTimer(); + } + }; + + $element.on('mouseenter', $scope.pause); + $element.on('mouseleave', $scope.play); + + $scope.$on('$destroy', function() { + destroyed = true; + resetTimer(); + }); + + $scope.$watch('noTransition', function(noTransition) { + $animate.enabled($element, !noTransition); + }); + + $scope.$watch('interval', restartTimer); + + $scope.$watchCollection('slides', resetTransition); + + $scope.$watch('active', function(index) { + if (angular.isNumber(index) && currentIndex !== index) { + for (var i = 0; i < slides.length; i++) { + if (slides[i].slide.index === index) { + index = i; + break; + } + } + + var slide = slides[index]; + if (slide) { + setActive(index); + self.select(slides[index]); + currentIndex = index; + } + } + }); + + function clearBufferedTransitions() { + while (bufferedTransitions.length) { + bufferedTransitions.shift(); + } + } + + function getSlideByIndex(index) { + for (var i = 0, l = slides.length; i < l; ++i) { + if (slides[i].index === index) { + return slides[i]; + } + } + } + + function setActive(index) { + for (var i = 0; i < slides.length; i++) { + slides[i].slide.active = i === index; + } + } + + function goNext(slide, index, direction) { + if (destroyed) { + return; + } + + angular.extend(slide, {direction: direction}); + angular.extend(slides[currentIndex].slide || {}, {direction: direction}); + if ($animate.enabled($element) && !$scope.$currentTransition && + slides[index].element && self.slides.length > 1) { + slides[index].element.data(SLIDE_DIRECTION, slide.direction); + var currentIdx = self.getCurrentIndex(); + + if (angular.isNumber(currentIdx) && slides[currentIdx].element) { + slides[currentIdx].element.data(SLIDE_DIRECTION, slide.direction); + } + + $scope.$currentTransition = true; + $animate.on('addClass', slides[index].element, function(element, phase) { + if (phase === 'close') { + $scope.$currentTransition = null; + $animate.off('addClass', element); + if (bufferedTransitions.length) { + var nextSlide = bufferedTransitions.pop().slide; + var nextIndex = nextSlide.index; + var nextDirection = nextIndex > self.getCurrentIndex() ? 'next' : 'prev'; + clearBufferedTransitions(); + + goNext(nextSlide, nextIndex, nextDirection); + } + } + }); + } + + $scope.active = slide.index; + currentIndex = slide.index; + setActive(index); + + //every time you change slides, reset the timer + restartTimer(); + } + + function findSlideIndex(slide) { + for (var i = 0; i < slides.length; i++) { + if (slides[i].slide === slide) { + return i; + } + } + } + + function resetTimer() { + if (currentInterval) { + $interval.cancel(currentInterval); + currentInterval = null; + } + } + + function resetTransition(slides) { + if (!slides.length) { + $scope.$currentTransition = null; + clearBufferedTransitions(); + } + } + + function restartTimer() { + resetTimer(); + var interval = +$scope.interval; + if (!isNaN(interval) && interval > 0) { + currentInterval = $interval(timerFn, interval); + } + } + + function timerFn() { + var interval = +$scope.interval; + if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) { + $scope.next(); + } else { + $scope.pause(); + } + } +}]) + +.directive('uibCarousel', function() { + return { + transclude: true, + controller: 'UibCarouselController', + controllerAs: 'carousel', + restrict: 'A', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/carousel/carousel.html'; + }, + scope: { + active: '=', + interval: '=', + noTransition: '=', + noPause: '=', + noWrap: '&' + } + }; +}) + +.directive('uibSlide', ['$animate', function($animate) { + return { + require: '^uibCarousel', + restrict: 'A', + transclude: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/carousel/slide.html'; + }, + scope: { + actual: '=?', + index: '=?' + }, + link: function (scope, element, attrs, carouselCtrl) { + element.addClass('item'); + carouselCtrl.addSlide(scope, element); + //when the scope is destroyed then remove the slide from the current slides array + scope.$on('$destroy', function() { + carouselCtrl.removeSlide(scope); + }); + + scope.$watch('active', function(active) { + $animate[active ? 'addClass' : 'removeClass'](element, 'active'); + }); + } + }; +}]) + +.animation('.item', ['$animateCss', +function($animateCss) { + var SLIDE_DIRECTION = 'uib-slideDirection'; + + function removeClass(element, className, callback) { + element.removeClass(className); + if (callback) { + callback(); + } + } + + return { + beforeAddClass: function(element, className, done) { + if (className === 'active') { + var stopped = false; + var direction = element.data(SLIDE_DIRECTION); + var directionClass = direction === 'next' ? 'left' : 'right'; + var removeClassFn = removeClass.bind(this, element, + directionClass + ' ' + direction, done); + element.addClass(direction); + + $animateCss(element, {addClass: directionClass}) + .start() + .done(removeClassFn); + + return function() { + stopped = true; + }; + } + done(); + }, + beforeRemoveClass: function (element, className, done) { + if (className === 'active') { + var stopped = false; + var direction = element.data(SLIDE_DIRECTION); + var directionClass = direction === 'next' ? 'left' : 'right'; + var removeClassFn = removeClass.bind(this, element, directionClass, done); + + $animateCss(element, {addClass: directionClass}) + .start() + .done(removeClassFn); + + return function() { + stopped = true; + }; + } + done(); + } + }; +}]); + +angular.module('ui.bootstrap.dateparser', []) + +.service('uibDateParser', ['$log', '$locale', 'dateFilter', 'orderByFilter', 'filterFilter', function($log, $locale, dateFilter, orderByFilter, filterFilter) { + // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js + var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; + + var localeId; + var formatCodeToRegex; + + this.init = function() { + localeId = $locale.id; + + this.parsers = {}; + this.formatters = {}; + + formatCodeToRegex = [ + { + key: 'yyyy', + regex: '\\d{4}', + apply: function(value) { this.year = +value; }, + formatter: function(date) { + var _date = new Date(); + _date.setFullYear(Math.abs(date.getFullYear())); + return dateFilter(_date, 'yyyy'); + } + }, + { + key: 'yy', + regex: '\\d{2}', + apply: function(value) { value = +value; this.year = value < 69 ? value + 2000 : value + 1900; }, + formatter: function(date) { + var _date = new Date(); + _date.setFullYear(Math.abs(date.getFullYear())); + return dateFilter(_date, 'yy'); + } + }, + { + key: 'y', + regex: '\\d{1,4}', + apply: function(value) { this.year = +value; }, + formatter: function(date) { + var _date = new Date(); + _date.setFullYear(Math.abs(date.getFullYear())); + return dateFilter(_date, 'y'); + } + }, + { + key: 'M!', + regex: '0?[1-9]|1[0-2]', + apply: function(value) { this.month = value - 1; }, + formatter: function(date) { + var value = date.getMonth(); + if (/^[0-9]$/.test(value)) { + return dateFilter(date, 'MM'); + } + + return dateFilter(date, 'M'); + } + }, + { + key: 'MMMM', + regex: $locale.DATETIME_FORMATS.MONTH.join('|'), + apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }, + formatter: function(date) { return dateFilter(date, 'MMMM'); } + }, + { + key: 'MMM', + regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'), + apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }, + formatter: function(date) { return dateFilter(date, 'MMM'); } + }, + { + key: 'MM', + regex: '0[1-9]|1[0-2]', + apply: function(value) { this.month = value - 1; }, + formatter: function(date) { return dateFilter(date, 'MM'); } + }, + { + key: 'M', + regex: '[1-9]|1[0-2]', + apply: function(value) { this.month = value - 1; }, + formatter: function(date) { return dateFilter(date, 'M'); } + }, + { + key: 'd!', + regex: '[0-2]?[0-9]{1}|3[0-1]{1}', + apply: function(value) { this.date = +value; }, + formatter: function(date) { + var value = date.getDate(); + if (/^[1-9]$/.test(value)) { + return dateFilter(date, 'dd'); + } + + return dateFilter(date, 'd'); + } + }, + { + key: 'dd', + regex: '[0-2][0-9]{1}|3[0-1]{1}', + apply: function(value) { this.date = +value; }, + formatter: function(date) { return dateFilter(date, 'dd'); } + }, + { + key: 'd', + regex: '[1-2]?[0-9]{1}|3[0-1]{1}', + apply: function(value) { this.date = +value; }, + formatter: function(date) { return dateFilter(date, 'd'); } + }, + { + key: 'EEEE', + regex: $locale.DATETIME_FORMATS.DAY.join('|'), + formatter: function(date) { return dateFilter(date, 'EEEE'); } + }, + { + key: 'EEE', + regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|'), + formatter: function(date) { return dateFilter(date, 'EEE'); } + }, + { + key: 'HH', + regex: '(?:0|1)[0-9]|2[0-3]', + apply: function(value) { this.hours = +value; }, + formatter: function(date) { return dateFilter(date, 'HH'); } + }, + { + key: 'hh', + regex: '0[0-9]|1[0-2]', + apply: function(value) { this.hours = +value; }, + formatter: function(date) { return dateFilter(date, 'hh'); } + }, + { + key: 'H', + regex: '1?[0-9]|2[0-3]', + apply: function(value) { this.hours = +value; }, + formatter: function(date) { return dateFilter(date, 'H'); } + }, + { + key: 'h', + regex: '[0-9]|1[0-2]', + apply: function(value) { this.hours = +value; }, + formatter: function(date) { return dateFilter(date, 'h'); } + }, + { + key: 'mm', + regex: '[0-5][0-9]', + apply: function(value) { this.minutes = +value; }, + formatter: function(date) { return dateFilter(date, 'mm'); } + }, + { + key: 'm', + regex: '[0-9]|[1-5][0-9]', + apply: function(value) { this.minutes = +value; }, + formatter: function(date) { return dateFilter(date, 'm'); } + }, + { + key: 'sss', + regex: '[0-9][0-9][0-9]', + apply: function(value) { this.milliseconds = +value; }, + formatter: function(date) { return dateFilter(date, 'sss'); } + }, + { + key: 'ss', + regex: '[0-5][0-9]', + apply: function(value) { this.seconds = +value; }, + formatter: function(date) { return dateFilter(date, 'ss'); } + }, + { + key: 's', + regex: '[0-9]|[1-5][0-9]', + apply: function(value) { this.seconds = +value; }, + formatter: function(date) { return dateFilter(date, 's'); } + }, + { + key: 'a', + regex: $locale.DATETIME_FORMATS.AMPMS.join('|'), + apply: function(value) { + if (this.hours === 12) { + this.hours = 0; + } + + if (value === 'PM') { + this.hours += 12; + } + }, + formatter: function(date) { return dateFilter(date, 'a'); } + }, + { + key: 'Z', + regex: '[+-]\\d{4}', + apply: function(value) { + var matches = value.match(/([+-])(\d{2})(\d{2})/), + sign = matches[1], + hours = matches[2], + minutes = matches[3]; + this.hours += toInt(sign + hours); + this.minutes += toInt(sign + minutes); + }, + formatter: function(date) { + return dateFilter(date, 'Z'); + } + }, + { + key: 'ww', + regex: '[0-4][0-9]|5[0-3]', + formatter: function(date) { return dateFilter(date, 'ww'); } + }, + { + key: 'w', + regex: '[0-9]|[1-4][0-9]|5[0-3]', + formatter: function(date) { return dateFilter(date, 'w'); } + }, + { + key: 'GGGG', + regex: $locale.DATETIME_FORMATS.ERANAMES.join('|').replace(/\s/g, '\\s'), + formatter: function(date) { return dateFilter(date, 'GGGG'); } + }, + { + key: 'GGG', + regex: $locale.DATETIME_FORMATS.ERAS.join('|'), + formatter: function(date) { return dateFilter(date, 'GGG'); } + }, + { + key: 'GG', + regex: $locale.DATETIME_FORMATS.ERAS.join('|'), + formatter: function(date) { return dateFilter(date, 'GG'); } + }, + { + key: 'G', + regex: $locale.DATETIME_FORMATS.ERAS.join('|'), + formatter: function(date) { return dateFilter(date, 'G'); } + } + ]; + + if (angular.version.major >= 1 && angular.version.minor > 4) { + formatCodeToRegex.push({ + key: 'LLLL', + regex: $locale.DATETIME_FORMATS.STANDALONEMONTH.join('|'), + apply: function(value) { this.month = $locale.DATETIME_FORMATS.STANDALONEMONTH.indexOf(value); }, + formatter: function(date) { return dateFilter(date, 'LLLL'); } + }); + } + }; + + this.init(); + + function getFormatCodeToRegex(key) { + return filterFilter(formatCodeToRegex, {key: key}, true)[0]; + } + + this.getParser = function (key) { + var f = getFormatCodeToRegex(key); + return f && f.apply || null; + }; + + this.overrideParser = function (key, parser) { + var f = getFormatCodeToRegex(key); + if (f && angular.isFunction(parser)) { + this.parsers = {}; + f.apply = parser; + } + }.bind(this); + + function createParser(format) { + var map = [], regex = format.split(''); + + // check for literal values + var quoteIndex = format.indexOf('\''); + if (quoteIndex > -1) { + var inLiteral = false; + format = format.split(''); + for (var i = quoteIndex; i < format.length; i++) { + if (inLiteral) { + if (format[i] === '\'') { + if (i + 1 < format.length && format[i+1] === '\'') { // escaped single quote + format[i+1] = '$'; + regex[i+1] = ''; + } else { // end of literal + regex[i] = ''; + inLiteral = false; + } + } + format[i] = '$'; + } else { + if (format[i] === '\'') { // start of literal + format[i] = '$'; + regex[i] = ''; + inLiteral = true; + } + } + } + + format = format.join(''); + } + + angular.forEach(formatCodeToRegex, function(data) { + var index = format.indexOf(data.key); + + if (index > -1) { + format = format.split(''); + + regex[index] = '(' + data.regex + ')'; + format[index] = '$'; // Custom symbol to define consumed part of format + for (var i = index + 1, n = index + data.key.length; i < n; i++) { + regex[i] = ''; + format[i] = '$'; + } + format = format.join(''); + + map.push({ + index: index, + key: data.key, + apply: data.apply, + matcher: data.regex + }); + } + }); + + return { + regex: new RegExp('^' + regex.join('') + '$'), + map: orderByFilter(map, 'index') + }; + } + + function createFormatter(format) { + var formatters = []; + var i = 0; + var formatter, literalIdx; + while (i < format.length) { + if (angular.isNumber(literalIdx)) { + if (format.charAt(i) === '\'') { + if (i + 1 >= format.length || format.charAt(i + 1) !== '\'') { + formatters.push(constructLiteralFormatter(format, literalIdx, i)); + literalIdx = null; + } + } else if (i === format.length) { + while (literalIdx < format.length) { + formatter = constructFormatterFromIdx(format, literalIdx); + formatters.push(formatter); + literalIdx = formatter.endIdx; + } + } + + i++; + continue; + } + + if (format.charAt(i) === '\'') { + literalIdx = i; + i++; + continue; + } + + formatter = constructFormatterFromIdx(format, i); + + formatters.push(formatter.parser); + i = formatter.endIdx; + } + + return formatters; + } + + function constructLiteralFormatter(format, literalIdx, endIdx) { + return function() { + return format.substr(literalIdx + 1, endIdx - literalIdx - 1); + }; + } + + function constructFormatterFromIdx(format, i) { + var currentPosStr = format.substr(i); + for (var j = 0; j < formatCodeToRegex.length; j++) { + if (new RegExp('^' + formatCodeToRegex[j].key).test(currentPosStr)) { + var data = formatCodeToRegex[j]; + return { + endIdx: i + data.key.length, + parser: data.formatter + }; + } + } + + return { + endIdx: i + 1, + parser: function() { + return currentPosStr.charAt(0); + } + }; + } + + this.filter = function(date, format) { + if (!angular.isDate(date) || isNaN(date) || !format) { + return ''; + } + + format = $locale.DATETIME_FORMATS[format] || format; + + if ($locale.id !== localeId) { + this.init(); + } + + if (!this.formatters[format]) { + this.formatters[format] = createFormatter(format); + } + + var formatters = this.formatters[format]; + + return formatters.reduce(function(str, formatter) { + return str + formatter(date); + }, ''); + }; + + this.parse = function(input, format, baseDate) { + if (!angular.isString(input) || !format) { + return input; + } + + format = $locale.DATETIME_FORMATS[format] || format; + format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&'); + + if ($locale.id !== localeId) { + this.init(); + } + + if (!this.parsers[format]) { + this.parsers[format] = createParser(format, 'apply'); + } + + var parser = this.parsers[format], + regex = parser.regex, + map = parser.map, + results = input.match(regex), + tzOffset = false; + if (results && results.length) { + var fields, dt; + if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) { + fields = { + year: baseDate.getFullYear(), + month: baseDate.getMonth(), + date: baseDate.getDate(), + hours: baseDate.getHours(), + minutes: baseDate.getMinutes(), + seconds: baseDate.getSeconds(), + milliseconds: baseDate.getMilliseconds() + }; + } else { + if (baseDate) { + $log.warn('dateparser:', 'baseDate is not a valid date'); + } + fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 }; + } + + for (var i = 1, n = results.length; i < n; i++) { + var mapper = map[i - 1]; + if (mapper.matcher === 'Z') { + tzOffset = true; + } + + if (mapper.apply) { + mapper.apply.call(fields, results[i]); + } + } + + var datesetter = tzOffset ? Date.prototype.setUTCFullYear : + Date.prototype.setFullYear; + var timesetter = tzOffset ? Date.prototype.setUTCHours : + Date.prototype.setHours; + + if (isValid(fields.year, fields.month, fields.date)) { + if (angular.isDate(baseDate) && !isNaN(baseDate.getTime()) && !tzOffset) { + dt = new Date(baseDate); + datesetter.call(dt, fields.year, fields.month, fields.date); + timesetter.call(dt, fields.hours, fields.minutes, + fields.seconds, fields.milliseconds); + } else { + dt = new Date(0); + datesetter.call(dt, fields.year, fields.month, fields.date); + timesetter.call(dt, fields.hours || 0, fields.minutes || 0, + fields.seconds || 0, fields.milliseconds || 0); + } + } + + return dt; + } + }; + + // Check if date is valid for specific month (and year for February). + // Month: 0 = Jan, 1 = Feb, etc + function isValid(year, month, date) { + if (date < 1) { + return false; + } + + if (month === 1 && date > 28) { + return date === 29 && (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0); + } + + if (month === 3 || month === 5 || month === 8 || month === 10) { + return date < 31; + } + + return true; + } + + function toInt(str) { + return parseInt(str, 10); + } + + this.toTimezone = toTimezone; + this.fromTimezone = fromTimezone; + this.timezoneToOffset = timezoneToOffset; + this.addDateMinutes = addDateMinutes; + this.convertTimezoneToLocal = convertTimezoneToLocal; + + function toTimezone(date, timezone) { + return date && timezone ? convertTimezoneToLocal(date, timezone) : date; + } + + function fromTimezone(date, timezone) { + return date && timezone ? convertTimezoneToLocal(date, timezone, true) : date; + } + + //https://github.com/angular/angular.js/blob/622c42169699ec07fc6daaa19fe6d224e5d2f70e/src/Angular.js#L1207 + function timezoneToOffset(timezone, fallback) { + timezone = timezone.replace(/:/g, ''); + var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; + return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; + } + + function addDateMinutes(date, minutes) { + date = new Date(date.getTime()); + date.setMinutes(date.getMinutes() + minutes); + return date; + } + + function convertTimezoneToLocal(date, timezone, reverse) { + reverse = reverse ? -1 : 1; + var dateTimezoneOffset = date.getTimezoneOffset(); + var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); + return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset)); + } +}]); + +// Avoiding use of ng-class as it creates a lot of watchers when a class is to be applied to +// at most one element. +angular.module('ui.bootstrap.isClass', []) +.directive('uibIsClass', [ + '$animate', +function ($animate) { + // 11111111 22222222 + var ON_REGEXP = /^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/; + // 11111111 22222222 + var IS_REGEXP = /^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/; + + var dataPerTracked = {}; + + return { + restrict: 'A', + compile: function(tElement, tAttrs) { + var linkedScopes = []; + var instances = []; + var expToData = {}; + var lastActivated = null; + var onExpMatches = tAttrs.uibIsClass.match(ON_REGEXP); + var onExp = onExpMatches[2]; + var expsStr = onExpMatches[1]; + var exps = expsStr.split(','); + + return linkFn; + + function linkFn(scope, element, attrs) { + linkedScopes.push(scope); + instances.push({ + scope: scope, + element: element + }); + + exps.forEach(function(exp, k) { + addForExp(exp, scope); + }); + + scope.$on('$destroy', removeScope); + } + + function addForExp(exp, scope) { + var matches = exp.match(IS_REGEXP); + var clazz = scope.$eval(matches[1]); + var compareWithExp = matches[2]; + var data = expToData[exp]; + if (!data) { + var watchFn = function(compareWithVal) { + var newActivated = null; + instances.some(function(instance) { + var thisVal = instance.scope.$eval(onExp); + if (thisVal === compareWithVal) { + newActivated = instance; + return true; + } + }); + if (data.lastActivated !== newActivated) { + if (data.lastActivated) { + $animate.removeClass(data.lastActivated.element, clazz); + } + if (newActivated) { + $animate.addClass(newActivated.element, clazz); + } + data.lastActivated = newActivated; + } + }; + expToData[exp] = data = { + lastActivated: null, + scope: scope, + watchFn: watchFn, + compareWithExp: compareWithExp, + watcher: scope.$watch(compareWithExp, watchFn) + }; + } + data.watchFn(scope.$eval(compareWithExp)); + } + + function removeScope(e) { + var removedScope = e.targetScope; + var index = linkedScopes.indexOf(removedScope); + linkedScopes.splice(index, 1); + instances.splice(index, 1); + if (linkedScopes.length) { + var newWatchScope = linkedScopes[0]; + angular.forEach(expToData, function(data) { + if (data.scope === removedScope) { + data.watcher = newWatchScope.$watch(data.compareWithExp, data.watchFn); + data.scope = newWatchScope; + } + }); + } else { + expToData = {}; + } + } + } + }; +}]); +angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.isClass']) + +.value('$datepickerSuppressError', false) + +.value('$datepickerLiteralWarning', true) + +.constant('uibDatepickerConfig', { + datepickerMode: 'day', + formatDay: 'dd', + formatMonth: 'MMMM', + formatYear: 'yyyy', + formatDayHeader: 'EEE', + formatDayTitle: 'MMMM yyyy', + formatMonthTitle: 'yyyy', + maxDate: null, + maxMode: 'year', + minDate: null, + minMode: 'day', + monthColumns: 3, + ngModelOptions: {}, + shortcutPropagation: false, + showWeeks: true, + yearColumns: 5, + yearRows: 4 +}) + +.controller('UibDatepickerController', ['$scope', '$element', '$attrs', '$parse', '$interpolate', '$locale', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerLiteralWarning', '$datepickerSuppressError', 'uibDateParser', + function($scope, $element, $attrs, $parse, $interpolate, $locale, $log, dateFilter, datepickerConfig, $datepickerLiteralWarning, $datepickerSuppressError, dateParser) { + var self = this, + ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl; + ngModelOptions = {}, + watchListeners = []; + + $element.addClass('uib-datepicker'); + $attrs.$set('role', 'application'); + + if (!$scope.datepickerOptions) { + $scope.datepickerOptions = {}; + } + + // Modes chain + this.modes = ['day', 'month', 'year']; + + [ + 'customClass', + 'dateDisabled', + 'datepickerMode', + 'formatDay', + 'formatDayHeader', + 'formatDayTitle', + 'formatMonth', + 'formatMonthTitle', + 'formatYear', + 'maxDate', + 'maxMode', + 'minDate', + 'minMode', + 'monthColumns', + 'showWeeks', + 'shortcutPropagation', + 'startingDay', + 'yearColumns', + 'yearRows' + ].forEach(function(key) { + switch (key) { + case 'customClass': + case 'dateDisabled': + $scope[key] = $scope.datepickerOptions[key] || angular.noop; + break; + case 'datepickerMode': + $scope.datepickerMode = angular.isDefined($scope.datepickerOptions.datepickerMode) ? + $scope.datepickerOptions.datepickerMode : datepickerConfig.datepickerMode; + break; + case 'formatDay': + case 'formatDayHeader': + case 'formatDayTitle': + case 'formatMonth': + case 'formatMonthTitle': + case 'formatYear': + self[key] = angular.isDefined($scope.datepickerOptions[key]) ? + $interpolate($scope.datepickerOptions[key])($scope.$parent) : + datepickerConfig[key]; + break; + case 'monthColumns': + case 'showWeeks': + case 'shortcutPropagation': + case 'yearColumns': + case 'yearRows': + self[key] = angular.isDefined($scope.datepickerOptions[key]) ? + $scope.datepickerOptions[key] : datepickerConfig[key]; + break; + case 'startingDay': + if (angular.isDefined($scope.datepickerOptions.startingDay)) { + self.startingDay = $scope.datepickerOptions.startingDay; + } else if (angular.isNumber(datepickerConfig.startingDay)) { + self.startingDay = datepickerConfig.startingDay; + } else { + self.startingDay = ($locale.DATETIME_FORMATS.FIRSTDAYOFWEEK + 8) % 7; + } + + break; + case 'maxDate': + case 'minDate': + $scope.$watch('datepickerOptions.' + key, function(value) { + if (value) { + if (angular.isDate(value)) { + self[key] = dateParser.fromTimezone(new Date(value), ngModelOptions.timezone); + } else { + if ($datepickerLiteralWarning) { + $log.warn('Literal date support has been deprecated, please switch to date object usage'); + } + + self[key] = new Date(dateFilter(value, 'medium')); + } + } else { + self[key] = datepickerConfig[key] ? + dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.timezone) : + null; + } + + self.refreshView(); + }); + + break; + case 'maxMode': + case 'minMode': + if ($scope.datepickerOptions[key]) { + $scope.$watch(function() { return $scope.datepickerOptions[key]; }, function(value) { + self[key] = $scope[key] = angular.isDefined(value) ? value : $scope.datepickerOptions[key]; + if (key === 'minMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) < self.modes.indexOf(self[key]) || + key === 'maxMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) > self.modes.indexOf(self[key])) { + $scope.datepickerMode = self[key]; + $scope.datepickerOptions.datepickerMode = self[key]; + } + }); + } else { + self[key] = $scope[key] = datepickerConfig[key] || null; + } + + break; + } + }); + + $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000); + + $scope.disabled = angular.isDefined($attrs.disabled) || false; + if (angular.isDefined($attrs.ngDisabled)) { + watchListeners.push($scope.$parent.$watch($attrs.ngDisabled, function(disabled) { + $scope.disabled = disabled; + self.refreshView(); + })); + } + + $scope.isActive = function(dateObject) { + if (self.compare(dateObject.date, self.activeDate) === 0) { + $scope.activeDateId = dateObject.uid; + return true; + } + return false; + }; + + this.init = function(ngModelCtrl_) { + ngModelCtrl = ngModelCtrl_; + ngModelOptions = ngModelCtrl_.$options || + $scope.datepickerOptions.ngModelOptions || + datepickerConfig.ngModelOptions; + if ($scope.datepickerOptions.initDate) { + self.activeDate = dateParser.fromTimezone($scope.datepickerOptions.initDate, ngModelOptions.timezone) || new Date(); + $scope.$watch('datepickerOptions.initDate', function(initDate) { + if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) { + self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.timezone); + self.refreshView(); + } + }); + } else { + self.activeDate = new Date(); + } + + var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : new Date(); + this.activeDate = !isNaN(date) ? + dateParser.fromTimezone(date, ngModelOptions.timezone) : + dateParser.fromTimezone(new Date(), ngModelOptions.timezone); + + ngModelCtrl.$render = function() { + self.render(); + }; + }; + + this.render = function() { + if (ngModelCtrl.$viewValue) { + var date = new Date(ngModelCtrl.$viewValue), + isValid = !isNaN(date); + + if (isValid) { + this.activeDate = dateParser.fromTimezone(date, ngModelOptions.timezone); + } else if (!$datepickerSuppressError) { + $log.error('Datepicker directive: "ng-model" value must be a Date object'); + } + } + this.refreshView(); + }; + + this.refreshView = function() { + if (this.element) { + $scope.selectedDt = null; + this._refreshView(); + if ($scope.activeDt) { + $scope.activeDateId = $scope.activeDt.uid; + } + + var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; + date = dateParser.fromTimezone(date, ngModelOptions.timezone); + ngModelCtrl.$setValidity('dateDisabled', !date || + this.element && !this.isDisabled(date)); + } + }; + + this.createDateObject = function(date, format) { + var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; + model = dateParser.fromTimezone(model, ngModelOptions.timezone); + var today = new Date(); + today = dateParser.fromTimezone(today, ngModelOptions.timezone); + var time = this.compare(date, today); + var dt = { + date: date, + label: dateParser.filter(date, format), + selected: model && this.compare(date, model) === 0, + disabled: this.isDisabled(date), + past: time < 0, + current: time === 0, + future: time > 0, + customClass: this.customClass(date) || null + }; + + if (model && this.compare(date, model) === 0) { + $scope.selectedDt = dt; + } + + if (self.activeDate && this.compare(dt.date, self.activeDate) === 0) { + $scope.activeDt = dt; + } + + return dt; + }; + + this.isDisabled = function(date) { + return $scope.disabled || + this.minDate && this.compare(date, this.minDate) < 0 || + this.maxDate && this.compare(date, this.maxDate) > 0 || + $scope.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode}); + }; + + this.customClass = function(date) { + return $scope.customClass({date: date, mode: $scope.datepickerMode}); + }; + + // Split array into smaller arrays + this.split = function(arr, size) { + var arrays = []; + while (arr.length > 0) { + arrays.push(arr.splice(0, size)); + } + return arrays; + }; + + $scope.select = function(date) { + if ($scope.datepickerMode === self.minMode) { + var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.timezone) : new Date(0, 0, 0, 0, 0, 0, 0); + dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); + dt = dateParser.toTimezone(dt, ngModelOptions.timezone); + ngModelCtrl.$setViewValue(dt); + ngModelCtrl.$render(); + } else { + self.activeDate = date; + setMode(self.modes[self.modes.indexOf($scope.datepickerMode) - 1]); + + $scope.$emit('uib:datepicker.mode'); + } + + $scope.$broadcast('uib:datepicker.focus'); + }; + + $scope.move = function(direction) { + var year = self.activeDate.getFullYear() + direction * (self.step.years || 0), + month = self.activeDate.getMonth() + direction * (self.step.months || 0); + self.activeDate.setFullYear(year, month, 1); + self.refreshView(); + }; + + $scope.toggleMode = function(direction) { + direction = direction || 1; + + if ($scope.datepickerMode === self.maxMode && direction === 1 || + $scope.datepickerMode === self.minMode && direction === -1) { + return; + } + + setMode(self.modes[self.modes.indexOf($scope.datepickerMode) + direction]); + + $scope.$emit('uib:datepicker.mode'); + }; + + // Key event mapper + $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' }; + + var focusElement = function() { + self.element[0].focus(); + }; + + // Listen for focus requests from popup directive + $scope.$on('uib:datepicker.focus', focusElement); + + $scope.keydown = function(evt) { + var key = $scope.keys[evt.which]; + + if (!key || evt.shiftKey || evt.altKey || $scope.disabled) { + return; + } + + evt.preventDefault(); + if (!self.shortcutPropagation) { + evt.stopPropagation(); + } + + if (key === 'enter' || key === 'space') { + if (self.isDisabled(self.activeDate)) { + return; // do nothing + } + $scope.select(self.activeDate); + } else if (evt.ctrlKey && (key === 'up' || key === 'down')) { + $scope.toggleMode(key === 'up' ? 1 : -1); + } else { + self.handleKeyDown(key, evt); + self.refreshView(); + } + }; + + $element.on('keydown', function(evt) { + $scope.$apply(function() { + $scope.keydown(evt); + }); + }); + + $scope.$on('$destroy', function() { + //Clear all watch listeners on destroy + while (watchListeners.length) { + watchListeners.shift()(); + } + }); + + function setMode(mode) { + $scope.datepickerMode = mode; + $scope.datepickerOptions.datepickerMode = mode; + } +}]) + +.controller('UibDaypickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { + var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + this.step = { months: 1 }; + this.element = $element; + function getDaysInMonth(year, month) { + return month === 1 && year % 4 === 0 && + (year % 100 !== 0 || year % 400 === 0) ? 29 : DAYS_IN_MONTH[month]; + } + + this.init = function(ctrl) { + angular.extend(ctrl, this); + scope.showWeeks = ctrl.showWeeks; + ctrl.refreshView(); + }; + + this.getDates = function(startDate, n) { + var dates = new Array(n), current = new Date(startDate), i = 0, date; + while (i < n) { + date = new Date(current); + dates[i++] = date; + current.setDate(current.getDate() + 1); + } + return dates; + }; + + this._refreshView = function() { + var year = this.activeDate.getFullYear(), + month = this.activeDate.getMonth(), + firstDayOfMonth = new Date(this.activeDate); + + firstDayOfMonth.setFullYear(year, month, 1); + + var difference = this.startingDay - firstDayOfMonth.getDay(), + numDisplayedFromPreviousMonth = difference > 0 ? + 7 - difference : - difference, + firstDate = new Date(firstDayOfMonth); + + if (numDisplayedFromPreviousMonth > 0) { + firstDate.setDate(-numDisplayedFromPreviousMonth + 1); + } + + // 42 is the number of days on a six-week calendar + var days = this.getDates(firstDate, 42); + for (var i = 0; i < 42; i ++) { + days[i] = angular.extend(this.createDateObject(days[i], this.formatDay), { + secondary: days[i].getMonth() !== month, + uid: scope.uniqueId + '-' + i + }); + } + + scope.labels = new Array(7); + for (var j = 0; j < 7; j++) { + scope.labels[j] = { + abbr: dateFilter(days[j].date, this.formatDayHeader), + full: dateFilter(days[j].date, 'EEEE') + }; + } + + scope.title = dateFilter(this.activeDate, this.formatDayTitle); + scope.rows = this.split(days, 7); + + if (scope.showWeeks) { + scope.weekNumbers = []; + var thursdayIndex = (4 + 7 - this.startingDay) % 7, + numWeeks = scope.rows.length; + for (var curWeek = 0; curWeek < numWeeks; curWeek++) { + scope.weekNumbers.push( + getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date)); + } + } + }; + + this.compare = function(date1, date2) { + var _date1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()); + var _date2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()); + _date1.setFullYear(date1.getFullYear()); + _date2.setFullYear(date2.getFullYear()); + return _date1 - _date2; + }; + + function getISO8601WeekNumber(date) { + var checkDate = new Date(date); + checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday + var time = checkDate.getTime(); + checkDate.setMonth(0); // Compare with Jan 1 + checkDate.setDate(1); + return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1; + } + + this.handleKeyDown = function(key, evt) { + var date = this.activeDate.getDate(); + + if (key === 'left') { + date = date - 1; + } else if (key === 'up') { + date = date - 7; + } else if (key === 'right') { + date = date + 1; + } else if (key === 'down') { + date = date + 7; + } else if (key === 'pageup' || key === 'pagedown') { + var month = this.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1); + this.activeDate.setMonth(month, 1); + date = Math.min(getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()), date); + } else if (key === 'home') { + date = 1; + } else if (key === 'end') { + date = getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()); + } + this.activeDate.setDate(date); + }; +}]) + +.controller('UibMonthpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { + this.step = { years: 1 }; + this.element = $element; + + this.init = function(ctrl) { + angular.extend(ctrl, this); + ctrl.refreshView(); + }; + + this._refreshView = function() { + var months = new Array(12), + year = this.activeDate.getFullYear(), + date; + + for (var i = 0; i < 12; i++) { + date = new Date(this.activeDate); + date.setFullYear(year, i, 1); + months[i] = angular.extend(this.createDateObject(date, this.formatMonth), { + uid: scope.uniqueId + '-' + i + }); + } + + scope.title = dateFilter(this.activeDate, this.formatMonthTitle); + scope.rows = this.split(months, this.monthColumns); + scope.yearHeaderColspan = this.monthColumns > 3 ? this.monthColumns - 2 : 1; + }; + + this.compare = function(date1, date2) { + var _date1 = new Date(date1.getFullYear(), date1.getMonth()); + var _date2 = new Date(date2.getFullYear(), date2.getMonth()); + _date1.setFullYear(date1.getFullYear()); + _date2.setFullYear(date2.getFullYear()); + return _date1 - _date2; + }; + + this.handleKeyDown = function(key, evt) { + var date = this.activeDate.getMonth(); + + if (key === 'left') { + date = date - 1; + } else if (key === 'up') { + date = date - this.monthColumns; + } else if (key === 'right') { + date = date + 1; + } else if (key === 'down') { + date = date + this.monthColumns; + } else if (key === 'pageup' || key === 'pagedown') { + var year = this.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1); + this.activeDate.setFullYear(year); + } else if (key === 'home') { + date = 0; + } else if (key === 'end') { + date = 11; + } + this.activeDate.setMonth(date); + }; +}]) + +.controller('UibYearpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { + var columns, range; + this.element = $element; + + function getStartingYear(year) { + return parseInt((year - 1) / range, 10) * range + 1; + } + + this.yearpickerInit = function() { + columns = this.yearColumns; + range = this.yearRows * columns; + this.step = { years: range }; + }; + + this._refreshView = function() { + var years = new Array(range), date; + + for (var i = 0, start = getStartingYear(this.activeDate.getFullYear()); i < range; i++) { + date = new Date(this.activeDate); + date.setFullYear(start + i, 0, 1); + years[i] = angular.extend(this.createDateObject(date, this.formatYear), { + uid: scope.uniqueId + '-' + i + }); + } + + scope.title = [years[0].label, years[range - 1].label].join(' - '); + scope.rows = this.split(years, columns); + scope.columns = columns; + }; + + this.compare = function(date1, date2) { + return date1.getFullYear() - date2.getFullYear(); + }; + + this.handleKeyDown = function(key, evt) { + var date = this.activeDate.getFullYear(); + + if (key === 'left') { + date = date - 1; + } else if (key === 'up') { + date = date - columns; + } else if (key === 'right') { + date = date + 1; + } else if (key === 'down') { + date = date + columns; + } else if (key === 'pageup' || key === 'pagedown') { + date += (key === 'pageup' ? - 1 : 1) * range; + } else if (key === 'home') { + date = getStartingYear(this.activeDate.getFullYear()); + } else if (key === 'end') { + date = getStartingYear(this.activeDate.getFullYear()) + range - 1; + } + this.activeDate.setFullYear(date); + }; +}]) + +.directive('uibDatepicker', function() { + return { + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/datepicker/datepicker.html'; + }, + scope: { + datepickerOptions: '=?' + }, + require: ['uibDatepicker', '^ngModel'], + restrict: 'A', + controller: 'UibDatepickerController', + controllerAs: 'datepicker', + link: function(scope, element, attrs, ctrls) { + var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + datepickerCtrl.init(ngModelCtrl); + } + }; +}) + +.directive('uibDaypicker', function() { + return { + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/datepicker/day.html'; + }, + require: ['^uibDatepicker', 'uibDaypicker'], + restrict: 'A', + controller: 'UibDaypickerController', + link: function(scope, element, attrs, ctrls) { + var datepickerCtrl = ctrls[0], + daypickerCtrl = ctrls[1]; + + daypickerCtrl.init(datepickerCtrl); + } + }; +}) + +.directive('uibMonthpicker', function() { + return { + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/datepicker/month.html'; + }, + require: ['^uibDatepicker', 'uibMonthpicker'], + restrict: 'A', + controller: 'UibMonthpickerController', + link: function(scope, element, attrs, ctrls) { + var datepickerCtrl = ctrls[0], + monthpickerCtrl = ctrls[1]; + + monthpickerCtrl.init(datepickerCtrl); + } + }; +}) + +.directive('uibYearpicker', function() { + return { + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/datepicker/year.html'; + }, + require: ['^uibDatepicker', 'uibYearpicker'], + restrict: 'A', + controller: 'UibYearpickerController', + link: function(scope, element, attrs, ctrls) { + var ctrl = ctrls[0]; + angular.extend(ctrl, ctrls[1]); + ctrl.yearpickerInit(); + + ctrl.refreshView(); + } + }; +}); + +angular.module('ui.bootstrap.position', []) + +/** + * A set of utility methods for working with the DOM. + * It is meant to be used where we need to absolute-position elements in + * relation to another element (this is the case for tooltips, popovers, + * typeahead suggestions etc.). + */ + .factory('$uibPosition', ['$document', '$window', function($document, $window) { + /** + * Used by scrollbarWidth() function to cache scrollbar's width. + * Do not access this variable directly, use scrollbarWidth() instead. + */ + var SCROLLBAR_WIDTH; + /** + * scrollbar on body and html element in IE and Edge overlay + * content and should be considered 0 width. + */ + var BODY_SCROLLBAR_WIDTH; + var OVERFLOW_REGEX = { + normal: /(auto|scroll)/, + hidden: /(auto|scroll|hidden)/ + }; + var PLACEMENT_REGEX = { + auto: /\s?auto?\s?/i, + primary: /^(top|bottom|left|right)$/, + secondary: /^(top|bottom|left|right|center)$/, + vertical: /^(top|bottom)$/ + }; + var BODY_REGEX = /(HTML|BODY)/; + + return { + + /** + * Provides a raw DOM element from a jQuery/jQLite element. + * + * @param {element} elem - The element to convert. + * + * @returns {element} A HTML element. + */ + getRawNode: function(elem) { + return elem.nodeName ? elem : elem[0] || elem; + }, + + /** + * Provides a parsed number for a style property. Strips + * units and casts invalid numbers to 0. + * + * @param {string} value - The style value to parse. + * + * @returns {number} A valid number. + */ + parseStyle: function(value) { + value = parseFloat(value); + return isFinite(value) ? value : 0; + }, + + /** + * Provides the closest positioned ancestor. + * + * @param {element} element - The element to get the offest parent for. + * + * @returns {element} The closest positioned ancestor. + */ + offsetParent: function(elem) { + elem = this.getRawNode(elem); + + var offsetParent = elem.offsetParent || $document[0].documentElement; + + function isStaticPositioned(el) { + return ($window.getComputedStyle(el).position || 'static') === 'static'; + } + + while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) { + offsetParent = offsetParent.offsetParent; + } + + return offsetParent || $document[0].documentElement; + }, + + /** + * Provides the scrollbar width, concept from TWBS measureScrollbar() + * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js + * In IE and Edge, scollbar on body and html element overlay and should + * return a width of 0. + * + * @returns {number} The width of the browser scollbar. + */ + scrollbarWidth: function(isBody) { + if (isBody) { + if (angular.isUndefined(BODY_SCROLLBAR_WIDTH)) { + var bodyElem = $document.find('body'); + bodyElem.addClass('uib-position-body-scrollbar-measure'); + BODY_SCROLLBAR_WIDTH = $window.innerWidth - bodyElem[0].clientWidth; + BODY_SCROLLBAR_WIDTH = isFinite(BODY_SCROLLBAR_WIDTH) ? BODY_SCROLLBAR_WIDTH : 0; + bodyElem.removeClass('uib-position-body-scrollbar-measure'); + } + return BODY_SCROLLBAR_WIDTH; + } + + if (angular.isUndefined(SCROLLBAR_WIDTH)) { + var scrollElem = angular.element('
'); + $document.find('body').append(scrollElem); + SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth; + SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0; + scrollElem.remove(); + } + + return SCROLLBAR_WIDTH; + }, + + /** + * Provides the padding required on an element to replace the scrollbar. + * + * @returns {object} An object with the following properties: + *
    + *
  • **scrollbarWidth**: the width of the scrollbar
  • + *
  • **widthOverflow**: whether the the width is overflowing
  • + *
  • **right**: the amount of right padding on the element needed to replace the scrollbar
  • + *
  • **rightOriginal**: the amount of right padding currently on the element
  • + *
  • **heightOverflow**: whether the the height is overflowing
  • + *
  • **bottom**: the amount of bottom padding on the element needed to replace the scrollbar
  • + *
  • **bottomOriginal**: the amount of bottom padding currently on the element
  • + *
+ */ + scrollbarPadding: function(elem) { + elem = this.getRawNode(elem); + + var elemStyle = $window.getComputedStyle(elem); + var paddingRight = this.parseStyle(elemStyle.paddingRight); + var paddingBottom = this.parseStyle(elemStyle.paddingBottom); + var scrollParent = this.scrollParent(elem, false, true); + var scrollbarWidth = this.scrollbarWidth(BODY_REGEX.test(scrollParent.tagName)); + + return { + scrollbarWidth: scrollbarWidth, + widthOverflow: scrollParent.scrollWidth > scrollParent.clientWidth, + right: paddingRight + scrollbarWidth, + originalRight: paddingRight, + heightOverflow: scrollParent.scrollHeight > scrollParent.clientHeight, + bottom: paddingBottom + scrollbarWidth, + originalBottom: paddingBottom + }; + }, + + /** + * Checks to see if the element is scrollable. + * + * @param {element} elem - The element to check. + * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered, + * default is false. + * + * @returns {boolean} Whether the element is scrollable. + */ + isScrollable: function(elem, includeHidden) { + elem = this.getRawNode(elem); + + var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal; + var elemStyle = $window.getComputedStyle(elem); + return overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX); + }, + + /** + * Provides the closest scrollable ancestor. + * A port of the jQuery UI scrollParent method: + * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js + * + * @param {element} elem - The element to find the scroll parent of. + * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered, + * default is false. + * @param {boolean=} [includeSelf=false] - Should the element being passed be + * included in the scrollable llokup. + * + * @returns {element} A HTML element. + */ + scrollParent: function(elem, includeHidden, includeSelf) { + elem = this.getRawNode(elem); + + var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal; + var documentEl = $document[0].documentElement; + var elemStyle = $window.getComputedStyle(elem); + if (includeSelf && overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX)) { + return elem; + } + var excludeStatic = elemStyle.position === 'absolute'; + var scrollParent = elem.parentElement || documentEl; + + if (scrollParent === documentEl || elemStyle.position === 'fixed') { + return documentEl; + } + + while (scrollParent.parentElement && scrollParent !== documentEl) { + var spStyle = $window.getComputedStyle(scrollParent); + if (excludeStatic && spStyle.position !== 'static') { + excludeStatic = false; + } + + if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) { + break; + } + scrollParent = scrollParent.parentElement; + } + + return scrollParent; + }, + + /** + * Provides read-only equivalent of jQuery's position function: + * http://api.jquery.com/position/ - distance to closest positioned + * ancestor. Does not account for margins by default like jQuery position. + * + * @param {element} elem - The element to caclulate the position on. + * @param {boolean=} [includeMargins=false] - Should margins be accounted + * for, default is false. + * + * @returns {object} An object with the following properties: + *
    + *
  • **width**: the width of the element
  • + *
  • **height**: the height of the element
  • + *
  • **top**: distance to top edge of offset parent
  • + *
  • **left**: distance to left edge of offset parent
  • + *
+ */ + position: function(elem, includeMagins) { + elem = this.getRawNode(elem); + + var elemOffset = this.offset(elem); + if (includeMagins) { + var elemStyle = $window.getComputedStyle(elem); + elemOffset.top -= this.parseStyle(elemStyle.marginTop); + elemOffset.left -= this.parseStyle(elemStyle.marginLeft); + } + var parent = this.offsetParent(elem); + var parentOffset = {top: 0, left: 0}; + + if (parent !== $document[0].documentElement) { + parentOffset = this.offset(parent); + parentOffset.top += parent.clientTop - parent.scrollTop; + parentOffset.left += parent.clientLeft - parent.scrollLeft; + } + + return { + width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth), + height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight), + top: Math.round(elemOffset.top - parentOffset.top), + left: Math.round(elemOffset.left - parentOffset.left) + }; + }, + + /** + * Provides read-only equivalent of jQuery's offset function: + * http://api.jquery.com/offset/ - distance to viewport. Does + * not account for borders, margins, or padding on the body + * element. + * + * @param {element} elem - The element to calculate the offset on. + * + * @returns {object} An object with the following properties: + *
    + *
  • **width**: the width of the element
  • + *
  • **height**: the height of the element
  • + *
  • **top**: distance to top edge of viewport
  • + *
  • **right**: distance to bottom edge of viewport
  • + *
+ */ + offset: function(elem) { + elem = this.getRawNode(elem); + + var elemBCR = elem.getBoundingClientRect(); + return { + width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth), + height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight), + top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)), + left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)) + }; + }, + + /** + * Provides offset distance to the closest scrollable ancestor + * or viewport. Accounts for border and scrollbar width. + * + * Right and bottom dimensions represent the distance to the + * respective edge of the viewport element. If the element + * edge extends beyond the viewport, a negative value will be + * reported. + * + * @param {element} elem - The element to get the viewport offset for. + * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead + * of the first scrollable element, default is false. + * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element + * be accounted for, default is true. + * + * @returns {object} An object with the following properties: + *
    + *
  • **top**: distance to the top content edge of viewport element
  • + *
  • **bottom**: distance to the bottom content edge of viewport element
  • + *
  • **left**: distance to the left content edge of viewport element
  • + *
  • **right**: distance to the right content edge of viewport element
  • + *
+ */ + viewportOffset: function(elem, useDocument, includePadding) { + elem = this.getRawNode(elem); + includePadding = includePadding !== false ? true : false; + + var elemBCR = elem.getBoundingClientRect(); + var offsetBCR = {top: 0, left: 0, bottom: 0, right: 0}; + + var offsetParent = useDocument ? $document[0].documentElement : this.scrollParent(elem); + var offsetParentBCR = offsetParent.getBoundingClientRect(); + + offsetBCR.top = offsetParentBCR.top + offsetParent.clientTop; + offsetBCR.left = offsetParentBCR.left + offsetParent.clientLeft; + if (offsetParent === $document[0].documentElement) { + offsetBCR.top += $window.pageYOffset; + offsetBCR.left += $window.pageXOffset; + } + offsetBCR.bottom = offsetBCR.top + offsetParent.clientHeight; + offsetBCR.right = offsetBCR.left + offsetParent.clientWidth; + + if (includePadding) { + var offsetParentStyle = $window.getComputedStyle(offsetParent); + offsetBCR.top += this.parseStyle(offsetParentStyle.paddingTop); + offsetBCR.bottom -= this.parseStyle(offsetParentStyle.paddingBottom); + offsetBCR.left += this.parseStyle(offsetParentStyle.paddingLeft); + offsetBCR.right -= this.parseStyle(offsetParentStyle.paddingRight); + } + + return { + top: Math.round(elemBCR.top - offsetBCR.top), + bottom: Math.round(offsetBCR.bottom - elemBCR.bottom), + left: Math.round(elemBCR.left - offsetBCR.left), + right: Math.round(offsetBCR.right - elemBCR.right) + }; + }, + + /** + * Provides an array of placement values parsed from a placement string. + * Along with the 'auto' indicator, supported placement strings are: + *
    + *
  • top: element on top, horizontally centered on host element.
  • + *
  • top-left: element on top, left edge aligned with host element left edge.
  • + *
  • top-right: element on top, lerightft edge aligned with host element right edge.
  • + *
  • bottom: element on bottom, horizontally centered on host element.
  • + *
  • bottom-left: element on bottom, left edge aligned with host element left edge.
  • + *
  • bottom-right: element on bottom, right edge aligned with host element right edge.
  • + *
  • left: element on left, vertically centered on host element.
  • + *
  • left-top: element on left, top edge aligned with host element top edge.
  • + *
  • left-bottom: element on left, bottom edge aligned with host element bottom edge.
  • + *
  • right: element on right, vertically centered on host element.
  • + *
  • right-top: element on right, top edge aligned with host element top edge.
  • + *
  • right-bottom: element on right, bottom edge aligned with host element bottom edge.
  • + *
+ * A placement string with an 'auto' indicator is expected to be + * space separated from the placement, i.e: 'auto bottom-left' If + * the primary and secondary placement values do not match 'top, + * bottom, left, right' then 'top' will be the primary placement and + * 'center' will be the secondary placement. If 'auto' is passed, true + * will be returned as the 3rd value of the array. + * + * @param {string} placement - The placement string to parse. + * + * @returns {array} An array with the following values + *
    + *
  • **[0]**: The primary placement.
  • + *
  • **[1]**: The secondary placement.
  • + *
  • **[2]**: If auto is passed: true, else undefined.
  • + *
+ */ + parsePlacement: function(placement) { + var autoPlace = PLACEMENT_REGEX.auto.test(placement); + if (autoPlace) { + placement = placement.replace(PLACEMENT_REGEX.auto, ''); + } + + placement = placement.split('-'); + + placement[0] = placement[0] || 'top'; + if (!PLACEMENT_REGEX.primary.test(placement[0])) { + placement[0] = 'top'; + } + + placement[1] = placement[1] || 'center'; + if (!PLACEMENT_REGEX.secondary.test(placement[1])) { + placement[1] = 'center'; + } + + if (autoPlace) { + placement[2] = true; + } else { + placement[2] = false; + } + + return placement; + }, + + /** + * Provides coordinates for an element to be positioned relative to + * another element. Passing 'auto' as part of the placement parameter + * will enable smart placement - where the element fits. i.e: + * 'auto left-top' will check to see if there is enough space to the left + * of the hostElem to fit the targetElem, if not place right (same for secondary + * top placement). Available space is calculated using the viewportOffset + * function. + * + * @param {element} hostElem - The element to position against. + * @param {element} targetElem - The element to position. + * @param {string=} [placement=top] - The placement for the targetElem, + * default is 'top'. 'center' is assumed as secondary placement for + * 'top', 'left', 'right', and 'bottom' placements. Available placements are: + *
    + *
  • top
  • + *
  • top-right
  • + *
  • top-left
  • + *
  • bottom
  • + *
  • bottom-left
  • + *
  • bottom-right
  • + *
  • left
  • + *
  • left-top
  • + *
  • left-bottom
  • + *
  • right
  • + *
  • right-top
  • + *
  • right-bottom
  • + *
+ * @param {boolean=} [appendToBody=false] - Should the top and left values returned + * be calculated from the body element, default is false. + * + * @returns {object} An object with the following properties: + *
    + *
  • **top**: Value for targetElem top.
  • + *
  • **left**: Value for targetElem left.
  • + *
  • **placement**: The resolved placement.
  • + *
+ */ + positionElements: function(hostElem, targetElem, placement, appendToBody) { + hostElem = this.getRawNode(hostElem); + targetElem = this.getRawNode(targetElem); + + // need to read from prop to support tests. + var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth'); + var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight'); + + placement = this.parsePlacement(placement); + + var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem); + var targetElemPos = {top: 0, left: 0, placement: ''}; + + if (placement[2]) { + var viewportOffset = this.viewportOffset(hostElem, appendToBody); + + var targetElemStyle = $window.getComputedStyle(targetElem); + var adjustedSize = { + width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))), + height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom))) + }; + + placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' : + placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' : + placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' : + placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' : + placement[0]; + + placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' : + placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' : + placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' : + placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' : + placement[1]; + + if (placement[1] === 'center') { + if (PLACEMENT_REGEX.vertical.test(placement[0])) { + var xOverflow = hostElemPos.width / 2 - targetWidth / 2; + if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) { + placement[1] = 'left'; + } else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) { + placement[1] = 'right'; + } + } else { + var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2; + if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) { + placement[1] = 'top'; + } else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) { + placement[1] = 'bottom'; + } + } + } + } + + switch (placement[0]) { + case 'top': + targetElemPos.top = hostElemPos.top - targetHeight; + break; + case 'bottom': + targetElemPos.top = hostElemPos.top + hostElemPos.height; + break; + case 'left': + targetElemPos.left = hostElemPos.left - targetWidth; + break; + case 'right': + targetElemPos.left = hostElemPos.left + hostElemPos.width; + break; + } + + switch (placement[1]) { + case 'top': + targetElemPos.top = hostElemPos.top; + break; + case 'bottom': + targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight; + break; + case 'left': + targetElemPos.left = hostElemPos.left; + break; + case 'right': + targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth; + break; + case 'center': + if (PLACEMENT_REGEX.vertical.test(placement[0])) { + targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2; + } else { + targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2; + } + break; + } + + targetElemPos.top = Math.round(targetElemPos.top); + targetElemPos.left = Math.round(targetElemPos.left); + targetElemPos.placement = placement[1] === 'center' ? placement[0] : placement[0] + '-' + placement[1]; + + return targetElemPos; + }, + + /** + * Provides a way to adjust the top positioning after first + * render to correctly align element to top after content + * rendering causes resized element height + * + * @param {array} placementClasses - The array of strings of classes + * element should have. + * @param {object} containerPosition - The object with container + * position information + * @param {number} initialHeight - The initial height for the elem. + * @param {number} currentHeight - The current height for the elem. + */ + adjustTop: function(placementClasses, containerPosition, initialHeight, currentHeight) { + if (placementClasses.indexOf('top') !== -1 && initialHeight !== currentHeight) { + return { + top: containerPosition.top - currentHeight + 'px' + }; + } + }, + + /** + * Provides a way for positioning tooltip & dropdown + * arrows when using placement options beyond the standard + * left, right, top, or bottom. + * + * @param {element} elem - The tooltip/dropdown element. + * @param {string} placement - The placement for the elem. + */ + positionArrow: function(elem, placement) { + elem = this.getRawNode(elem); + + var innerElem = elem.querySelector('.tooltip-inner, .popover-inner'); + if (!innerElem) { + return; + } + + var isTooltip = angular.element(innerElem).hasClass('tooltip-inner'); + + var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow'); + if (!arrowElem) { + return; + } + + var arrowCss = { + top: '', + bottom: '', + left: '', + right: '' + }; + + placement = this.parsePlacement(placement); + if (placement[1] === 'center') { + // no adjustment necessary - just reset styles + angular.element(arrowElem).css(arrowCss); + return; + } + + var borderProp = 'border-' + placement[0] + '-width'; + var borderWidth = $window.getComputedStyle(arrowElem)[borderProp]; + + var borderRadiusProp = 'border-'; + if (PLACEMENT_REGEX.vertical.test(placement[0])) { + borderRadiusProp += placement[0] + '-' + placement[1]; + } else { + borderRadiusProp += placement[1] + '-' + placement[0]; + } + borderRadiusProp += '-radius'; + var borderRadius = $window.getComputedStyle(isTooltip ? innerElem : elem)[borderRadiusProp]; + + switch (placement[0]) { + case 'top': + arrowCss.bottom = isTooltip ? '0' : '-' + borderWidth; + break; + case 'bottom': + arrowCss.top = isTooltip ? '0' : '-' + borderWidth; + break; + case 'left': + arrowCss.right = isTooltip ? '0' : '-' + borderWidth; + break; + case 'right': + arrowCss.left = isTooltip ? '0' : '-' + borderWidth; + break; + } + + arrowCss[placement[1]] = borderRadius; + + angular.element(arrowElem).css(arrowCss); + } + }; + }]); + +angular.module('ui.bootstrap.datepickerPopup', ['ui.bootstrap.datepicker', 'ui.bootstrap.position']) + +.value('$datepickerPopupLiteralWarning', true) + +.constant('uibDatepickerPopupConfig', { + altInputFormats: [], + appendToBody: false, + clearText: 'Clear', + closeOnDateSelection: true, + closeText: 'Done', + currentText: 'Today', + datepickerPopup: 'yyyy-MM-dd', + datepickerPopupTemplateUrl: 'uib/template/datepickerPopup/popup.html', + datepickerTemplateUrl: 'uib/template/datepicker/datepicker.html', + html5Types: { + date: 'yyyy-MM-dd', + 'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss', + 'month': 'yyyy-MM' + }, + onOpenFocus: true, + showButtonBar: true, + placement: 'auto bottom-left' +}) + +.controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$log', '$parse', '$window', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout', 'uibDatepickerConfig', '$datepickerPopupLiteralWarning', +function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout, datepickerConfig, $datepickerPopupLiteralWarning) { + var cache = {}, + isHtml5DateInput = false; + var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus, + datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl, scrollParentEl, + ngModel, ngModelOptions, $popup, altInputFormats, watchListeners = []; + + this.init = function(_ngModel_) { + ngModel = _ngModel_; + ngModelOptions = angular.isObject(_ngModel_.$options) ? + _ngModel_.$options : + { + timezone: null + }; + closeOnDateSelection = angular.isDefined($attrs.closeOnDateSelection) ? + $scope.$parent.$eval($attrs.closeOnDateSelection) : + datepickerPopupConfig.closeOnDateSelection; + appendToBody = angular.isDefined($attrs.datepickerAppendToBody) ? + $scope.$parent.$eval($attrs.datepickerAppendToBody) : + datepickerPopupConfig.appendToBody; + onOpenFocus = angular.isDefined($attrs.onOpenFocus) ? + $scope.$parent.$eval($attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus; + datepickerPopupTemplateUrl = angular.isDefined($attrs.datepickerPopupTemplateUrl) ? + $attrs.datepickerPopupTemplateUrl : + datepickerPopupConfig.datepickerPopupTemplateUrl; + datepickerTemplateUrl = angular.isDefined($attrs.datepickerTemplateUrl) ? + $attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl; + altInputFormats = angular.isDefined($attrs.altInputFormats) ? + $scope.$parent.$eval($attrs.altInputFormats) : + datepickerPopupConfig.altInputFormats; + + $scope.showButtonBar = angular.isDefined($attrs.showButtonBar) ? + $scope.$parent.$eval($attrs.showButtonBar) : + datepickerPopupConfig.showButtonBar; + + if (datepickerPopupConfig.html5Types[$attrs.type]) { + dateFormat = datepickerPopupConfig.html5Types[$attrs.type]; + isHtml5DateInput = true; + } else { + dateFormat = $attrs.uibDatepickerPopup || datepickerPopupConfig.datepickerPopup; + $attrs.$observe('uibDatepickerPopup', function(value, oldValue) { + var newDateFormat = value || datepickerPopupConfig.datepickerPopup; + // Invalidate the $modelValue to ensure that formatters re-run + // FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764 + if (newDateFormat !== dateFormat) { + dateFormat = newDateFormat; + ngModel.$modelValue = null; + + if (!dateFormat) { + throw new Error('uibDatepickerPopup must have a date format specified.'); + } + } + }); + } + + if (!dateFormat) { + throw new Error('uibDatepickerPopup must have a date format specified.'); + } + + if (isHtml5DateInput && $attrs.uibDatepickerPopup) { + throw new Error('HTML5 date input types do not support custom formats.'); + } + + // popup element used to display calendar + popupEl = angular.element('
'); + + popupEl.attr({ + 'ng-model': 'date', + 'ng-change': 'dateSelection(date)', + 'template-url': datepickerPopupTemplateUrl + }); + + // datepicker element + datepickerEl = angular.element(popupEl.children()[0]); + datepickerEl.attr('template-url', datepickerTemplateUrl); + + if (!$scope.datepickerOptions) { + $scope.datepickerOptions = {}; + } + + if (isHtml5DateInput) { + if ($attrs.type === 'month') { + $scope.datepickerOptions.datepickerMode = 'month'; + $scope.datepickerOptions.minMode = 'month'; + } + } + + datepickerEl.attr('datepicker-options', 'datepickerOptions'); + + if (!isHtml5DateInput) { + // Internal API to maintain the correct ng-invalid-[key] class + ngModel.$$parserName = 'date'; + ngModel.$validators.date = validator; + ngModel.$parsers.unshift(parseDate); + ngModel.$formatters.push(function(value) { + if (ngModel.$isEmpty(value)) { + $scope.date = value; + return value; + } + + if (angular.isNumber(value)) { + value = new Date(value); + } + + $scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone); + + return dateParser.filter($scope.date, dateFormat); + }); + } else { + ngModel.$formatters.push(function(value) { + $scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone); + return value; + }); + } + + // Detect changes in the view from the text box + ngModel.$viewChangeListeners.push(function() { + $scope.date = parseDateString(ngModel.$viewValue); + }); + + $element.on('keydown', inputKeydownBind); + + $popup = $compile(popupEl)($scope); + // Prevent jQuery cache memory leak (template is now redundant after linking) + popupEl.remove(); + + if (appendToBody) { + $document.find('body').append($popup); + } else { + $element.after($popup); + } + + $scope.$on('$destroy', function() { + if ($scope.isOpen === true) { + if (!$rootScope.$$phase) { + $scope.$apply(function() { + $scope.isOpen = false; + }); + } + } + + $popup.remove(); + $element.off('keydown', inputKeydownBind); + $document.off('click', documentClickBind); + if (scrollParentEl) { + scrollParentEl.off('scroll', positionPopup); + } + angular.element($window).off('resize', positionPopup); + + //Clear all watch listeners on destroy + while (watchListeners.length) { + watchListeners.shift()(); + } + }); + }; + + $scope.getText = function(key) { + return $scope[key + 'Text'] || datepickerPopupConfig[key + 'Text']; + }; + + $scope.isDisabled = function(date) { + if (date === 'today') { + date = dateParser.fromTimezone(new Date(), ngModelOptions.timezone); + } + + var dates = {}; + angular.forEach(['minDate', 'maxDate'], function(key) { + if (!$scope.datepickerOptions[key]) { + dates[key] = null; + } else if (angular.isDate($scope.datepickerOptions[key])) { + dates[key] = new Date($scope.datepickerOptions[key]); + } else { + if ($datepickerPopupLiteralWarning) { + $log.warn('Literal date support has been deprecated, please switch to date object usage'); + } + + dates[key] = new Date(dateFilter($scope.datepickerOptions[key], 'medium')); + } + }); + + return $scope.datepickerOptions && + dates.minDate && $scope.compare(date, dates.minDate) < 0 || + dates.maxDate && $scope.compare(date, dates.maxDate) > 0; + }; + + $scope.compare = function(date1, date2) { + return new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()); + }; + + // Inner change + $scope.dateSelection = function(dt) { + $scope.date = dt; + var date = $scope.date ? dateParser.filter($scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function + $element.val(date); + ngModel.$setViewValue(date); + + if (closeOnDateSelection) { + $scope.isOpen = false; + $element[0].focus(); + } + }; + + $scope.keydown = function(evt) { + if (evt.which === 27) { + evt.stopPropagation(); + $scope.isOpen = false; + $element[0].focus(); + } + }; + + $scope.select = function(date, evt) { + evt.stopPropagation(); + + if (date === 'today') { + var today = new Date(); + if (angular.isDate($scope.date)) { + date = new Date($scope.date); + date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate()); + } else { + date = dateParser.fromTimezone(today, ngModelOptions.timezone); + date.setHours(0, 0, 0, 0); + } + } + $scope.dateSelection(date); + }; + + $scope.close = function(evt) { + evt.stopPropagation(); + + $scope.isOpen = false; + $element[0].focus(); + }; + + $scope.disabled = angular.isDefined($attrs.disabled) || false; + if ($attrs.ngDisabled) { + watchListeners.push($scope.$parent.$watch($parse($attrs.ngDisabled), function(disabled) { + $scope.disabled = disabled; + })); + } + + $scope.$watch('isOpen', function(value) { + if (value) { + if (!$scope.disabled) { + $timeout(function() { + positionPopup(); + + if (onOpenFocus) { + $scope.$broadcast('uib:datepicker.focus'); + } + + $document.on('click', documentClickBind); + + var placement = $attrs.popupPlacement ? $attrs.popupPlacement : datepickerPopupConfig.placement; + if (appendToBody || $position.parsePlacement(placement)[2]) { + scrollParentEl = scrollParentEl || angular.element($position.scrollParent($element)); + if (scrollParentEl) { + scrollParentEl.on('scroll', positionPopup); + } + } else { + scrollParentEl = null; + } + + angular.element($window).on('resize', positionPopup); + }, 0, false); + } else { + $scope.isOpen = false; + } + } else { + $document.off('click', documentClickBind); + if (scrollParentEl) { + scrollParentEl.off('scroll', positionPopup); + } + angular.element($window).off('resize', positionPopup); + } + }); + + function cameltoDash(string) { + return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); }); + } + + function parseDateString(viewValue) { + var date = dateParser.parse(viewValue, dateFormat, $scope.date); + if (isNaN(date)) { + for (var i = 0; i < altInputFormats.length; i++) { + date = dateParser.parse(viewValue, altInputFormats[i], $scope.date); + if (!isNaN(date)) { + return date; + } + } + } + return date; + } + + function parseDate(viewValue) { + if (angular.isNumber(viewValue)) { + // presumably timestamp to date object + viewValue = new Date(viewValue); + } + + if (!viewValue) { + return null; + } + + if (angular.isDate(viewValue) && !isNaN(viewValue)) { + return viewValue; + } + + if (angular.isString(viewValue)) { + var date = parseDateString(viewValue); + if (!isNaN(date)) { + return dateParser.toTimezone(date, ngModelOptions.timezone); + } + } + + return ngModel.$options && ngModel.$options.allowInvalid ? viewValue : undefined; + } + + function validator(modelValue, viewValue) { + var value = modelValue || viewValue; + + if (!$attrs.ngRequired && !value) { + return true; + } + + if (angular.isNumber(value)) { + value = new Date(value); + } + + if (!value) { + return true; + } + + if (angular.isDate(value) && !isNaN(value)) { + return true; + } + + if (angular.isString(value)) { + return !isNaN(parseDateString(value)); + } + + return false; + } + + function documentClickBind(event) { + if (!$scope.isOpen && $scope.disabled) { + return; + } + + var popup = $popup[0]; + var dpContainsTarget = $element[0].contains(event.target); + // The popup node may not be an element node + // In some browsers (IE) only element nodes have the 'contains' function + var popupContainsTarget = popup.contains !== undefined && popup.contains(event.target); + if ($scope.isOpen && !(dpContainsTarget || popupContainsTarget)) { + $scope.$apply(function() { + $scope.isOpen = false; + }); + } + } + + function inputKeydownBind(evt) { + if (evt.which === 27 && $scope.isOpen) { + evt.preventDefault(); + evt.stopPropagation(); + $scope.$apply(function() { + $scope.isOpen = false; + }); + $element[0].focus(); + } else if (evt.which === 40 && !$scope.isOpen) { + evt.preventDefault(); + evt.stopPropagation(); + $scope.$apply(function() { + $scope.isOpen = true; + }); + } + } + + function positionPopup() { + if ($scope.isOpen) { + var dpElement = angular.element($popup[0].querySelector('.uib-datepicker-popup')); + var placement = $attrs.popupPlacement ? $attrs.popupPlacement : datepickerPopupConfig.placement; + var position = $position.positionElements($element, dpElement, placement, appendToBody); + dpElement.css({top: position.top + 'px', left: position.left + 'px'}); + if (dpElement.hasClass('uib-position-measure')) { + dpElement.removeClass('uib-position-measure'); + } + } + } + + $scope.$on('uib:datepicker.mode', function() { + $timeout(positionPopup, 0, false); + }); +}]) + +.directive('uibDatepickerPopup', function() { + return { + require: ['ngModel', 'uibDatepickerPopup'], + controller: 'UibDatepickerPopupController', + scope: { + datepickerOptions: '=?', + isOpen: '=?', + currentText: '@', + clearText: '@', + closeText: '@' + }, + link: function(scope, element, attrs, ctrls) { + var ngModel = ctrls[0], + ctrl = ctrls[1]; + + ctrl.init(ngModel); + } + }; +}) + +.directive('uibDatepickerPopupWrap', function() { + return { + restrict: 'A', + transclude: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/datepickerPopup/popup.html'; + } + }; +}); + +angular.module('ui.bootstrap.debounce', []) +/** + * A helper, internal service that debounces a function + */ + .factory('$$debounce', ['$timeout', function($timeout) { + return function(callback, debounceTime) { + var timeoutPromise; + + return function() { + var self = this; + var args = Array.prototype.slice.call(arguments); + if (timeoutPromise) { + $timeout.cancel(timeoutPromise); + } + + timeoutPromise = $timeout(function() { + callback.apply(self, args); + }, debounceTime); + }; + }; + }]); + +angular.module('ui.bootstrap.multiMap', []) +/** + * A helper, internal data structure that stores all references attached to key + */ + .factory('$$multiMap', function() { + return { + createNew: function() { + var map = {}; + + return { + entries: function() { + return Object.keys(map).map(function(key) { + return { + key: key, + value: map[key] + }; + }); + }, + get: function(key) { + return map[key]; + }, + hasKey: function(key) { + return !!map[key]; + }, + keys: function() { + return Object.keys(map); + }, + put: function(key, value) { + if (!map[key]) { + map[key] = []; + } + + map[key].push(value); + }, + remove: function(key, value) { + var values = map[key]; + + if (!values) { + return; + } + + var idx = values.indexOf(value); + + if (idx !== -1) { + values.splice(idx, 1); + } + + if (!values.length) { + delete map[key]; + } + } + }; + } + }; + }); + +angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.multiMap', 'ui.bootstrap.position']) + +.constant('uibDropdownConfig', { + appendToOpenClass: 'uib-dropdown-open', + openClass: 'open' +}) + +.service('uibDropdownService', ['$document', '$rootScope', '$$multiMap', function($document, $rootScope, $$multiMap) { + var openScope = null; + var openedContainers = $$multiMap.createNew(); + + this.isOnlyOpen = function(dropdownScope, appendTo) { + var openedDropdowns = openedContainers.get(appendTo); + if (openedDropdowns) { + var openDropdown = openedDropdowns.reduce(function(toClose, dropdown) { + if (dropdown.scope === dropdownScope) { + return dropdown; + } + + return toClose; + }, {}); + if (openDropdown) { + return openedDropdowns.length === 1; + } + } + + return false; + }; + + this.open = function(dropdownScope, element, appendTo) { + if (!openScope) { + $document.on('click', closeDropdown); + } + + if (openScope && openScope !== dropdownScope) { + openScope.isOpen = false; + } + + openScope = dropdownScope; + + if (!appendTo) { + return; + } + + var openedDropdowns = openedContainers.get(appendTo); + if (openedDropdowns) { + var openedScopes = openedDropdowns.map(function(dropdown) { + return dropdown.scope; + }); + if (openedScopes.indexOf(dropdownScope) === -1) { + openedContainers.put(appendTo, { + scope: dropdownScope + }); + } + } else { + openedContainers.put(appendTo, { + scope: dropdownScope + }); + } + }; + + this.close = function(dropdownScope, element, appendTo) { + if (openScope === dropdownScope) { + $document.off('click', closeDropdown); + $document.off('keydown', this.keybindFilter); + openScope = null; + } + + if (!appendTo) { + return; + } + + var openedDropdowns = openedContainers.get(appendTo); + if (openedDropdowns) { + var dropdownToClose = openedDropdowns.reduce(function(toClose, dropdown) { + if (dropdown.scope === dropdownScope) { + return dropdown; + } + + return toClose; + }, {}); + if (dropdownToClose) { + openedContainers.remove(appendTo, dropdownToClose); + } + } + }; + + var closeDropdown = function(evt) { + // This method may still be called during the same mouse event that + // unbound this event handler. So check openScope before proceeding. + if (!openScope) { return; } + + if (evt && openScope.getAutoClose() === 'disabled') { return; } + + if (evt && evt.which === 3) { return; } + + var toggleElement = openScope.getToggleElement(); + if (evt && toggleElement && toggleElement[0].contains(evt.target)) { + return; + } + + var dropdownElement = openScope.getDropdownElement(); + if (evt && openScope.getAutoClose() === 'outsideClick' && + dropdownElement && dropdownElement[0].contains(evt.target)) { + return; + } + + openScope.focusToggleElement(); + openScope.isOpen = false; + + if (!$rootScope.$$phase) { + openScope.$apply(); + } + }; + + this.keybindFilter = function(evt) { + if (!openScope) { + // see this.close as ESC could have been pressed which kills the scope so we can not proceed + return; + } + + var dropdownElement = openScope.getDropdownElement(); + var toggleElement = openScope.getToggleElement(); + var dropdownElementTargeted = dropdownElement && dropdownElement[0].contains(evt.target); + var toggleElementTargeted = toggleElement && toggleElement[0].contains(evt.target); + if (evt.which === 27) { + evt.stopPropagation(); + openScope.focusToggleElement(); + closeDropdown(); + } else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen && (dropdownElementTargeted || toggleElementTargeted)) { + evt.preventDefault(); + evt.stopPropagation(); + openScope.focusDropdownEntry(evt.which); + } + }; +}]) + +.controller('UibDropdownController', ['$scope', '$element', '$attrs', '$parse', 'uibDropdownConfig', 'uibDropdownService', '$animate', '$uibPosition', '$document', '$compile', '$templateRequest', function($scope, $element, $attrs, $parse, dropdownConfig, uibDropdownService, $animate, $position, $document, $compile, $templateRequest) { + var self = this, + scope = $scope.$new(), // create a child scope so we are not polluting original one + templateScope, + appendToOpenClass = dropdownConfig.appendToOpenClass, + openClass = dropdownConfig.openClass, + getIsOpen, + setIsOpen = angular.noop, + toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop, + appendToBody = false, + appendTo = null, + keynavEnabled = false, + selectedOption = null, + body = $document.find('body'); + + $element.addClass('dropdown'); + + this.init = function() { + if ($attrs.isOpen) { + getIsOpen = $parse($attrs.isOpen); + setIsOpen = getIsOpen.assign; + + $scope.$watch(getIsOpen, function(value) { + scope.isOpen = !!value; + }); + } + + if (angular.isDefined($attrs.dropdownAppendTo)) { + var appendToEl = $parse($attrs.dropdownAppendTo)(scope); + if (appendToEl) { + appendTo = angular.element(appendToEl); + } + } + + appendToBody = angular.isDefined($attrs.dropdownAppendToBody); + keynavEnabled = angular.isDefined($attrs.keyboardNav); + + if (appendToBody && !appendTo) { + appendTo = body; + } + + if (appendTo && self.dropdownMenu) { + appendTo.append(self.dropdownMenu); + $element.on('$destroy', function handleDestroyEvent() { + self.dropdownMenu.remove(); + }); + } + }; + + this.toggle = function(open) { + scope.isOpen = arguments.length ? !!open : !scope.isOpen; + if (angular.isFunction(setIsOpen)) { + setIsOpen(scope, scope.isOpen); + } + + return scope.isOpen; + }; + + // Allow other directives to watch status + this.isOpen = function() { + return scope.isOpen; + }; + + scope.getToggleElement = function() { + return self.toggleElement; + }; + + scope.getAutoClose = function() { + return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled' + }; + + scope.getElement = function() { + return $element; + }; + + scope.isKeynavEnabled = function() { + return keynavEnabled; + }; + + scope.focusDropdownEntry = function(keyCode) { + var elems = self.dropdownMenu ? //If append to body is used. + angular.element(self.dropdownMenu).find('a') : + $element.find('ul').eq(0).find('a'); + + switch (keyCode) { + case 40: { + if (!angular.isNumber(self.selectedOption)) { + self.selectedOption = 0; + } else { + self.selectedOption = self.selectedOption === elems.length - 1 ? + self.selectedOption : + self.selectedOption + 1; + } + break; + } + case 38: { + if (!angular.isNumber(self.selectedOption)) { + self.selectedOption = elems.length - 1; + } else { + self.selectedOption = self.selectedOption === 0 ? + 0 : self.selectedOption - 1; + } + break; + } + } + elems[self.selectedOption].focus(); + }; + + scope.getDropdownElement = function() { + return self.dropdownMenu; + }; + + scope.focusToggleElement = function() { + if (self.toggleElement) { + self.toggleElement[0].focus(); + } + }; + + scope.$watch('isOpen', function(isOpen, wasOpen) { + if (appendTo && self.dropdownMenu) { + var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true), + css, + rightalign, + scrollbarPadding, + scrollbarWidth = 0; + + css = { + top: pos.top + 'px', + display: isOpen ? 'block' : 'none' + }; + + rightalign = self.dropdownMenu.hasClass('dropdown-menu-right'); + if (!rightalign) { + css.left = pos.left + 'px'; + css.right = 'auto'; + } else { + css.left = 'auto'; + scrollbarPadding = $position.scrollbarPadding(appendTo); + + if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) { + scrollbarWidth = scrollbarPadding.scrollbarWidth; + } + + css.right = window.innerWidth - scrollbarWidth - + (pos.left + $element.prop('offsetWidth')) + 'px'; + } + + // Need to adjust our positioning to be relative to the appendTo container + // if it's not the body element + if (!appendToBody) { + var appendOffset = $position.offset(appendTo); + + css.top = pos.top - appendOffset.top + 'px'; + + if (!rightalign) { + css.left = pos.left - appendOffset.left + 'px'; + } else { + css.right = window.innerWidth - + (pos.left - appendOffset.left + $element.prop('offsetWidth')) + 'px'; + } + } + + self.dropdownMenu.css(css); + } + + var openContainer = appendTo ? appendTo : $element; + var dropdownOpenClass = appendTo ? appendToOpenClass : openClass; + var hasOpenClass = openContainer.hasClass(dropdownOpenClass); + var isOnlyOpen = uibDropdownService.isOnlyOpen($scope, appendTo); + + if (hasOpenClass === !isOpen) { + var toggleClass; + if (appendTo) { + toggleClass = !isOnlyOpen ? 'addClass' : 'removeClass'; + } else { + toggleClass = isOpen ? 'addClass' : 'removeClass'; + } + $animate[toggleClass](openContainer, dropdownOpenClass).then(function() { + if (angular.isDefined(isOpen) && isOpen !== wasOpen) { + toggleInvoker($scope, { open: !!isOpen }); + } + }); + } + + if (isOpen) { + if (self.dropdownMenuTemplateUrl) { + $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) { + templateScope = scope.$new(); + $compile(tplContent.trim())(templateScope, function(dropdownElement) { + var newEl = dropdownElement; + self.dropdownMenu.replaceWith(newEl); + self.dropdownMenu = newEl; + $document.on('keydown', uibDropdownService.keybindFilter); + }); + }); + } else { + $document.on('keydown', uibDropdownService.keybindFilter); + } + + scope.focusToggleElement(); + uibDropdownService.open(scope, $element, appendTo); + } else { + uibDropdownService.close(scope, $element, appendTo); + if (self.dropdownMenuTemplateUrl) { + if (templateScope) { + templateScope.$destroy(); + } + var newEl = angular.element(''); + self.dropdownMenu.replaceWith(newEl); + self.dropdownMenu = newEl; + } + + self.selectedOption = null; + } + + if (angular.isFunction(setIsOpen)) { + setIsOpen($scope, isOpen); + } + }); +}]) + +.directive('uibDropdown', function() { + return { + controller: 'UibDropdownController', + link: function(scope, element, attrs, dropdownCtrl) { + dropdownCtrl.init(); + } + }; +}) + +.directive('uibDropdownMenu', function() { + return { + restrict: 'A', + require: '?^uibDropdown', + link: function(scope, element, attrs, dropdownCtrl) { + if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) { + return; + } + + element.addClass('dropdown-menu'); + + var tplUrl = attrs.templateUrl; + if (tplUrl) { + dropdownCtrl.dropdownMenuTemplateUrl = tplUrl; + } + + if (!dropdownCtrl.dropdownMenu) { + dropdownCtrl.dropdownMenu = element; + } + } + }; +}) + +.directive('uibDropdownToggle', function() { + return { + require: '?^uibDropdown', + link: function(scope, element, attrs, dropdownCtrl) { + if (!dropdownCtrl) { + return; + } + + element.addClass('dropdown-toggle'); + + dropdownCtrl.toggleElement = element; + + var toggleDropdown = function(event) { + event.preventDefault(); + + if (!element.hasClass('disabled') && !attrs.disabled) { + scope.$apply(function() { + dropdownCtrl.toggle(); + }); + } + }; + + element.on('click', toggleDropdown); + + // WAI-ARIA + element.attr({ 'aria-haspopup': true, 'aria-expanded': false }); + scope.$watch(dropdownCtrl.isOpen, function(isOpen) { + element.attr('aria-expanded', !!isOpen); + }); + + scope.$on('$destroy', function() { + element.off('click', toggleDropdown); + }); + } + }; +}); + +angular.module('ui.bootstrap.stackedMap', []) +/** + * A helper, internal data structure that acts as a map but also allows getting / removing + * elements in the LIFO order + */ + .factory('$$stackedMap', function() { + return { + createNew: function() { + var stack = []; + + return { + add: function(key, value) { + stack.push({ + key: key, + value: value + }); + }, + get: function(key) { + for (var i = 0; i < stack.length; i++) { + if (key === stack[i].key) { + return stack[i]; + } + } + }, + keys: function() { + var keys = []; + for (var i = 0; i < stack.length; i++) { + keys.push(stack[i].key); + } + return keys; + }, + top: function() { + return stack[stack.length - 1]; + }, + remove: function(key) { + var idx = -1; + for (var i = 0; i < stack.length; i++) { + if (key === stack[i].key) { + idx = i; + break; + } + } + return stack.splice(idx, 1)[0]; + }, + removeTop: function() { + return stack.pop(); + }, + length: function() { + return stack.length; + } + }; + } + }; + }); +angular.module('ui.bootstrap.modal', ['ui.bootstrap.multiMap', 'ui.bootstrap.stackedMap', 'ui.bootstrap.position']) +/** + * Pluggable resolve mechanism for the modal resolve resolution + * Supports UI Router's $resolve service + */ + .provider('$uibResolve', function() { + var resolve = this; + this.resolver = null; + + this.setResolver = function(resolver) { + this.resolver = resolver; + }; + + this.$get = ['$injector', '$q', function($injector, $q) { + var resolver = resolve.resolver ? $injector.get(resolve.resolver) : null; + return { + resolve: function(invocables, locals, parent, self) { + if (resolver) { + return resolver.resolve(invocables, locals, parent, self); + } + + var promises = []; + + angular.forEach(invocables, function(value) { + if (angular.isFunction(value) || angular.isArray(value)) { + promises.push($q.resolve($injector.invoke(value))); + } else if (angular.isString(value)) { + promises.push($q.resolve($injector.get(value))); + } else { + promises.push($q.resolve(value)); + } + }); + + return $q.all(promises).then(function(resolves) { + var resolveObj = {}; + var resolveIter = 0; + angular.forEach(invocables, function(value, key) { + resolveObj[key] = resolves[resolveIter++]; + }); + + return resolveObj; + }); + } + }; + }]; + }) + +/** + * A helper directive for the $modal service. It creates a backdrop element. + */ + .directive('uibModalBackdrop', ['$animate', '$injector', '$uibModalStack', + function($animate, $injector, $modalStack) { + return { + restrict: 'A', + compile: function(tElement, tAttrs) { + tElement.addClass(tAttrs.backdropClass); + return linkFn; + } + }; + + function linkFn(scope, element, attrs) { + if (attrs.modalInClass) { + $animate.addClass(element, attrs.modalInClass); + + scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { + var done = setIsAsync(); + if (scope.modalOptions.animation) { + $animate.removeClass(element, attrs.modalInClass).then(done); + } else { + done(); + } + }); + } + } + }]) + + .directive('uibModalWindow', ['$uibModalStack', '$q', '$animateCss', '$document', + function($modalStack, $q, $animateCss, $document) { + return { + scope: { + index: '@' + }, + restrict: 'A', + transclude: true, + templateUrl: function(tElement, tAttrs) { + return tAttrs.templateUrl || 'uib/template/modal/window.html'; + }, + link: function(scope, element, attrs) { + element.addClass(attrs.windowTopClass || ''); + scope.size = attrs.size; + + scope.close = function(evt) { + var modal = $modalStack.getTop(); + if (modal && modal.value.backdrop && + modal.value.backdrop !== 'static' && + evt.target === evt.currentTarget) { + evt.preventDefault(); + evt.stopPropagation(); + $modalStack.dismiss(modal.key, 'backdrop click'); + } + }; + + // moved from template to fix issue #2280 + element.on('click', scope.close); + + // This property is only added to the scope for the purpose of detecting when this directive is rendered. + // We can detect that by using this property in the template associated with this directive and then use + // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}. + scope.$isRendered = true; + + // Deferred object that will be resolved when this modal is rendered. + var modalRenderDeferObj = $q.defer(); + // Resolve render promise post-digest + scope.$$postDigest(function() { + modalRenderDeferObj.resolve(); + }); + + modalRenderDeferObj.promise.then(function() { + var animationPromise = null; + + if (attrs.modalInClass) { + animationPromise = $animateCss(element, { + addClass: attrs.modalInClass + }).start(); + + scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { + var done = setIsAsync(); + $animateCss(element, { + removeClass: attrs.modalInClass + }).start().then(done); + }); + } + + + $q.when(animationPromise).then(function() { + // Notify {@link $modalStack} that modal is rendered. + var modal = $modalStack.getTop(); + if (modal) { + $modalStack.modalRendered(modal.key); + } + + /** + * If something within the freshly-opened modal already has focus (perhaps via a + * directive that causes focus) then there's no need to try to focus anything. + */ + if (!($document[0].activeElement && element[0].contains($document[0].activeElement))) { + var inputWithAutofocus = element[0].querySelector('[autofocus]'); + /** + * Auto-focusing of a freshly-opened modal element causes any child elements + * with the autofocus attribute to lose focus. This is an issue on touch + * based devices which will show and then hide the onscreen keyboard. + * Attempts to refocus the autofocus element via JavaScript will not reopen + * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus + * the modal element if the modal does not contain an autofocus element. + */ + if (inputWithAutofocus) { + inputWithAutofocus.focus(); + } else { + element[0].focus(); + } + } + }); + }); + } + }; + }]) + + .directive('uibModalAnimationClass', function() { + return { + compile: function(tElement, tAttrs) { + if (tAttrs.modalAnimation) { + tElement.addClass(tAttrs.uibModalAnimationClass); + } + } + }; + }) + + .directive('uibModalTransclude', ['$animate', function($animate) { + return { + link: function(scope, element, attrs, controller, transclude) { + transclude(scope.$parent, function(clone) { + element.empty(); + $animate.enter(clone, element); + }); + } + }; + }]) + + .factory('$uibModalStack', ['$animate', '$animateCss', '$document', + '$compile', '$rootScope', '$q', '$$multiMap', '$$stackedMap', '$uibPosition', + function($animate, $animateCss, $document, $compile, $rootScope, $q, $$multiMap, $$stackedMap, $uibPosition) { + var OPENED_MODAL_CLASS = 'modal-open'; + + var backdropDomEl, backdropScope; + var openedWindows = $$stackedMap.createNew(); + var openedClasses = $$multiMap.createNew(); + var $modalStack = { + NOW_CLOSING_EVENT: 'modal.stack.now-closing' + }; + var topModalIndex = 0; + var previousTopOpenedModal = null; + var ARIA_HIDDEN_ATTRIBUTE_NAME = 'data-bootstrap-modal-aria-hidden-count'; + + //Modal focus behavior + var tabbableSelector = 'a[href], area[href], input:not([disabled]):not([tabindex=\'-1\']), ' + + 'button:not([disabled]):not([tabindex=\'-1\']),select:not([disabled]):not([tabindex=\'-1\']), textarea:not([disabled]):not([tabindex=\'-1\']), ' + + 'iframe, object, embed, *[tabindex]:not([tabindex=\'-1\']), *[contenteditable=true]'; + var scrollbarPadding; + var SNAKE_CASE_REGEXP = /[A-Z]/g; + + // TODO: extract into common dependency with tooltip + function snake_case(name) { + var separator = '-'; + return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { + return (pos ? separator : '') + letter.toLowerCase(); + }); + } + + function isVisible(element) { + return !!(element.offsetWidth || + element.offsetHeight || + element.getClientRects().length); + } + + function backdropIndex() { + var topBackdropIndex = -1; + var opened = openedWindows.keys(); + for (var i = 0; i < opened.length; i++) { + if (openedWindows.get(opened[i]).value.backdrop) { + topBackdropIndex = i; + } + } + + // If any backdrop exist, ensure that it's index is always + // right below the top modal + if (topBackdropIndex > -1 && topBackdropIndex < topModalIndex) { + topBackdropIndex = topModalIndex; + } + return topBackdropIndex; + } + + $rootScope.$watch(backdropIndex, function(newBackdropIndex) { + if (backdropScope) { + backdropScope.index = newBackdropIndex; + } + }); + + function removeModalWindow(modalInstance, elementToReceiveFocus) { + var modalWindow = openedWindows.get(modalInstance).value; + var appendToElement = modalWindow.appendTo; + + //clean up the stack + openedWindows.remove(modalInstance); + previousTopOpenedModal = openedWindows.top(); + if (previousTopOpenedModal) { + topModalIndex = parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10); + } + + removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() { + var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS; + openedClasses.remove(modalBodyClass, modalInstance); + var areAnyOpen = openedClasses.hasKey(modalBodyClass); + appendToElement.toggleClass(modalBodyClass, areAnyOpen); + if (!areAnyOpen && scrollbarPadding && scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) { + if (scrollbarPadding.originalRight) { + appendToElement.css({paddingRight: scrollbarPadding.originalRight + 'px'}); + } else { + appendToElement.css({paddingRight: ''}); + } + scrollbarPadding = null; + } + toggleTopWindowClass(true); + }, modalWindow.closedDeferred); + checkRemoveBackdrop(); + + //move focus to specified element if available, or else to body + if (elementToReceiveFocus && elementToReceiveFocus.focus) { + elementToReceiveFocus.focus(); + } else if (appendToElement.focus) { + appendToElement.focus(); + } + } + + // Add or remove "windowTopClass" from the top window in the stack + function toggleTopWindowClass(toggleSwitch) { + var modalWindow; + + if (openedWindows.length() > 0) { + modalWindow = openedWindows.top().value; + modalWindow.modalDomEl.toggleClass(modalWindow.windowTopClass || '', toggleSwitch); + } + } + + function checkRemoveBackdrop() { + //remove backdrop if no longer needed + if (backdropDomEl && backdropIndex() === -1) { + var backdropScopeRef = backdropScope; + removeAfterAnimate(backdropDomEl, backdropScope, function() { + backdropScopeRef = null; + }); + backdropDomEl = undefined; + backdropScope = undefined; + } + } + + function removeAfterAnimate(domEl, scope, done, closedDeferred) { + var asyncDeferred; + var asyncPromise = null; + var setIsAsync = function() { + if (!asyncDeferred) { + asyncDeferred = $q.defer(); + asyncPromise = asyncDeferred.promise; + } + + return function asyncDone() { + asyncDeferred.resolve(); + }; + }; + scope.$broadcast($modalStack.NOW_CLOSING_EVENT, setIsAsync); + + // Note that it's intentional that asyncPromise might be null. + // That's when setIsAsync has not been called during the + // NOW_CLOSING_EVENT broadcast. + return $q.when(asyncPromise).then(afterAnimating); + + function afterAnimating() { + if (afterAnimating.done) { + return; + } + afterAnimating.done = true; + + $animate.leave(domEl).then(function() { + if (done) { + done(); + } + + domEl.remove(); + if (closedDeferred) { + closedDeferred.resolve(); + } + }); + + scope.$destroy(); + } + } + + $document.on('keydown', keydownListener); + + $rootScope.$on('$destroy', function() { + $document.off('keydown', keydownListener); + }); + + function keydownListener(evt) { + if (evt.isDefaultPrevented()) { + return evt; + } + + var modal = openedWindows.top(); + if (modal) { + switch (evt.which) { + case 27: { + if (modal.value.keyboard) { + evt.preventDefault(); + $rootScope.$apply(function() { + $modalStack.dismiss(modal.key, 'escape key press'); + }); + } + break; + } + case 9: { + var list = $modalStack.loadFocusElementList(modal); + var focusChanged = false; + if (evt.shiftKey) { + if ($modalStack.isFocusInFirstItem(evt, list) || $modalStack.isModalFocused(evt, modal)) { + focusChanged = $modalStack.focusLastFocusableElement(list); + } + } else { + if ($modalStack.isFocusInLastItem(evt, list)) { + focusChanged = $modalStack.focusFirstFocusableElement(list); + } + } + + if (focusChanged) { + evt.preventDefault(); + evt.stopPropagation(); + } + + break; + } + } + } + } + + $modalStack.open = function(modalInstance, modal) { + var modalOpener = $document[0].activeElement, + modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS; + + toggleTopWindowClass(false); + + // Store the current top first, to determine what index we ought to use + // for the current top modal + previousTopOpenedModal = openedWindows.top(); + + openedWindows.add(modalInstance, { + deferred: modal.deferred, + renderDeferred: modal.renderDeferred, + closedDeferred: modal.closedDeferred, + modalScope: modal.scope, + backdrop: modal.backdrop, + keyboard: modal.keyboard, + openedClass: modal.openedClass, + windowTopClass: modal.windowTopClass, + animation: modal.animation, + appendTo: modal.appendTo + }); + + openedClasses.put(modalBodyClass, modalInstance); + + var appendToElement = modal.appendTo, + currBackdropIndex = backdropIndex(); + + if (!appendToElement.length) { + throw new Error('appendTo element not found. Make sure that the element passed is in DOM.'); + } + + if (currBackdropIndex >= 0 && !backdropDomEl) { + backdropScope = $rootScope.$new(true); + backdropScope.modalOptions = modal; + backdropScope.index = currBackdropIndex; + backdropDomEl = angular.element('
'); + backdropDomEl.attr({ + 'class': 'modal-backdrop', + 'ng-style': '{\'z-index\': 1040 + (index && 1 || 0) + index*10}', + 'uib-modal-animation-class': 'fade', + 'modal-in-class': 'in' + }); + if (modal.backdropClass) { + backdropDomEl.addClass(modal.backdropClass); + } + + if (modal.animation) { + backdropDomEl.attr('modal-animation', 'true'); + } + $compile(backdropDomEl)(backdropScope); + $animate.enter(backdropDomEl, appendToElement); + if ($uibPosition.isScrollable(appendToElement)) { + scrollbarPadding = $uibPosition.scrollbarPadding(appendToElement); + if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) { + appendToElement.css({paddingRight: scrollbarPadding.right + 'px'}); + } + } + } + + var content; + if (modal.component) { + content = document.createElement(snake_case(modal.component.name)); + content = angular.element(content); + content.attr({ + resolve: '$resolve', + 'modal-instance': '$uibModalInstance', + close: '$close($value)', + dismiss: '$dismiss($value)' + }); + } else { + content = modal.content; + } + + // Set the top modal index based on the index of the previous top modal + topModalIndex = previousTopOpenedModal ? parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10) + 1 : 0; + var angularDomEl = angular.element('
'); + angularDomEl.attr({ + 'class': 'modal', + 'template-url': modal.windowTemplateUrl, + 'window-top-class': modal.windowTopClass, + 'role': 'dialog', + 'aria-labelledby': modal.ariaLabelledBy, + 'aria-describedby': modal.ariaDescribedBy, + 'size': modal.size, + 'index': topModalIndex, + 'animate': 'animate', + 'ng-style': '{\'z-index\': 1050 + $$topModalIndex*10, display: \'block\'}', + 'tabindex': -1, + 'uib-modal-animation-class': 'fade', + 'modal-in-class': 'in' + }).append(content); + if (modal.windowClass) { + angularDomEl.addClass(modal.windowClass); + } + + if (modal.animation) { + angularDomEl.attr('modal-animation', 'true'); + } + + appendToElement.addClass(modalBodyClass); + if (modal.scope) { + // we need to explicitly add the modal index to the modal scope + // because it is needed by ngStyle to compute the zIndex property. + modal.scope.$$topModalIndex = topModalIndex; + } + $animate.enter($compile(angularDomEl)(modal.scope), appendToElement); + + openedWindows.top().value.modalDomEl = angularDomEl; + openedWindows.top().value.modalOpener = modalOpener; + + applyAriaHidden(angularDomEl); + + function applyAriaHidden(el) { + if (!el || el[0].tagName === 'BODY') { + return; + } + + getSiblings(el).forEach(function(sibling) { + var elemIsAlreadyHidden = sibling.getAttribute('aria-hidden') === 'true', + ariaHiddenCount = parseInt(sibling.getAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME), 10); + + if (!ariaHiddenCount) { + ariaHiddenCount = elemIsAlreadyHidden ? 1 : 0; + } + + sibling.setAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME, ariaHiddenCount + 1); + sibling.setAttribute('aria-hidden', 'true'); + }); + + return applyAriaHidden(el.parent()); + + function getSiblings(el) { + var children = el.parent() ? el.parent().children() : []; + + return Array.prototype.filter.call(children, function(child) { + return child !== el[0]; + }); + } + } + }; + + function broadcastClosing(modalWindow, resultOrReason, closing) { + return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented; + } + + function unhideBackgroundElements() { + Array.prototype.forEach.call( + document.querySelectorAll('[' + ARIA_HIDDEN_ATTRIBUTE_NAME + ']'), + function(hiddenEl) { + var ariaHiddenCount = parseInt(hiddenEl.getAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME), 10), + newHiddenCount = ariaHiddenCount - 1; + hiddenEl.setAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME, newHiddenCount); + + if (!newHiddenCount) { + hiddenEl.removeAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME); + hiddenEl.removeAttribute('aria-hidden'); + } + } + ); + } + + $modalStack.close = function(modalInstance, result) { + var modalWindow = openedWindows.get(modalInstance); + unhideBackgroundElements(); + if (modalWindow && broadcastClosing(modalWindow, result, true)) { + modalWindow.value.modalScope.$$uibDestructionScheduled = true; + modalWindow.value.deferred.resolve(result); + removeModalWindow(modalInstance, modalWindow.value.modalOpener); + return true; + } + + return !modalWindow; + }; + + $modalStack.dismiss = function(modalInstance, reason) { + var modalWindow = openedWindows.get(modalInstance); + unhideBackgroundElements(); + if (modalWindow && broadcastClosing(modalWindow, reason, false)) { + modalWindow.value.modalScope.$$uibDestructionScheduled = true; + modalWindow.value.deferred.reject(reason); + removeModalWindow(modalInstance, modalWindow.value.modalOpener); + return true; + } + return !modalWindow; + }; + + $modalStack.dismissAll = function(reason) { + var topModal = this.getTop(); + while (topModal && this.dismiss(topModal.key, reason)) { + topModal = this.getTop(); + } + }; + + $modalStack.getTop = function() { + return openedWindows.top(); + }; + + $modalStack.modalRendered = function(modalInstance) { + var modalWindow = openedWindows.get(modalInstance); + if (modalWindow) { + modalWindow.value.renderDeferred.resolve(); + } + }; + + $modalStack.focusFirstFocusableElement = function(list) { + if (list.length > 0) { + list[0].focus(); + return true; + } + return false; + }; + + $modalStack.focusLastFocusableElement = function(list) { + if (list.length > 0) { + list[list.length - 1].focus(); + return true; + } + return false; + }; + + $modalStack.isModalFocused = function(evt, modalWindow) { + if (evt && modalWindow) { + var modalDomEl = modalWindow.value.modalDomEl; + if (modalDomEl && modalDomEl.length) { + return (evt.target || evt.srcElement) === modalDomEl[0]; + } + } + return false; + }; + + $modalStack.isFocusInFirstItem = function(evt, list) { + if (list.length > 0) { + return (evt.target || evt.srcElement) === list[0]; + } + return false; + }; + + $modalStack.isFocusInLastItem = function(evt, list) { + if (list.length > 0) { + return (evt.target || evt.srcElement) === list[list.length - 1]; + } + return false; + }; + + $modalStack.loadFocusElementList = function(modalWindow) { + if (modalWindow) { + var modalDomE1 = modalWindow.value.modalDomEl; + if (modalDomE1 && modalDomE1.length) { + var elements = modalDomE1[0].querySelectorAll(tabbableSelector); + return elements ? + Array.prototype.filter.call(elements, function(element) { + return isVisible(element); + }) : elements; + } + } + }; + + return $modalStack; + }]) + + .provider('$uibModal', function() { + var $modalProvider = { + options: { + animation: true, + backdrop: true, //can also be false or 'static' + keyboard: true + }, + $get: ['$rootScope', '$q', '$document', '$templateRequest', '$controller', '$uibResolve', '$uibModalStack', + function ($rootScope, $q, $document, $templateRequest, $controller, $uibResolve, $modalStack) { + var $modal = {}; + + function getTemplatePromise(options) { + return options.template ? $q.when(options.template) : + $templateRequest(angular.isFunction(options.templateUrl) ? + options.templateUrl() : options.templateUrl); + } + + var promiseChain = null; + $modal.getPromiseChain = function() { + return promiseChain; + }; + + $modal.open = function(modalOptions) { + var modalResultDeferred = $q.defer(); + var modalOpenedDeferred = $q.defer(); + var modalClosedDeferred = $q.defer(); + var modalRenderDeferred = $q.defer(); + + //prepare an instance of a modal to be injected into controllers and returned to a caller + var modalInstance = { + result: modalResultDeferred.promise, + opened: modalOpenedDeferred.promise, + closed: modalClosedDeferred.promise, + rendered: modalRenderDeferred.promise, + close: function (result) { + return $modalStack.close(modalInstance, result); + }, + dismiss: function (reason) { + return $modalStack.dismiss(modalInstance, reason); + } + }; + + //merge and clean up options + modalOptions = angular.extend({}, $modalProvider.options, modalOptions); + modalOptions.resolve = modalOptions.resolve || {}; + modalOptions.appendTo = modalOptions.appendTo || $document.find('body').eq(0); + + //verify options + if (!modalOptions.component && !modalOptions.template && !modalOptions.templateUrl) { + throw new Error('One of component or template or templateUrl options is required.'); + } + + var templateAndResolvePromise; + if (modalOptions.component) { + templateAndResolvePromise = $q.when($uibResolve.resolve(modalOptions.resolve, {}, null, null)); + } else { + templateAndResolvePromise = + $q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]); + } + + function resolveWithTemplate() { + return templateAndResolvePromise; + } + + // Wait for the resolution of the existing promise chain. + // Then switch to our own combined promise dependency (regardless of how the previous modal fared). + // Then add to $modalStack and resolve opened. + // Finally clean up the chain variable if no subsequent modal has overwritten it. + var samePromise; + samePromise = promiseChain = $q.all([promiseChain]) + .then(resolveWithTemplate, resolveWithTemplate) + .then(function resolveSuccess(tplAndVars) { + var providedScope = modalOptions.scope || $rootScope; + + var modalScope = providedScope.$new(); + modalScope.$close = modalInstance.close; + modalScope.$dismiss = modalInstance.dismiss; + + modalScope.$on('$destroy', function() { + if (!modalScope.$$uibDestructionScheduled) { + modalScope.$dismiss('$uibUnscheduledDestruction'); + } + }); + + var modal = { + scope: modalScope, + deferred: modalResultDeferred, + renderDeferred: modalRenderDeferred, + closedDeferred: modalClosedDeferred, + animation: modalOptions.animation, + backdrop: modalOptions.backdrop, + keyboard: modalOptions.keyboard, + backdropClass: modalOptions.backdropClass, + windowTopClass: modalOptions.windowTopClass, + windowClass: modalOptions.windowClass, + windowTemplateUrl: modalOptions.windowTemplateUrl, + ariaLabelledBy: modalOptions.ariaLabelledBy, + ariaDescribedBy: modalOptions.ariaDescribedBy, + size: modalOptions.size, + openedClass: modalOptions.openedClass, + appendTo: modalOptions.appendTo + }; + + var component = {}; + var ctrlInstance, ctrlInstantiate, ctrlLocals = {}; + + if (modalOptions.component) { + constructLocals(component, false, true, false); + component.name = modalOptions.component; + modal.component = component; + } else if (modalOptions.controller) { + constructLocals(ctrlLocals, true, false, true); + + // the third param will make the controller instantiate later,private api + // @see https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L126 + ctrlInstantiate = $controller(modalOptions.controller, ctrlLocals, true, modalOptions.controllerAs); + if (modalOptions.controllerAs && modalOptions.bindToController) { + ctrlInstance = ctrlInstantiate.instance; + ctrlInstance.$close = modalScope.$close; + ctrlInstance.$dismiss = modalScope.$dismiss; + angular.extend(ctrlInstance, { + $resolve: ctrlLocals.$scope.$resolve + }, providedScope); + } + + ctrlInstance = ctrlInstantiate(); + + if (angular.isFunction(ctrlInstance.$onInit)) { + ctrlInstance.$onInit(); + } + } + + if (!modalOptions.component) { + modal.content = tplAndVars[0]; + } + + $modalStack.open(modalInstance, modal); + modalOpenedDeferred.resolve(true); + + function constructLocals(obj, template, instanceOnScope, injectable) { + obj.$scope = modalScope; + obj.$scope.$resolve = {}; + if (instanceOnScope) { + obj.$scope.$uibModalInstance = modalInstance; + } else { + obj.$uibModalInstance = modalInstance; + } + + var resolves = template ? tplAndVars[1] : tplAndVars; + angular.forEach(resolves, function(value, key) { + if (injectable) { + obj[key] = value; + } + + obj.$scope.$resolve[key] = value; + }); + } + }, function resolveError(reason) { + modalOpenedDeferred.reject(reason); + modalResultDeferred.reject(reason); + })['finally'](function() { + if (promiseChain === samePromise) { + promiseChain = null; + } + }); + + return modalInstance; + }; + + return $modal; + } + ] + }; + + return $modalProvider; + }); + +angular.module('ui.bootstrap.paging', []) +/** + * Helper internal service for generating common controller code between the + * pager and pagination components + */ +.factory('uibPaging', ['$parse', function($parse) { + return { + create: function(ctrl, $scope, $attrs) { + ctrl.setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop; + ctrl.ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl + ctrl._watchers = []; + + ctrl.init = function(ngModelCtrl, config) { + ctrl.ngModelCtrl = ngModelCtrl; + ctrl.config = config; + + ngModelCtrl.$render = function() { + ctrl.render(); + }; + + if ($attrs.itemsPerPage) { + ctrl._watchers.push($scope.$parent.$watch($attrs.itemsPerPage, function(value) { + ctrl.itemsPerPage = parseInt(value, 10); + $scope.totalPages = ctrl.calculateTotalPages(); + ctrl.updatePage(); + })); + } else { + ctrl.itemsPerPage = config.itemsPerPage; + } + + $scope.$watch('totalItems', function(newTotal, oldTotal) { + if (angular.isDefined(newTotal) || newTotal !== oldTotal) { + $scope.totalPages = ctrl.calculateTotalPages(); + ctrl.updatePage(); + } + }); + }; + + ctrl.calculateTotalPages = function() { + var totalPages = ctrl.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / ctrl.itemsPerPage); + return Math.max(totalPages || 0, 1); + }; + + ctrl.render = function() { + $scope.page = parseInt(ctrl.ngModelCtrl.$viewValue, 10) || 1; + }; + + $scope.selectPage = function(page, evt) { + if (evt) { + evt.preventDefault(); + } + + var clickAllowed = !$scope.ngDisabled || !evt; + if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) { + if (evt && evt.target) { + evt.target.blur(); + } + ctrl.ngModelCtrl.$setViewValue(page); + ctrl.ngModelCtrl.$render(); + } + }; + + $scope.getText = function(key) { + return $scope[key + 'Text'] || ctrl.config[key + 'Text']; + }; + + $scope.noPrevious = function() { + return $scope.page === 1; + }; + + $scope.noNext = function() { + return $scope.page === $scope.totalPages; + }; + + ctrl.updatePage = function() { + ctrl.setNumPages($scope.$parent, $scope.totalPages); // Readonly variable + + if ($scope.page > $scope.totalPages) { + $scope.selectPage($scope.totalPages); + } else { + ctrl.ngModelCtrl.$render(); + } + }; + + $scope.$on('$destroy', function() { + while (ctrl._watchers.length) { + ctrl._watchers.shift()(); + } + }); + } + }; +}]); + +angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging', 'ui.bootstrap.tabindex']) + +.controller('UibPagerController', ['$scope', '$attrs', 'uibPaging', 'uibPagerConfig', function($scope, $attrs, uibPaging, uibPagerConfig) { + $scope.align = angular.isDefined($attrs.align) ? $scope.$parent.$eval($attrs.align) : uibPagerConfig.align; + + uibPaging.create(this, $scope, $attrs); +}]) + +.constant('uibPagerConfig', { + itemsPerPage: 10, + previousText: '« Previous', + nextText: 'Next »', + align: true +}) + +.directive('uibPager', ['uibPagerConfig', function(uibPagerConfig) { + return { + scope: { + totalItems: '=', + previousText: '@', + nextText: '@', + ngDisabled: '=' + }, + require: ['uibPager', '?ngModel'], + restrict: 'A', + controller: 'UibPagerController', + controllerAs: 'pager', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/pager/pager.html'; + }, + link: function(scope, element, attrs, ctrls) { + element.addClass('pager'); + var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (!ngModelCtrl) { + return; // do nothing if no ng-model + } + + paginationCtrl.init(ngModelCtrl, uibPagerConfig); + } + }; +}]); + +angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging', 'ui.bootstrap.tabindex']) +.controller('UibPaginationController', ['$scope', '$attrs', '$parse', 'uibPaging', 'uibPaginationConfig', function($scope, $attrs, $parse, uibPaging, uibPaginationConfig) { + var ctrl = this; + // Setup configuration parameters + var maxSize = angular.isDefined($attrs.maxSize) ? $scope.$parent.$eval($attrs.maxSize) : uibPaginationConfig.maxSize, + rotate = angular.isDefined($attrs.rotate) ? $scope.$parent.$eval($attrs.rotate) : uibPaginationConfig.rotate, + forceEllipses = angular.isDefined($attrs.forceEllipses) ? $scope.$parent.$eval($attrs.forceEllipses) : uibPaginationConfig.forceEllipses, + boundaryLinkNumbers = angular.isDefined($attrs.boundaryLinkNumbers) ? $scope.$parent.$eval($attrs.boundaryLinkNumbers) : uibPaginationConfig.boundaryLinkNumbers, + pageLabel = angular.isDefined($attrs.pageLabel) ? function(idx) { return $scope.$parent.$eval($attrs.pageLabel, {$page: idx}); } : angular.identity; + $scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : uibPaginationConfig.boundaryLinks; + $scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : uibPaginationConfig.directionLinks; + + uibPaging.create(this, $scope, $attrs); + + if ($attrs.maxSize) { + ctrl._watchers.push($scope.$parent.$watch($parse($attrs.maxSize), function(value) { + maxSize = parseInt(value, 10); + ctrl.render(); + })); + } + + // Create page object used in template + function makePage(number, text, isActive) { + return { + number: number, + text: text, + active: isActive + }; + } + + function getPages(currentPage, totalPages) { + var pages = []; + + // Default page limits + var startPage = 1, endPage = totalPages; + var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages; + + // recompute if maxSize + if (isMaxSized) { + if (rotate) { + // Current page is displayed in the middle of the visible ones + startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1); + endPage = startPage + maxSize - 1; + + // Adjust if limit is exceeded + if (endPage > totalPages) { + endPage = totalPages; + startPage = endPage - maxSize + 1; + } + } else { + // Visible pages are paginated with maxSize + startPage = (Math.ceil(currentPage / maxSize) - 1) * maxSize + 1; + + // Adjust last page if limit is exceeded + endPage = Math.min(startPage + maxSize - 1, totalPages); + } + } + + // Add page number links + for (var number = startPage; number <= endPage; number++) { + var page = makePage(number, pageLabel(number), number === currentPage); + pages.push(page); + } + + // Add links to move between page sets + if (isMaxSized && maxSize > 0 && (!rotate || forceEllipses || boundaryLinkNumbers)) { + if (startPage > 1) { + if (!boundaryLinkNumbers || startPage > 3) { //need ellipsis for all options unless range is too close to beginning + var previousPageSet = makePage(startPage - 1, '...', false); + pages.unshift(previousPageSet); + } + if (boundaryLinkNumbers) { + if (startPage === 3) { //need to replace ellipsis when the buttons would be sequential + var secondPageLink = makePage(2, '2', false); + pages.unshift(secondPageLink); + } + //add the first page + var firstPageLink = makePage(1, '1', false); + pages.unshift(firstPageLink); + } + } + + if (endPage < totalPages) { + if (!boundaryLinkNumbers || endPage < totalPages - 2) { //need ellipsis for all options unless range is too close to end + var nextPageSet = makePage(endPage + 1, '...', false); + pages.push(nextPageSet); + } + if (boundaryLinkNumbers) { + if (endPage === totalPages - 2) { //need to replace ellipsis when the buttons would be sequential + var secondToLastPageLink = makePage(totalPages - 1, totalPages - 1, false); + pages.push(secondToLastPageLink); + } + //add the last page + var lastPageLink = makePage(totalPages, totalPages, false); + pages.push(lastPageLink); + } + } + } + return pages; + } + + var originalRender = this.render; + this.render = function() { + originalRender(); + if ($scope.page > 0 && $scope.page <= $scope.totalPages) { + $scope.pages = getPages($scope.page, $scope.totalPages); + } + }; +}]) + +.constant('uibPaginationConfig', { + itemsPerPage: 10, + boundaryLinks: false, + boundaryLinkNumbers: false, + directionLinks: true, + firstText: 'First', + previousText: 'Previous', + nextText: 'Next', + lastText: 'Last', + rotate: true, + forceEllipses: false +}) + +.directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, uibPaginationConfig) { + return { + scope: { + totalItems: '=', + firstText: '@', + previousText: '@', + nextText: '@', + lastText: '@', + ngDisabled:'=' + }, + require: ['uibPagination', '?ngModel'], + restrict: 'A', + controller: 'UibPaginationController', + controllerAs: 'pagination', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/pagination/pagination.html'; + }, + link: function(scope, element, attrs, ctrls) { + element.addClass('pagination'); + var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (!ngModelCtrl) { + return; // do nothing if no ng-model + } + + paginationCtrl.init(ngModelCtrl, uibPaginationConfig); + } + }; +}]); + +/** + * 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', 'ui.bootstrap.stackedMap']) + +/** + * The $tooltip service creates tooltip- and popover-like directives as well as + * houses global options for them. + */ +.provider('$uibTooltip', function() { + // The default options tooltip and popover. + var defaultOptions = { + placement: 'top', + placementClassPrefix: '', + animation: true, + popupDelay: 0, + popupCloseDelay: 0, + useContentExp: false + }; + + // Default hide triggers for each show trigger + var triggerMap = { + 'mouseenter': 'mouseleave', + 'click': 'click', + 'outsideClick': 'outsideClick', + 'focus': 'blur', + 'none': '' + }; + + // 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', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) { + var openedTooltips = $$stackedMap.createNew(); + $document.on('keyup', keypressListener); + + $rootScope.$on('$destroy', function() { + $document.off('keyup', keypressListener); + }); + + function keypressListener(e) { + if (e.which === 27) { + var last = openedTooltips.top(); + if (last) { + last.value.close(); + last = null; + } + } + } + + return function $tooltip(ttType, prefix, defaultTriggerShow, options) { + options = angular.extend({}, defaultOptions, globalOptions, options); + + /** + * 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 getTriggers(trigger) { + var show = (trigger || options.trigger || defaultTriggerShow).split(' '); + var hide = show.map(function(trigger) { + return triggerMap[trigger] || trigger; + }); + return { + show: show, + hide: hide + }; + } + + var directiveName = snake_case(ttType); + + var startSym = $interpolate.startSymbol(); + var endSym = $interpolate.endSymbol(); + var template = + '
' + + '
'; + + return { + compile: function(tElem, tAttrs) { + var tooltipLinker = $compile(template); + + return function link(scope, element, attrs, tooltipCtrl) { + var tooltip; + var tooltipLinkedScope; + var transitionTimeout; + var showTimeout; + var hideTimeout; + var positionTimeout; + var adjustmentTimeout; + var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false; + var triggers = getTriggers(undefined); + var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']); + var ttScope = scope.$new(true); + var repositionScheduled = false; + var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false; + var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false; + var observers = []; + var lastPlacement; + + var positionTooltip = function() { + // check if tooltip exists and is not empty + if (!tooltip || !tooltip.html()) { return; } + + if (!positionTimeout) { + positionTimeout = $timeout(function() { + var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody); + var initialHeight = angular.isDefined(tooltip.offsetHeight) ? tooltip.offsetHeight : tooltip.prop('offsetHeight'); + var elementPos = appendToBody ? $position.offset(element) : $position.position(element); + tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px' }); + var placementClasses = ttPosition.placement.split('-'); + + if (!tooltip.hasClass(placementClasses[0])) { + tooltip.removeClass(lastPlacement.split('-')[0]); + tooltip.addClass(placementClasses[0]); + } + + if (!tooltip.hasClass(options.placementClassPrefix + ttPosition.placement)) { + tooltip.removeClass(options.placementClassPrefix + lastPlacement); + tooltip.addClass(options.placementClassPrefix + ttPosition.placement); + } + + adjustmentTimeout = $timeout(function() { + var currentHeight = angular.isDefined(tooltip.offsetHeight) ? tooltip.offsetHeight : tooltip.prop('offsetHeight'); + var adjustment = $position.adjustTop(placementClasses, elementPos, initialHeight, currentHeight); + if (adjustment) { + tooltip.css(adjustment); + } + adjustmentTimeout = null; + }, 0, false); + + // first time through tt element will have the + // uib-position-measure class or if the placement + // has changed we need to position the arrow. + if (tooltip.hasClass('uib-position-measure')) { + $position.positionArrow(tooltip, ttPosition.placement); + tooltip.removeClass('uib-position-measure'); + } else if (lastPlacement !== ttPosition.placement) { + $position.positionArrow(tooltip, ttPosition.placement); + } + lastPlacement = ttPosition.placement; + + positionTimeout = null; + }, 0, false); + } + }; + + // Set up the correct scope to allow transclusion later + ttScope.origScope = scope; + + // By default, the tooltip is not open. + // TODO add ability to start tooltip opened + ttScope.isOpen = false; + + function toggleTooltipBind() { + if (!ttScope.isOpen) { + showTooltipBind(); + } else { + hideTooltipBind(); + } + } + + // Show the tooltip with delay if specified, otherwise show it immediately + function showTooltipBind() { + if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) { + return; + } + + cancelHide(); + prepareTooltip(); + + if (ttScope.popupDelay) { + // Do nothing if the tooltip was already scheduled to pop-up. + // This happens if show is triggered multiple times before any hide is triggered. + if (!showTimeout) { + showTimeout = $timeout(show, ttScope.popupDelay, false); + } + } else { + show(); + } + } + + function hideTooltipBind() { + cancelShow(); + + if (ttScope.popupCloseDelay) { + if (!hideTimeout) { + hideTimeout = $timeout(hide, ttScope.popupCloseDelay, false); + } + } else { + hide(); + } + } + + // Show the tooltip popup element. + function show() { + cancelShow(); + cancelHide(); + + // Don't show empty tooltips. + if (!ttScope.content) { + return angular.noop; + } + + createTooltip(); + + // And show the tooltip. + ttScope.$evalAsync(function() { + ttScope.isOpen = true; + assignIsOpen(true); + positionTooltip(); + }); + } + + function cancelShow() { + if (showTimeout) { + $timeout.cancel(showTimeout); + showTimeout = null; + } + + if (positionTimeout) { + $timeout.cancel(positionTimeout); + positionTimeout = null; + } + } + + // Hide the tooltip popup element. + function hide() { + if (!ttScope) { + return; + } + + // First things first: we don't show it anymore. + ttScope.$evalAsync(function() { + if (ttScope) { + ttScope.isOpen = false; + assignIsOpen(false); + // 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. + // The fade transition in TWBS is 150ms. + if (ttScope.animation) { + if (!transitionTimeout) { + transitionTimeout = $timeout(removeTooltip, 150, false); + } + } else { + removeTooltip(); + } + } + }); + } + + function cancelHide() { + if (hideTimeout) { + $timeout.cancel(hideTimeout); + hideTimeout = null; + } + + if (transitionTimeout) { + $timeout.cancel(transitionTimeout); + transitionTimeout = null; + } + } + + function createTooltip() { + // There can only be one tooltip element per directive shown at once. + if (tooltip) { + return; + } + + tooltipLinkedScope = ttScope.$new(); + tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) { + if (appendToBody) { + $document.find('body').append(tooltip); + } else { + element.after(tooltip); + } + }); + + openedTooltips.add(ttScope, { + close: hide + }); + + prepObservers(); + } + + function removeTooltip() { + cancelShow(); + cancelHide(); + unregisterObservers(); + + if (tooltip) { + tooltip.remove(); + + tooltip = null; + if (adjustmentTimeout) { + $timeout.cancel(adjustmentTimeout); + } + } + + openedTooltips.remove(ttScope); + + if (tooltipLinkedScope) { + tooltipLinkedScope.$destroy(); + tooltipLinkedScope = null; + } + } + + /** + * Set the initial scope values. Once + * the tooltip is created, the observers + * will be added to keep things in sync. + */ + function prepareTooltip() { + ttScope.title = attrs[prefix + 'Title']; + if (contentParse) { + ttScope.content = contentParse(scope); + } else { + ttScope.content = attrs[ttType]; + } + + ttScope.popupClass = attrs[prefix + 'Class']; + ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement; + var placement = $position.parsePlacement(ttScope.placement); + lastPlacement = placement[1] ? placement[0] + '-' + placement[1] : placement[0]; + + var delay = parseInt(attrs[prefix + 'PopupDelay'], 10); + var closeDelay = parseInt(attrs[prefix + 'PopupCloseDelay'], 10); + ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay; + ttScope.popupCloseDelay = !isNaN(closeDelay) ? closeDelay : options.popupCloseDelay; + } + + function assignIsOpen(isOpen) { + if (isOpenParse && angular.isFunction(isOpenParse.assign)) { + isOpenParse.assign(scope, isOpen); + } + } + + ttScope.contentExp = function() { + return ttScope.content; + }; + + /** + * Observe the relevant attributes. + */ + attrs.$observe('disabled', function(val) { + if (val) { + cancelShow(); + } + + if (val && ttScope.isOpen) { + hide(); + } + }); + + if (isOpenParse) { + scope.$watch(isOpenParse, function(val) { + if (ttScope && !val === ttScope.isOpen) { + toggleTooltipBind(); + } + }); + } + + function prepObservers() { + observers.length = 0; + + if (contentParse) { + observers.push( + scope.$watch(contentParse, function(val) { + ttScope.content = val; + if (!val && ttScope.isOpen) { + hide(); + } + }) + ); + + observers.push( + tooltipLinkedScope.$watch(function() { + if (!repositionScheduled) { + repositionScheduled = true; + tooltipLinkedScope.$$postDigest(function() { + repositionScheduled = false; + if (ttScope && ttScope.isOpen) { + positionTooltip(); + } + }); + } + }) + ); + } else { + observers.push( + attrs.$observe(ttType, function(val) { + ttScope.content = val; + if (!val && ttScope.isOpen) { + hide(); + } else { + positionTooltip(); + } + }) + ); + } + + observers.push( + attrs.$observe(prefix + 'Title', function(val) { + ttScope.title = val; + if (ttScope.isOpen) { + positionTooltip(); + } + }) + ); + + observers.push( + attrs.$observe(prefix + 'Placement', function(val) { + ttScope.placement = val ? val : options.placement; + if (ttScope.isOpen) { + positionTooltip(); + } + }) + ); + } + + function unregisterObservers() { + if (observers.length) { + angular.forEach(observers, function(observer) { + observer(); + }); + observers.length = 0; + } + } + + // hide tooltips/popovers for outsideClick trigger + function bodyHideTooltipBind(e) { + if (!ttScope || !ttScope.isOpen || !tooltip) { + return; + } + // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked + if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) { + hideTooltipBind(); + } + } + + var unregisterTriggers = function() { + triggers.show.forEach(function(trigger) { + if (trigger === 'outsideClick') { + element.off('click', toggleTooltipBind); + } else { + element.off(trigger, showTooltipBind); + element.off(trigger, toggleTooltipBind); + } + }); + triggers.hide.forEach(function(trigger) { + if (trigger === 'outsideClick') { + $document.off('click', bodyHideTooltipBind); + } else { + element.off(trigger, hideTooltipBind); + } + }); + }; + + function prepTriggers() { + var showTriggers = [], hideTriggers = []; + var val = scope.$eval(attrs[prefix + 'Trigger']); + unregisterTriggers(); + + if (angular.isObject(val)) { + Object.keys(val).forEach(function(key) { + showTriggers.push(key); + hideTriggers.push(val[key]); + }); + triggers = { + show: showTriggers, + hide: hideTriggers + }; + } else { + triggers = getTriggers(val); + } + + if (triggers.show !== 'none') { + triggers.show.forEach(function(trigger, idx) { + if (trigger === 'outsideClick') { + element.on('click', toggleTooltipBind); + $document.on('click', bodyHideTooltipBind); + } else if (trigger === triggers.hide[idx]) { + element.on(trigger, toggleTooltipBind); + } else if (trigger) { + element.on(trigger, showTooltipBind); + element.on(triggers.hide[idx], hideTooltipBind); + } + + element.on('keypress', function(e) { + if (e.which === 27) { + hideTooltipBind(); + } + }); + }); + } + } + + prepTriggers(); + + var animation = scope.$eval(attrs[prefix + 'Animation']); + ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation; + + var appendToBodyVal; + var appendKey = prefix + 'AppendToBody'; + if (appendKey in attrs && attrs[appendKey] === undefined) { + appendToBodyVal = true; + } else { + appendToBodyVal = scope.$eval(attrs[appendKey]); + } + + appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody; + + // Make sure tooltip is destroyed and removed. + scope.$on('$destroy', function onDestroyTooltip() { + unregisterTriggers(); + removeTooltip(); + ttScope = null; + }); + }; + } + }; + }; + }]; +}) + +// This is mostly ngInclude code but with a custom scope +.directive('uibTooltipTemplateTransclude', [ + '$animate', '$sce', '$compile', '$templateRequest', +function ($animate, $sce, $compile, $templateRequest) { + return { + link: function(scope, elem, attrs) { + var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope); + + var changeCounter = 0, + currentScope, + previousElement, + currentElement; + + var cleanupLastIncludeContent = function() { + if (previousElement) { + previousElement.remove(); + previousElement = null; + } + + if (currentScope) { + currentScope.$destroy(); + currentScope = null; + } + + if (currentElement) { + $animate.leave(currentElement).then(function() { + previousElement = null; + }); + previousElement = currentElement; + currentElement = null; + } + }; + + scope.$watch($sce.parseAsResourceUrl(attrs.uibTooltipTemplateTransclude), function(src) { + var thisChangeId = ++changeCounter; + + if (src) { + //set the 2nd param to true to ignore the template request error so that the inner + //contents and scope can be cleaned up. + $templateRequest(src, true).then(function(response) { + if (thisChangeId !== changeCounter) { return; } + var newScope = origScope.$new(); + var template = response; + + var clone = $compile(template)(newScope, function(clone) { + cleanupLastIncludeContent(); + $animate.enter(clone, elem); + }); + + currentScope = newScope; + currentElement = clone; + + currentScope.$emit('$includeContentLoaded', src); + }, function() { + if (thisChangeId === changeCounter) { + cleanupLastIncludeContent(); + scope.$emit('$includeContentError', src); + } + }); + scope.$emit('$includeContentRequested', src); + } else { + cleanupLastIncludeContent(); + } + }); + + scope.$on('$destroy', cleanupLastIncludeContent); + } + }; +}]) + +/** + * Note that it's intentional that these classes are *not* applied through $animate. + * They must not be animated as they're expected to be present on the tooltip on + * initialization. + */ +.directive('uibTooltipClasses', ['$uibPosition', function($uibPosition) { + return { + restrict: 'A', + link: function(scope, element, attrs) { + // need to set the primary position so the + // arrow has space during position measure. + // tooltip.positionTooltip() + if (scope.placement) { + // // There are no top-left etc... classes + // // in TWBS, so we need the primary position. + var position = $uibPosition.parsePlacement(scope.placement); + element.addClass(position[0]); + } + + if (scope.popupClass) { + element.addClass(scope.popupClass); + } + + if (scope.animation) { + element.addClass(attrs.tooltipAnimationClass); + } + } + }; +}]) + +.directive('uibTooltipPopup', function() { + return { + restrict: 'A', + scope: { content: '@' }, + templateUrl: 'uib/template/tooltip/tooltip-popup.html' + }; +}) + +.directive('uibTooltip', [ '$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibTooltip', 'tooltip', 'mouseenter'); +}]) + +.directive('uibTooltipTemplatePopup', function() { + return { + restrict: 'A', + scope: { contentExp: '&', originScope: '&' }, + templateUrl: 'uib/template/tooltip/tooltip-template-popup.html' + }; +}) + +.directive('uibTooltipTemplate', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibTooltipTemplate', 'tooltip', 'mouseenter', { + useContentExp: true + }); +}]) + +.directive('uibTooltipHtmlPopup', function() { + return { + restrict: 'A', + scope: { contentExp: '&' }, + templateUrl: 'uib/template/tooltip/tooltip-html-popup.html' + }; +}) + +.directive('uibTooltipHtml', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibTooltipHtml', 'tooltip', 'mouseenter', { + useContentExp: true + }); +}]); + +/** + * 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, and selector delegatation. + */ +angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip']) + +.directive('uibPopoverTemplatePopup', function() { + return { + restrict: 'A', + scope: { uibTitle: '@', contentExp: '&', originScope: '&' }, + templateUrl: 'uib/template/popover/popover-template.html' + }; +}) + +.directive('uibPopoverTemplate', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibPopoverTemplate', 'popover', 'click', { + useContentExp: true + }); +}]) + +.directive('uibPopoverHtmlPopup', function() { + return { + restrict: 'A', + scope: { contentExp: '&', uibTitle: '@' }, + templateUrl: 'uib/template/popover/popover-html.html' + }; +}) + +.directive('uibPopoverHtml', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibPopoverHtml', 'popover', 'click', { + useContentExp: true + }); +}]) + +.directive('uibPopoverPopup', function() { + return { + restrict: 'A', + scope: { uibTitle: '@', content: '@' }, + templateUrl: 'uib/template/popover/popover.html' + }; +}) + +.directive('uibPopover', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibPopover', 'popover', 'click'); +}]); + +angular.module('ui.bootstrap.progressbar', []) + +.constant('uibProgressConfig', { + animate: true, + max: 100 +}) + +.controller('UibProgressController', ['$scope', '$attrs', 'uibProgressConfig', function($scope, $attrs, progressConfig) { + var self = this, + animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate; + + this.bars = []; + $scope.max = getMaxOrDefault(); + + this.addBar = function(bar, element, attrs) { + if (!animate) { + element.css({'transition': 'none'}); + } + + this.bars.push(bar); + + bar.max = getMaxOrDefault(); + bar.title = attrs && angular.isDefined(attrs.title) ? attrs.title : 'progressbar'; + + bar.$watch('value', function(value) { + bar.recalculatePercentage(); + }); + + bar.recalculatePercentage = function() { + var totalPercentage = self.bars.reduce(function(total, bar) { + bar.percent = +(100 * bar.value / bar.max).toFixed(2); + return total + bar.percent; + }, 0); + + if (totalPercentage > 100) { + bar.percent -= totalPercentage - 100; + } + }; + + bar.$on('$destroy', function() { + element = null; + self.removeBar(bar); + }); + }; + + this.removeBar = function(bar) { + this.bars.splice(this.bars.indexOf(bar), 1); + this.bars.forEach(function (bar) { + bar.recalculatePercentage(); + }); + }; + + //$attrs.$observe('maxParam', function(maxParam) { + $scope.$watch('maxParam', function(maxParam) { + self.bars.forEach(function(bar) { + bar.max = getMaxOrDefault(); + bar.recalculatePercentage(); + }); + }); + + function getMaxOrDefault () { + return angular.isDefined($scope.maxParam) ? $scope.maxParam : progressConfig.max; + } +}]) + +.directive('uibProgress', function() { + return { + replace: true, + transclude: true, + controller: 'UibProgressController', + require: 'uibProgress', + scope: { + maxParam: '=?max' + }, + templateUrl: 'uib/template/progressbar/progress.html' + }; +}) + +.directive('uibBar', function() { + return { + replace: true, + transclude: true, + require: '^uibProgress', + scope: { + value: '=', + type: '@' + }, + templateUrl: 'uib/template/progressbar/bar.html', + link: function(scope, element, attrs, progressCtrl) { + progressCtrl.addBar(scope, element, attrs); + } + }; +}) + +.directive('uibProgressbar', function() { + return { + replace: true, + transclude: true, + controller: 'UibProgressController', + scope: { + value: '=', + maxParam: '=?max', + type: '@' + }, + templateUrl: 'uib/template/progressbar/progressbar.html', + link: function(scope, element, attrs, progressCtrl) { + progressCtrl.addBar(scope, angular.element(element.children()[0]), {title: attrs.title}); + } + }; +}); + +angular.module('ui.bootstrap.rating', []) + +.constant('uibRatingConfig', { + max: 5, + stateOn: null, + stateOff: null, + enableReset: true, + titles: ['one', 'two', 'three', 'four', 'five'] +}) + +.controller('UibRatingController', ['$scope', '$attrs', 'uibRatingConfig', function($scope, $attrs, ratingConfig) { + var ngModelCtrl = { $setViewValue: angular.noop }, + self = this; + + this.init = function(ngModelCtrl_) { + ngModelCtrl = ngModelCtrl_; + ngModelCtrl.$render = this.render; + + ngModelCtrl.$formatters.push(function(value) { + if (angular.isNumber(value) && value << 0 !== value) { + value = Math.round(value); + } + + return value; + }); + + this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn; + this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff; + this.enableReset = angular.isDefined($attrs.enableReset) ? + $scope.$parent.$eval($attrs.enableReset) : ratingConfig.enableReset; + var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles; + this.titles = angular.isArray(tmpTitles) && tmpTitles.length > 0 ? + tmpTitles : ratingConfig.titles; + + var ratingStates = angular.isDefined($attrs.ratingStates) ? + $scope.$parent.$eval($attrs.ratingStates) : + new Array(angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max); + $scope.range = this.buildTemplateObjects(ratingStates); + }; + + this.buildTemplateObjects = function(states) { + for (var i = 0, n = states.length; i < n; i++) { + states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff, title: this.getTitle(i) }, states[i]); + } + return states; + }; + + this.getTitle = function(index) { + if (index >= this.titles.length) { + return index + 1; + } + + return this.titles[index]; + }; + + $scope.rate = function(value) { + if (!$scope.readonly && value >= 0 && value <= $scope.range.length) { + var newViewValue = self.enableReset && ngModelCtrl.$viewValue === value ? 0 : value; + ngModelCtrl.$setViewValue(newViewValue); + ngModelCtrl.$render(); + } + }; + + $scope.enter = function(value) { + if (!$scope.readonly) { + $scope.value = value; + } + $scope.onHover({value: value}); + }; + + $scope.reset = function() { + $scope.value = ngModelCtrl.$viewValue; + $scope.onLeave(); + }; + + $scope.onKeydown = function(evt) { + if (/(37|38|39|40)/.test(evt.which)) { + evt.preventDefault(); + evt.stopPropagation(); + $scope.rate($scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1)); + } + }; + + this.render = function() { + $scope.value = ngModelCtrl.$viewValue; + $scope.title = self.getTitle($scope.value - 1); + }; +}]) + +.directive('uibRating', function() { + return { + require: ['uibRating', 'ngModel'], + restrict: 'A', + scope: { + readonly: '=?readOnly', + onHover: '&', + onLeave: '&' + }, + controller: 'UibRatingController', + templateUrl: 'uib/template/rating/rating.html', + link: function(scope, element, attrs, ctrls) { + var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + ratingCtrl.init(ngModelCtrl); + } + }; +}); + +angular.module('ui.bootstrap.tabs', []) + +.controller('UibTabsetController', ['$scope', function ($scope) { + var ctrl = this, + oldIndex; + ctrl.tabs = []; + + ctrl.select = function(index, evt) { + if (!destroyed) { + var previousIndex = findTabIndex(oldIndex); + var previousSelected = ctrl.tabs[previousIndex]; + if (previousSelected) { + previousSelected.tab.onDeselect({ + $event: evt, + $selectedIndex: index + }); + if (evt && evt.isDefaultPrevented()) { + return; + } + previousSelected.tab.active = false; + } + + var selected = ctrl.tabs[index]; + if (selected) { + selected.tab.onSelect({ + $event: evt + }); + selected.tab.active = true; + ctrl.active = selected.index; + oldIndex = selected.index; + } else if (!selected && angular.isDefined(oldIndex)) { + ctrl.active = null; + oldIndex = null; + } + } + }; + + ctrl.addTab = function addTab(tab) { + ctrl.tabs.push({ + tab: tab, + index: tab.index + }); + ctrl.tabs.sort(function(t1, t2) { + if (t1.index > t2.index) { + return 1; + } + + if (t1.index < t2.index) { + return -1; + } + + return 0; + }); + + if (tab.index === ctrl.active || !angular.isDefined(ctrl.active) && ctrl.tabs.length === 1) { + var newActiveIndex = findTabIndex(tab.index); + ctrl.select(newActiveIndex); + } + }; + + ctrl.removeTab = function removeTab(tab) { + var index; + for (var i = 0; i < ctrl.tabs.length; i++) { + if (ctrl.tabs[i].tab === tab) { + index = i; + break; + } + } + + if (ctrl.tabs[index].index === ctrl.active) { + var newActiveTabIndex = index === ctrl.tabs.length - 1 ? + index - 1 : index + 1 % ctrl.tabs.length; + ctrl.select(newActiveTabIndex); + } + + ctrl.tabs.splice(index, 1); + }; + + $scope.$watch('tabset.active', function(val) { + if (angular.isDefined(val) && val !== oldIndex) { + ctrl.select(findTabIndex(val)); + } + }); + + var destroyed; + $scope.$on('$destroy', function() { + destroyed = true; + }); + + function findTabIndex(index) { + for (var i = 0; i < ctrl.tabs.length; i++) { + if (ctrl.tabs[i].index === index) { + return i; + } + } + } +}]) + +.directive('uibTabset', function() { + return { + transclude: true, + replace: true, + scope: {}, + bindToController: { + active: '=?', + type: '@' + }, + controller: 'UibTabsetController', + controllerAs: 'tabset', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/tabs/tabset.html'; + }, + link: function(scope, element, attrs) { + scope.vertical = angular.isDefined(attrs.vertical) ? + scope.$parent.$eval(attrs.vertical) : false; + scope.justified = angular.isDefined(attrs.justified) ? + scope.$parent.$eval(attrs.justified) : false; + } + }; +}) + +.directive('uibTab', ['$parse', function($parse) { + return { + require: '^uibTabset', + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/tabs/tab.html'; + }, + transclude: true, + scope: { + heading: '@', + index: '=?', + classes: '@?', + onSelect: '&select', //This callback is called in contentHeadingTransclude + //once it inserts the tab's content into the dom + onDeselect: '&deselect' + }, + controller: function() { + //Empty controller so other directives can require being 'under' a tab + }, + controllerAs: 'tab', + link: function(scope, elm, attrs, tabsetCtrl, transclude) { + scope.disabled = false; + if (attrs.disable) { + scope.$parent.$watch($parse(attrs.disable), function(value) { + scope.disabled = !! value; + }); + } + + if (angular.isUndefined(attrs.index)) { + if (tabsetCtrl.tabs && tabsetCtrl.tabs.length) { + scope.index = Math.max.apply(null, tabsetCtrl.tabs.map(function(t) { return t.index; })) + 1; + } else { + scope.index = 0; + } + } + + if (angular.isUndefined(attrs.classes)) { + scope.classes = ''; + } + + scope.select = function(evt) { + if (!scope.disabled) { + var index; + for (var i = 0; i < tabsetCtrl.tabs.length; i++) { + if (tabsetCtrl.tabs[i].tab === scope) { + index = i; + break; + } + } + + tabsetCtrl.select(index, evt); + } + }; + + tabsetCtrl.addTab(scope); + scope.$on('$destroy', function() { + tabsetCtrl.removeTab(scope); + }); + + //We need to transclude later, once the content container is ready. + //when this link happens, we're inside a tab heading. + scope.$transcludeFn = transclude; + } + }; +}]) + +.directive('uibTabHeadingTransclude', function() { + return { + restrict: 'A', + require: '^uibTab', + link: function(scope, elm) { + scope.$watch('headingElement', function updateHeadingElement(heading) { + if (heading) { + elm.html(''); + elm.append(heading); + } + }); + } + }; +}) + +.directive('uibTabContentTransclude', function() { + return { + restrict: 'A', + require: '^uibTabset', + link: function(scope, elm, attrs) { + var tab = scope.$eval(attrs.uibTabContentTransclude).tab; + + //Now our tab is ready to be transcluded: both the tab heading area + //and the tab content area are loaded. Transclude 'em both. + tab.$transcludeFn(tab.$parent, function(contents) { + angular.forEach(contents, function(node) { + if (isTabHeading(node)) { + //Let tabHeadingTransclude know. + tab.headingElement = node; + } else { + elm.append(node); + } + }); + }); + } + }; + + function isTabHeading(node) { + return node.tagName && ( + node.hasAttribute('uib-tab-heading') || + node.hasAttribute('data-uib-tab-heading') || + node.hasAttribute('x-uib-tab-heading') || + node.tagName.toLowerCase() === 'uib-tab-heading' || + node.tagName.toLowerCase() === 'data-uib-tab-heading' || + node.tagName.toLowerCase() === 'x-uib-tab-heading' || + node.tagName.toLowerCase() === 'uib:tab-heading' + ); + } +}); + +angular.module('ui.bootstrap.timepicker', []) + +.constant('uibTimepickerConfig', { + hourStep: 1, + minuteStep: 1, + secondStep: 1, + showMeridian: true, + showSeconds: false, + meridians: null, + readonlyInput: false, + mousewheel: true, + arrowkeys: true, + showSpinners: true, + templateUrl: 'uib/template/timepicker/timepicker.html' +}) + +.controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) { + var hoursModelCtrl, minutesModelCtrl, secondsModelCtrl; + var selected = new Date(), + watchers = [], + ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl + meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS, + padHours = angular.isDefined($attrs.padHours) ? $scope.$parent.$eval($attrs.padHours) : true; + + $scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0; + $element.removeAttr('tabindex'); + + this.init = function(ngModelCtrl_, inputs) { + ngModelCtrl = ngModelCtrl_; + ngModelCtrl.$render = this.render; + + ngModelCtrl.$formatters.unshift(function(modelValue) { + return modelValue ? new Date(modelValue) : null; + }); + + var hoursInputEl = inputs.eq(0), + minutesInputEl = inputs.eq(1), + secondsInputEl = inputs.eq(2); + + hoursModelCtrl = hoursInputEl.controller('ngModel'); + minutesModelCtrl = minutesInputEl.controller('ngModel'); + secondsModelCtrl = secondsInputEl.controller('ngModel'); + + var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel; + + if (mousewheel) { + this.setupMousewheelEvents(hoursInputEl, minutesInputEl, secondsInputEl); + } + + var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys; + if (arrowkeys) { + this.setupArrowkeyEvents(hoursInputEl, minutesInputEl, secondsInputEl); + } + + $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput; + this.setupInputEvents(hoursInputEl, minutesInputEl, secondsInputEl); + }; + + var hourStep = timepickerConfig.hourStep; + if ($attrs.hourStep) { + watchers.push($scope.$parent.$watch($parse($attrs.hourStep), function(value) { + hourStep = +value; + })); + } + + var minuteStep = timepickerConfig.minuteStep; + if ($attrs.minuteStep) { + watchers.push($scope.$parent.$watch($parse($attrs.minuteStep), function(value) { + minuteStep = +value; + })); + } + + var min; + watchers.push($scope.$parent.$watch($parse($attrs.min), function(value) { + var dt = new Date(value); + min = isNaN(dt) ? undefined : dt; + })); + + var max; + watchers.push($scope.$parent.$watch($parse($attrs.max), function(value) { + var dt = new Date(value); + max = isNaN(dt) ? undefined : dt; + })); + + var disabled = false; + if ($attrs.ngDisabled) { + watchers.push($scope.$parent.$watch($parse($attrs.ngDisabled), function(value) { + disabled = value; + })); + } + + $scope.noIncrementHours = function() { + var incrementedSelected = addMinutes(selected, hourStep * 60); + return disabled || incrementedSelected > max || + incrementedSelected < selected && incrementedSelected < min; + }; + + $scope.noDecrementHours = function() { + var decrementedSelected = addMinutes(selected, -hourStep * 60); + return disabled || decrementedSelected < min || + decrementedSelected > selected && decrementedSelected > max; + }; + + $scope.noIncrementMinutes = function() { + var incrementedSelected = addMinutes(selected, minuteStep); + return disabled || incrementedSelected > max || + incrementedSelected < selected && incrementedSelected < min; + }; + + $scope.noDecrementMinutes = function() { + var decrementedSelected = addMinutes(selected, -minuteStep); + return disabled || decrementedSelected < min || + decrementedSelected > selected && decrementedSelected > max; + }; + + $scope.noIncrementSeconds = function() { + var incrementedSelected = addSeconds(selected, secondStep); + return disabled || incrementedSelected > max || + incrementedSelected < selected && incrementedSelected < min; + }; + + $scope.noDecrementSeconds = function() { + var decrementedSelected = addSeconds(selected, -secondStep); + return disabled || decrementedSelected < min || + decrementedSelected > selected && decrementedSelected > max; + }; + + $scope.noToggleMeridian = function() { + if (selected.getHours() < 12) { + return disabled || addMinutes(selected, 12 * 60) > max; + } + + return disabled || addMinutes(selected, -12 * 60) < min; + }; + + var secondStep = timepickerConfig.secondStep; + if ($attrs.secondStep) { + watchers.push($scope.$parent.$watch($parse($attrs.secondStep), function(value) { + secondStep = +value; + })); + } + + $scope.showSeconds = timepickerConfig.showSeconds; + if ($attrs.showSeconds) { + watchers.push($scope.$parent.$watch($parse($attrs.showSeconds), function(value) { + $scope.showSeconds = !!value; + })); + } + + // 12H / 24H mode + $scope.showMeridian = timepickerConfig.showMeridian; + if ($attrs.showMeridian) { + watchers.push($scope.$parent.$watch($parse($attrs.showMeridian), function(value) { + $scope.showMeridian = !!value; + + if (ngModelCtrl.$error.time) { + // Evaluate from template + var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate(); + if (angular.isDefined(hours) && angular.isDefined(minutes)) { + selected.setHours(hours); + refresh(); + } + } else { + updateTemplate(); + } + })); + } + + // Get $scope.hours in 24H mode if valid + function getHoursFromTemplate() { + var hours = +$scope.hours; + var valid = $scope.showMeridian ? hours > 0 && hours < 13 : + hours >= 0 && hours < 24; + if (!valid || $scope.hours === '') { + return undefined; + } + + if ($scope.showMeridian) { + if (hours === 12) { + hours = 0; + } + if ($scope.meridian === meridians[1]) { + hours = hours + 12; + } + } + return hours; + } + + function getMinutesFromTemplate() { + var minutes = +$scope.minutes; + var valid = minutes >= 0 && minutes < 60; + if (!valid || $scope.minutes === '') { + return undefined; + } + return minutes; + } + + function getSecondsFromTemplate() { + var seconds = +$scope.seconds; + return seconds >= 0 && seconds < 60 ? seconds : undefined; + } + + function pad(value, noPad) { + if (value === null) { + return ''; + } + + return angular.isDefined(value) && value.toString().length < 2 && !noPad ? + '0' + value : value.toString(); + } + + // Respond on mousewheel spin + this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { + var isScrollingUp = function(e) { + if (e.originalEvent) { + e = e.originalEvent; + } + //pick correct delta variable depending on event + var delta = e.wheelDelta ? e.wheelDelta : -e.deltaY; + return e.detail || delta > 0; + }; + + hoursInputEl.on('mousewheel wheel', function(e) { + if (!disabled) { + $scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours()); + } + e.preventDefault(); + }); + + minutesInputEl.on('mousewheel wheel', function(e) { + if (!disabled) { + $scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes()); + } + e.preventDefault(); + }); + + secondsInputEl.on('mousewheel wheel', function(e) { + if (!disabled) { + $scope.$apply(isScrollingUp(e) ? $scope.incrementSeconds() : $scope.decrementSeconds()); + } + e.preventDefault(); + }); + }; + + // Respond on up/down arrowkeys + this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { + hoursInputEl.on('keydown', function(e) { + if (!disabled) { + if (e.which === 38) { // up + e.preventDefault(); + $scope.incrementHours(); + $scope.$apply(); + } else if (e.which === 40) { // down + e.preventDefault(); + $scope.decrementHours(); + $scope.$apply(); + } + } + }); + + minutesInputEl.on('keydown', function(e) { + if (!disabled) { + if (e.which === 38) { // up + e.preventDefault(); + $scope.incrementMinutes(); + $scope.$apply(); + } else if (e.which === 40) { // down + e.preventDefault(); + $scope.decrementMinutes(); + $scope.$apply(); + } + } + }); + + secondsInputEl.on('keydown', function(e) { + if (!disabled) { + if (e.which === 38) { // up + e.preventDefault(); + $scope.incrementSeconds(); + $scope.$apply(); + } else if (e.which === 40) { // down + e.preventDefault(); + $scope.decrementSeconds(); + $scope.$apply(); + } + } + }); + }; + + this.setupInputEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { + if ($scope.readonlyInput) { + $scope.updateHours = angular.noop; + $scope.updateMinutes = angular.noop; + $scope.updateSeconds = angular.noop; + return; + } + + var invalidate = function(invalidHours, invalidMinutes, invalidSeconds) { + ngModelCtrl.$setViewValue(null); + ngModelCtrl.$setValidity('time', false); + if (angular.isDefined(invalidHours)) { + $scope.invalidHours = invalidHours; + if (hoursModelCtrl) { + hoursModelCtrl.$setValidity('hours', false); + } + } + + if (angular.isDefined(invalidMinutes)) { + $scope.invalidMinutes = invalidMinutes; + if (minutesModelCtrl) { + minutesModelCtrl.$setValidity('minutes', false); + } + } + + if (angular.isDefined(invalidSeconds)) { + $scope.invalidSeconds = invalidSeconds; + if (secondsModelCtrl) { + secondsModelCtrl.$setValidity('seconds', false); + } + } + }; + + $scope.updateHours = function() { + var hours = getHoursFromTemplate(), + minutes = getMinutesFromTemplate(); + + ngModelCtrl.$setDirty(); + + if (angular.isDefined(hours) && angular.isDefined(minutes)) { + selected.setHours(hours); + selected.setMinutes(minutes); + if (selected < min || selected > max) { + invalidate(true); + } else { + refresh('h'); + } + } else { + invalidate(true); + } + }; + + hoursInputEl.on('blur', function(e) { + ngModelCtrl.$setTouched(); + if (modelIsEmpty()) { + makeValid(); + } else if ($scope.hours === null || $scope.hours === '') { + invalidate(true); + } else if (!$scope.invalidHours && $scope.hours < 10) { + $scope.$apply(function() { + $scope.hours = pad($scope.hours, !padHours); + }); + } + }); + + $scope.updateMinutes = function() { + var minutes = getMinutesFromTemplate(), + hours = getHoursFromTemplate(); + + ngModelCtrl.$setDirty(); + + if (angular.isDefined(minutes) && angular.isDefined(hours)) { + selected.setHours(hours); + selected.setMinutes(minutes); + if (selected < min || selected > max) { + invalidate(undefined, true); + } else { + refresh('m'); + } + } else { + invalidate(undefined, true); + } + }; + + minutesInputEl.on('blur', function(e) { + ngModelCtrl.$setTouched(); + if (modelIsEmpty()) { + makeValid(); + } else if ($scope.minutes === null) { + invalidate(undefined, true); + } else if (!$scope.invalidMinutes && $scope.minutes < 10) { + $scope.$apply(function() { + $scope.minutes = pad($scope.minutes); + }); + } + }); + + $scope.updateSeconds = function() { + var seconds = getSecondsFromTemplate(); + + ngModelCtrl.$setDirty(); + + if (angular.isDefined(seconds)) { + selected.setSeconds(seconds); + refresh('s'); + } else { + invalidate(undefined, undefined, true); + } + }; + + secondsInputEl.on('blur', function(e) { + if (modelIsEmpty()) { + makeValid(); + } else if (!$scope.invalidSeconds && $scope.seconds < 10) { + $scope.$apply( function() { + $scope.seconds = pad($scope.seconds); + }); + } + }); + + }; + + this.render = function() { + var date = ngModelCtrl.$viewValue; + + if (isNaN(date)) { + ngModelCtrl.$setValidity('time', false); + $log.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.'); + } else { + if (date) { + selected = date; + } + + if (selected < min || selected > max) { + ngModelCtrl.$setValidity('time', false); + $scope.invalidHours = true; + $scope.invalidMinutes = true; + } else { + makeValid(); + } + updateTemplate(); + } + }; + + // Call internally when we know that model is valid. + function refresh(keyboardChange) { + makeValid(); + ngModelCtrl.$setViewValue(new Date(selected)); + updateTemplate(keyboardChange); + } + + function makeValid() { + if (hoursModelCtrl) { + hoursModelCtrl.$setValidity('hours', true); + } + + if (minutesModelCtrl) { + minutesModelCtrl.$setValidity('minutes', true); + } + + if (secondsModelCtrl) { + secondsModelCtrl.$setValidity('seconds', true); + } + + ngModelCtrl.$setValidity('time', true); + $scope.invalidHours = false; + $scope.invalidMinutes = false; + $scope.invalidSeconds = false; + } + + function updateTemplate(keyboardChange) { + if (!ngModelCtrl.$modelValue) { + $scope.hours = null; + $scope.minutes = null; + $scope.seconds = null; + $scope.meridian = meridians[0]; + } else { + var hours = selected.getHours(), + minutes = selected.getMinutes(), + seconds = selected.getSeconds(); + + if ($scope.showMeridian) { + hours = hours === 0 || hours === 12 ? 12 : hours % 12; // Convert 24 to 12 hour system + } + + $scope.hours = keyboardChange === 'h' ? hours : pad(hours, !padHours); + if (keyboardChange !== 'm') { + $scope.minutes = pad(minutes); + } + $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1]; + + if (keyboardChange !== 's') { + $scope.seconds = pad(seconds); + } + $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1]; + } + } + + function addSecondsToSelected(seconds) { + selected = addSeconds(selected, seconds); + refresh(); + } + + function addMinutes(selected, minutes) { + return addSeconds(selected, minutes*60); + } + + function addSeconds(date, seconds) { + var dt = new Date(date.getTime() + seconds * 1000); + var newDate = new Date(date); + newDate.setHours(dt.getHours(), dt.getMinutes(), dt.getSeconds()); + return newDate; + } + + function modelIsEmpty() { + return ($scope.hours === null || $scope.hours === '') && + ($scope.minutes === null || $scope.minutes === '') && + (!$scope.showSeconds || $scope.showSeconds && ($scope.seconds === null || $scope.seconds === '')); + } + + $scope.showSpinners = angular.isDefined($attrs.showSpinners) ? + $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners; + + $scope.incrementHours = function() { + if (!$scope.noIncrementHours()) { + addSecondsToSelected(hourStep * 60 * 60); + } + }; + + $scope.decrementHours = function() { + if (!$scope.noDecrementHours()) { + addSecondsToSelected(-hourStep * 60 * 60); + } + }; + + $scope.incrementMinutes = function() { + if (!$scope.noIncrementMinutes()) { + addSecondsToSelected(minuteStep * 60); + } + }; + + $scope.decrementMinutes = function() { + if (!$scope.noDecrementMinutes()) { + addSecondsToSelected(-minuteStep * 60); + } + }; + + $scope.incrementSeconds = function() { + if (!$scope.noIncrementSeconds()) { + addSecondsToSelected(secondStep); + } + }; + + $scope.decrementSeconds = function() { + if (!$scope.noDecrementSeconds()) { + addSecondsToSelected(-secondStep); + } + }; + + $scope.toggleMeridian = function() { + var minutes = getMinutesFromTemplate(), + hours = getHoursFromTemplate(); + + if (!$scope.noToggleMeridian()) { + if (angular.isDefined(minutes) && angular.isDefined(hours)) { + addSecondsToSelected(12 * 60 * (selected.getHours() < 12 ? 60 : -60)); + } else { + $scope.meridian = $scope.meridian === meridians[0] ? meridians[1] : meridians[0]; + } + } + }; + + $scope.blur = function() { + ngModelCtrl.$setTouched(); + }; + + $scope.$on('$destroy', function() { + while (watchers.length) { + watchers.shift()(); + } + }); +}]) + +.directive('uibTimepicker', ['uibTimepickerConfig', function(uibTimepickerConfig) { + return { + require: ['uibTimepicker', '?^ngModel'], + restrict: 'A', + controller: 'UibTimepickerController', + controllerAs: 'timepicker', + scope: {}, + templateUrl: function(element, attrs) { + return attrs.templateUrl || uibTimepickerConfig.templateUrl; + }, + link: function(scope, element, attrs, ctrls) { + var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (ngModelCtrl) { + timepickerCtrl.init(ngModelCtrl, element.find('input')); + } + } + }; +}]); + +angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', '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('uibTypeaheadParser', ['$parse', function($parse) { + // 000001111111100000000000002222222200000000000000003333333333333330000000000044444444000 + var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/; + return { + parse: function(input) { + var match = input.match(TYPEAHEAD_REGEXP); + 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]) + }; + } + }; + }]) + + .controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$$debounce', '$uibPosition', 'uibTypeaheadParser', + function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $$debounce, $position, typeaheadParser) { + var HOT_KEYS = [9, 13, 27, 38, 40]; + var eventDebounceTime = 200; + var modelCtrl, ngModelOptions; + //SUPPORTED ATTRIBUTES (OPTIONS) + + //minimal no of characters that needs to be entered before typeahead kicks-in + var minLength = originalScope.$eval(attrs.typeaheadMinLength); + if (!minLength && minLength !== 0) { + minLength = 1; + } + + originalScope.$watch(attrs.typeaheadMinLength, function (newVal) { + minLength = !newVal && newVal !== 0 ? 1 : newVal; + }); + + //minimal wait time after last character typed before typeahead kicks-in + var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0; + + //should it restrict model values to the ones selected from the popup only? + var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false; + originalScope.$watch(attrs.typeaheadEditable, function (newVal) { + isEditable = newVal !== false; + }); + + //binding to a variable that indicates if matches are being retrieved asynchronously + var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop; + + //a function to determine if an event should cause selection + var isSelectEvent = attrs.typeaheadShouldSelect ? $parse(attrs.typeaheadShouldSelect) : function(scope, vals) { + var evt = vals.$event; + return evt.which === 13 || evt.which === 9; + }; + + //a callback executed when a match is selected + var onSelectCallback = $parse(attrs.typeaheadOnSelect); + + //should it select highlighted popup value when losing focus? + var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false; + + //binding to a variable that indicates if there were no results after the query is completed + var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop; + + var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined; + + var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false; + + var appendTo = attrs.typeaheadAppendTo ? + originalScope.$eval(attrs.typeaheadAppendTo) : null; + + var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false; + + //If input matches an item of the list exactly, select it automatically + var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false; + + //binding to a variable that indicates if dropdown is open + var isOpenSetter = $parse(attrs.typeaheadIsOpen).assign || angular.noop; + + var showHint = originalScope.$eval(attrs.typeaheadShowHint) || false; + + //INTERNAL VARIABLES + + //model setter executed upon match selection + var parsedModel = $parse(attrs.ngModel); + var invokeModelSetter = $parse(attrs.ngModel + '($$$p)'); + var $setModelValue = function(scope, newValue) { + if (angular.isFunction(parsedModel(originalScope)) && + ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) { + return invokeModelSetter(scope, {$$$p: newValue}); + } + + return parsedModel.assign(scope, newValue); + }; + + //expressions used by typeahead + var parserResult = typeaheadParser.parse(attrs.uibTypeahead); + + var hasFocus; + + //Used to avoid bug in iOS webview where iOS keyboard does not fire + //mousedown & mouseup events + //Issue #3699 + var selected; + + //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(); + var offDestroy = originalScope.$on('$destroy', function() { + scope.$destroy(); + }); + scope.$on('$destroy', offDestroy); + + // WAI-ARIA + var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000); + element.attr({ + 'aria-autocomplete': 'list', + 'aria-expanded': false, + 'aria-owns': popupId + }); + + var inputsContainer, hintInputElem; + //add read-only input to show hint + if (showHint) { + inputsContainer = angular.element('
'); + inputsContainer.css('position', 'relative'); + element.after(inputsContainer); + hintInputElem = element.clone(); + hintInputElem.attr('placeholder', ''); + hintInputElem.attr('tabindex', '-1'); + hintInputElem.val(''); + hintInputElem.css({ + 'position': 'absolute', + 'top': '0px', + 'left': '0px', + 'border-color': 'transparent', + 'box-shadow': 'none', + 'opacity': 1, + 'background': 'none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)', + 'color': '#999' + }); + element.css({ + 'position': 'relative', + 'vertical-align': 'top', + 'background-color': 'transparent' + }); + + if (hintInputElem.attr('id')) { + hintInputElem.removeAttr('id'); // remove duplicate id if present. + } + inputsContainer.append(hintInputElem); + hintInputElem.after(element); + } + + //pop-up element used to display matches + var popUpEl = angular.element('
'); + popUpEl.attr({ + id: popupId, + matches: 'matches', + active: 'activeIdx', + select: 'select(activeIdx, evt)', + 'move-in-progress': 'moveInProgress', + query: 'query', + position: 'position', + 'assign-is-open': 'assignIsOpen(isOpen)', + debounce: 'debounceUpdate' + }); + //custom item template + if (angular.isDefined(attrs.typeaheadTemplateUrl)) { + popUpEl.attr('template-url', attrs.typeaheadTemplateUrl); + } + + if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) { + popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl); + } + + var resetHint = function() { + if (showHint) { + hintInputElem.val(''); + } + }; + + var resetMatches = function() { + scope.matches = []; + scope.activeIdx = -1; + element.attr('aria-expanded', false); + resetHint(); + }; + + var getMatchId = function(index) { + return popupId + '-option-' + index; + }; + + // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead. + // This attribute is added or removed automatically when the `activeIdx` changes. + scope.$watch('activeIdx', function(index) { + if (index < 0) { + element.removeAttr('aria-activedescendant'); + } else { + element.attr('aria-activedescendant', getMatchId(index)); + } + }); + + var inputIsExactMatch = function(inputValue, index) { + if (scope.matches.length > index && inputValue) { + return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase(); + } + + return false; + }; + + var getMatchesAsync = function(inputValue, evt) { + var locals = {$viewValue: inputValue}; + isLoadingSetter(originalScope, true); + isNoResultsSetter(originalScope, false); + $q.when(parserResult.source(originalScope, 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 + var onCurrentRequest = inputValue === modelCtrl.$viewValue; + if (onCurrentRequest && hasFocus) { + if (matches && matches.length > 0) { + scope.activeIdx = focusFirst ? 0 : -1; + isNoResultsSetter(originalScope, false); + scope.matches.length = 0; + + //transform labels + for (var i = 0; i < matches.length; i++) { + locals[parserResult.itemName] = matches[i]; + scope.matches.push({ + id: getMatchId(i), + label: parserResult.viewMapper(scope, locals), + model: matches[i] + }); + } + + scope.query = inputValue; + //position pop-up with matches - we need to re-calculate its position each time we are opening a window + //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page + //due to other elements being rendered + recalculatePosition(); + + element.attr('aria-expanded', true); + + //Select the single remaining option if user input matches + if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) { + if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) { + $$debounce(function() { + scope.select(0, evt); + }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']); + } else { + scope.select(0, evt); + } + } + + if (showHint) { + var firstLabel = scope.matches[0].label; + if (angular.isString(inputValue) && + inputValue.length > 0 && + firstLabel.slice(0, inputValue.length).toUpperCase() === inputValue.toUpperCase()) { + hintInputElem.val(inputValue + firstLabel.slice(inputValue.length)); + } else { + hintInputElem.val(''); + } + } + } else { + resetMatches(); + isNoResultsSetter(originalScope, true); + } + } + if (onCurrentRequest) { + isLoadingSetter(originalScope, false); + } + }, function() { + resetMatches(); + isLoadingSetter(originalScope, false); + isNoResultsSetter(originalScope, true); + }); + }; + + // bind events only if appendToBody params exist - performance feature + if (appendToBody) { + angular.element($window).on('resize', fireRecalculating); + $document.find('body').on('scroll', fireRecalculating); + } + + // Declare the debounced function outside recalculating for + // proper debouncing + var debouncedRecalculate = $$debounce(function() { + // if popup is visible + if (scope.matches.length) { + recalculatePosition(); + } + + scope.moveInProgress = false; + }, eventDebounceTime); + + // Default progress type + scope.moveInProgress = false; + + function fireRecalculating() { + if (!scope.moveInProgress) { + scope.moveInProgress = true; + scope.$digest(); + } + + debouncedRecalculate(); + } + + // recalculate actual position and set new values to scope + // after digest loop is popup in right position + function recalculatePosition() { + scope.position = appendToBody ? $position.offset(element) : $position.position(element); + scope.position.top += element.prop('offsetHeight'); + } + + //we need to propagate user's query so we can higlight matches + scope.query = undefined; + + //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later + var timeoutPromise; + + var scheduleSearchWithTimeout = function(inputValue) { + timeoutPromise = $timeout(function() { + getMatchesAsync(inputValue); + }, waitTime); + }; + + var cancelPreviousTimeout = function() { + if (timeoutPromise) { + $timeout.cancel(timeoutPromise); + } + }; + + resetMatches(); + + scope.assignIsOpen = function (isOpen) { + isOpenSetter(originalScope, isOpen); + }; + + scope.select = function(activeIdx, evt) { + //called from within the $digest() cycle + var locals = {}; + var model, item; + + selected = true; + locals[parserResult.itemName] = item = scope.matches[activeIdx].model; + model = parserResult.modelMapper(originalScope, locals); + $setModelValue(originalScope, model); + modelCtrl.$setValidity('editable', true); + modelCtrl.$setValidity('parse', true); + + onSelectCallback(originalScope, { + $item: item, + $model: model, + $label: parserResult.viewMapper(originalScope, locals), + $event: evt + }); + + resetMatches(); + + //return focus to the input element if a match was selected via a mouse click event + // use timeout to avoid $rootScope:inprog error + if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) { + $timeout(function() { element[0].focus(); }, 0, false); + } + }; + + //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27) + element.on('keydown', function(evt) { + //typeahead is open and an "interesting" key was pressed + if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) { + return; + } + + var shouldSelect = isSelectEvent(originalScope, {$event: evt}); + + /** + * if there's nothing selected (i.e. focusFirst) and enter or tab is hit + * or + * shift + tab is pressed to bring focus to the previous element + * then clear the results + */ + if (scope.activeIdx === -1 && shouldSelect || evt.which === 9 && !!evt.shiftKey) { + resetMatches(); + scope.$digest(); + return; + } + + evt.preventDefault(); + var target; + switch (evt.which) { + case 27: // escape + evt.stopPropagation(); + + resetMatches(); + originalScope.$digest(); + break; + case 38: // up arrow + scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1; + scope.$digest(); + target = popUpEl[0].querySelectorAll('.uib-typeahead-match')[scope.activeIdx]; + target.parentNode.scrollTop = target.offsetTop; + break; + case 40: // down arrow + scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length; + scope.$digest(); + target = popUpEl[0].querySelectorAll('.uib-typeahead-match')[scope.activeIdx]; + target.parentNode.scrollTop = target.offsetTop; + break; + default: + if (shouldSelect) { + scope.$apply(function() { + if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) { + $$debounce(function() { + scope.select(scope.activeIdx, evt); + }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']); + } else { + scope.select(scope.activeIdx, evt); + } + }); + } + } + }); + + element.on('focus', function (evt) { + hasFocus = true; + if (minLength === 0 && !modelCtrl.$viewValue) { + $timeout(function() { + getMatchesAsync(modelCtrl.$viewValue, evt); + }, 0); + } + }); + + element.on('blur', function(evt) { + if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) { + selected = true; + scope.$apply(function() { + if (angular.isObject(scope.debounceUpdate) && angular.isNumber(scope.debounceUpdate.blur)) { + $$debounce(function() { + scope.select(scope.activeIdx, evt); + }, scope.debounceUpdate.blur); + } else { + scope.select(scope.activeIdx, evt); + } + }); + } + if (!isEditable && modelCtrl.$error.editable) { + modelCtrl.$setViewValue(); + scope.$apply(function() { + // Reset validity as we are clearing + modelCtrl.$setValidity('editable', true); + modelCtrl.$setValidity('parse', true); + }); + element.val(''); + } + hasFocus = false; + selected = false; + }); + + // Keep reference to click handler to unbind it. + var dismissClickHandler = function(evt) { + // Issue #3973 + // Firefox treats right click as a click on document + if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) { + resetMatches(); + if (!$rootScope.$$phase) { + originalScope.$digest(); + } + } + }; + + $document.on('click', dismissClickHandler); + + originalScope.$on('$destroy', function() { + $document.off('click', dismissClickHandler); + if (appendToBody || appendTo) { + $popup.remove(); + } + + if (appendToBody) { + angular.element($window).off('resize', fireRecalculating); + $document.find('body').off('scroll', fireRecalculating); + } + // Prevent jQuery cache memory leak + popUpEl.remove(); + + if (showHint) { + inputsContainer.remove(); + } + }); + + var $popup = $compile(popUpEl)(scope); + + if (appendToBody) { + $document.find('body').append($popup); + } else if (appendTo) { + angular.element(appendTo).eq(0).append($popup); + } else { + element.after($popup); + } + + this.init = function(_modelCtrl, _ngModelOptions) { + modelCtrl = _modelCtrl; + ngModelOptions = _ngModelOptions; + + scope.debounceUpdate = modelCtrl.$options && $parse(modelCtrl.$options.debounce)(originalScope); + + //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM + //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue + modelCtrl.$parsers.unshift(function(inputValue) { + hasFocus = true; + + if (minLength === 0 || inputValue && inputValue.length >= minLength) { + if (waitTime > 0) { + cancelPreviousTimeout(); + scheduleSearchWithTimeout(inputValue); + } else { + getMatchesAsync(inputValue); + } + } else { + isLoadingSetter(originalScope, false); + cancelPreviousTimeout(); + resetMatches(); + } + + if (isEditable) { + return inputValue; + } + + if (!inputValue) { + // Reset in case user had typed something previously. + modelCtrl.$setValidity('editable', true); + return null; + } + + modelCtrl.$setValidity('editable', false); + return undefined; + }); + + modelCtrl.$formatters.push(function(modelValue) { + var candidateViewValue, emptyViewValue; + var locals = {}; + + // The validity may be set to false via $parsers (see above) if + // the model is restricted to selected values. If the model + // is set manually it is considered to be valid. + if (!isEditable) { + modelCtrl.$setValidity('editable', true); + } + + if (inputFormatter) { + locals.$model = modelValue; + return inputFormatter(originalScope, locals); + } + + //it might happen that we don't have enough info to properly render input value + //we need to check for this situation and simply return model value if we can't apply custom formatting + locals[parserResult.itemName] = modelValue; + candidateViewValue = parserResult.viewMapper(originalScope, locals); + locals[parserResult.itemName] = undefined; + emptyViewValue = parserResult.viewMapper(originalScope, locals); + + return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue; + }); + }; + }]) + + .directive('uibTypeahead', function() { + return { + controller: 'UibTypeaheadController', + require: ['ngModel', '^?ngModelOptions', 'uibTypeahead'], + link: function(originalScope, element, attrs, ctrls) { + ctrls[2].init(ctrls[0], ctrls[1]); + } + }; + }) + + .directive('uibTypeaheadPopup', ['$$debounce', function($$debounce) { + return { + scope: { + matches: '=', + query: '=', + active: '=', + position: '&', + moveInProgress: '=', + select: '&', + assignIsOpen: '&', + debounce: '&' + }, + replace: true, + templateUrl: function(element, attrs) { + return attrs.popupTemplateUrl || 'uib/template/typeahead/typeahead-popup.html'; + }, + link: function(scope, element, attrs) { + scope.templateUrl = attrs.templateUrl; + + scope.isOpen = function() { + var isDropdownOpen = scope.matches.length > 0; + scope.assignIsOpen({ isOpen: isDropdownOpen }); + return isDropdownOpen; + }; + + scope.isActive = function(matchIdx) { + return scope.active === matchIdx; + }; + + scope.selectActive = function(matchIdx) { + scope.active = matchIdx; + }; + + scope.selectMatch = function(activeIdx, evt) { + var debounce = scope.debounce(); + if (angular.isNumber(debounce) || angular.isObject(debounce)) { + $$debounce(function() { + scope.select({activeIdx: activeIdx, evt: evt}); + }, angular.isNumber(debounce) ? debounce : debounce['default']); + } else { + scope.select({activeIdx: activeIdx, evt: evt}); + } + }; + } + }; + }]) + + .directive('uibTypeaheadMatch', ['$templateRequest', '$compile', '$parse', function($templateRequest, $compile, $parse) { + return { + scope: { + index: '=', + match: '=', + query: '=' + }, + link: function(scope, element, attrs) { + var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'uib/template/typeahead/typeahead-match.html'; + $templateRequest(tplUrl).then(function(tplContent) { + var tplEl = angular.element(tplContent.trim()); + element.replaceWith(tplEl); + $compile(tplEl)(scope); + }); + } + }; + }]) + + .filter('uibTypeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) { + var isSanitizePresent; + isSanitizePresent = $injector.has('$sanitize'); + + function escapeRegexp(queryToEscape) { + // Regex: capture the whole query string and replace it with the string that will be used to match + // the results, for example if the capture is "a" the result will be \a + return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1'); + } + + function containsHtml(matchItem) { + return /<.*>/g.test(matchItem); + } + + return function(matchItem, query) { + if (!isSanitizePresent && containsHtml(matchItem)) { + $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger + } + matchItem = query ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '$&') : matchItem; // Replaces the capture string with a the same string inside of a "strong" tag + if (!isSanitizePresent) { + matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive + } + return matchItem; + }; + }]); + +angular.module("uib/template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/accordion/accordion-group.html", + "
\n" + + "

\n" + + " {{heading}}\n" + + "

\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + ""); +}]); + +angular.module("uib/template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/accordion/accordion.html", + "
"); +}]); + +angular.module("uib/template/alert/alert.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/alert/alert.html", + "\n" + + "
\n" + + ""); +}]); + +angular.module("uib/template/carousel/carousel.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/carousel/carousel.html", + "
\n" + + " 1\">\n" + + " \n" + + " previous\n" + + "\n" + + " 1\">\n" + + " \n" + + " next\n" + + "\n" + + "
    1\">\n" + + "
  1. \n" + + " slide {{ $index + 1 }} of {{ slides.length }}, currently active\n" + + "
  2. \n" + + "
\n" + + ""); +}]); + +angular.module("uib/template/carousel/slide.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/carousel/slide.html", + "
\n" + + ""); +}]); + +angular.module("uib/template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/datepicker/datepicker.html", + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + ""); +}]); + +angular.module("uib/template/datepicker/day.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/datepicker/day.html", + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
{{::label.abbr}}
{{ weekNumbers[$index] }}\n" + + " \n" + + "
\n" + + ""); +}]); + +angular.module("uib/template/datepicker/month.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/datepicker/month.html", + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + " \n" + + "
\n" + + ""); +}]); + +angular.module("uib/template/datepicker/year.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/datepicker/year.html", + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + " \n" + + "
\n" + + ""); +}]); + +angular.module("uib/template/datepickerPopup/popup.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/datepickerPopup/popup.html", + "
    \n" + + "
  • \n" + + "
  • \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
  • \n" + + "
\n" + + ""); +}]); + +angular.module("uib/template/modal/window.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/modal/window.html", + "
\n" + + ""); +}]); + +angular.module("uib/template/pager/pager.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/pager/pager.html", + "
  • {{::getText('previous')}}
  • \n" + + "
  • {{::getText('next')}}
  • \n" + + ""); +}]); + +angular.module("uib/template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/pagination/pagination.html", + "
  • {{::getText('first')}}
  • \n" + + "
  • {{::getText('previous')}}
  • \n" + + "
  • {{page.text}}
  • \n" + + "
  • {{::getText('next')}}
  • \n" + + "
  • {{::getText('last')}}
  • \n" + + ""); +}]); + +angular.module("uib/template/tooltip/tooltip-html-popup.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/tooltip/tooltip-html-popup.html", + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/tooltip/tooltip-popup.html", + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/tooltip/tooltip-template-popup.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/tooltip/tooltip-template-popup.html", + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/popover/popover-html.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/popover/popover-html.html", + "
    \n" + + "\n" + + "
    \n" + + "

    \n" + + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/popover/popover-template.html", + "
    \n" + + "\n" + + "
    \n" + + "

    \n" + + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/popover/popover.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/popover/popover.html", + "
    \n" + + "\n" + + "
    \n" + + "

    \n" + + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/progressbar/bar.html", + "
    \n" + + ""); +}]); + +angular.module("uib/template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/progressbar/progress.html", + "
    "); +}]); + +angular.module("uib/template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/progressbar/progressbar.html", + "
    \n" + + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/rating/rating.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/rating/rating.html", + "\n" + + " ({{ $index < value ? '*' : ' ' }})\n" + + " \n" + + "\n" + + ""); +}]); + +angular.module("uib/template/tabs/tab.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/tabs/tab.html", + "
  • \n" + + " {{heading}}\n" + + "
  • \n" + + ""); +}]); + +angular.module("uib/template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/tabs/tabset.html", + "
    \n" + + "
      \n" + + "
      \n" + + "
      \n" + + "
      \n" + + "
      \n" + + "
      \n" + + ""); +}]); + +angular.module("uib/template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/timepicker/timepicker.html", + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
        
      \n" + + " \n" + + " :\n" + + " \n" + + " :\n" + + " \n" + + "
        
      \n" + + ""); +}]); + +angular.module("uib/template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/typeahead/typeahead-match.html", + "\n" + + ""); +}]); + +angular.module("uib/template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/typeahead/typeahead-popup.html", + "
        \n" + + "
      • \n" + + "
        \n" + + "
      • \n" + + "
      \n" + + ""); +}]); +angular.module('ui.bootstrap.carousel').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibCarouselCss && angular.element(document).find('head').prepend(''); angular.$$uibCarouselCss = true; }); +angular.module('ui.bootstrap.datepicker').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibDatepickerCss && angular.element(document).find('head').prepend(''); angular.$$uibDatepickerCss = true; }); +angular.module('ui.bootstrap.position').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibPositionCss && angular.element(document).find('head').prepend(''); angular.$$uibPositionCss = true; }); +angular.module('ui.bootstrap.datepickerPopup').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibDatepickerpopupCss && angular.element(document).find('head').prepend(''); angular.$$uibDatepickerpopupCss = true; }); +angular.module('ui.bootstrap.tooltip').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTooltipCss && angular.element(document).find('head').prepend(''); angular.$$uibTooltipCss = true; }); +angular.module('ui.bootstrap.timepicker').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTimepickerCss && angular.element(document).find('head').prepend(''); angular.$$uibTimepickerCss = true; }); +angular.module('ui.bootstrap.typeahead').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTypeaheadCss && angular.element(document).find('head').prepend(''); angular.$$uibTypeaheadCss = true; }); \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js b/ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js new file mode 100644 index 00000000..53c07848 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js @@ -0,0 +1,10 @@ +/* + * angular-ui-bootstrap + * http://angular-ui.github.io/bootstrap/ + + * Version: 2.4.0 - 2016-12-29 + * License: MIT + */angular.module("ui.bootstrap",["ui.bootstrap.tpls","ui.bootstrap.collapse","ui.bootstrap.tabindex","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.datepicker","ui.bootstrap.position","ui.bootstrap.datepickerPopup","ui.bootstrap.debounce","ui.bootstrap.multiMap","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]),angular.module("ui.bootstrap.tpls",["uib/template/accordion/accordion-group.html","uib/template/accordion/accordion.html","uib/template/alert/alert.html","uib/template/carousel/carousel.html","uib/template/carousel/slide.html","uib/template/datepicker/datepicker.html","uib/template/datepicker/day.html","uib/template/datepicker/month.html","uib/template/datepicker/year.html","uib/template/datepickerPopup/popup.html","uib/template/modal/window.html","uib/template/pager/pager.html","uib/template/pagination/pagination.html","uib/template/tooltip/tooltip-html-popup.html","uib/template/tooltip/tooltip-popup.html","uib/template/tooltip/tooltip-template-popup.html","uib/template/popover/popover-html.html","uib/template/popover/popover-template.html","uib/template/popover/popover.html","uib/template/progressbar/bar.html","uib/template/progressbar/progress.html","uib/template/progressbar/progressbar.html","uib/template/rating/rating.html","uib/template/tabs/tab.html","uib/template/tabs/tabset.html","uib/template/timepicker/timepicker.html","uib/template/typeahead/typeahead-match.html","uib/template/typeahead/typeahead-popup.html"]),angular.module("ui.bootstrap.collapse",[]).directive("uibCollapse",["$animate","$q","$parse","$injector",function(a,b,c,d){var e=d.has("$animateCss")?d.get("$animateCss"):null;return{link:function(d,f,g){function h(){r=!!("horizontal"in g),r?(s={width:""},t={width:"0"}):(s={height:""},t={height:"0"}),d.$eval(g.uibCollapse)||f.addClass("in").addClass("collapse").attr("aria-expanded",!0).attr("aria-hidden",!1).css(s)}function i(a){return r?{width:a.scrollWidth+"px"}:{height:a.scrollHeight+"px"}}function j(){f.hasClass("collapse")&&f.hasClass("in")||b.resolve(n(d)).then(function(){f.removeClass("collapse").addClass("collapsing").attr("aria-expanded",!0).attr("aria-hidden",!1),e?e(f,{addClass:"in",easing:"ease",css:{overflow:"hidden"},to:i(f[0])}).start()["finally"](k):a.addClass(f,"in",{css:{overflow:"hidden"},to:i(f[0])}).then(k)})}function k(){f.removeClass("collapsing").addClass("collapse").css(s),o(d)}function l(){return f.hasClass("collapse")||f.hasClass("in")?void b.resolve(p(d)).then(function(){f.css(i(f[0])).removeClass("collapse").addClass("collapsing").attr("aria-expanded",!1).attr("aria-hidden",!0),e?e(f,{removeClass:"in",to:t}).start()["finally"](m):a.removeClass(f,"in",{to:t}).then(m)}):m()}function m(){f.css(t),f.removeClass("collapsing").addClass("collapse"),q(d)}var n=c(g.expanding),o=c(g.expanded),p=c(g.collapsing),q=c(g.collapsed),r=!1,s={},t={};h(),d.$watch(g.uibCollapse,function(a){a?l():j()})}}}]),angular.module("ui.bootstrap.tabindex",[]).directive("uibTabindexToggle",function(){return{restrict:"A",link:function(a,b,c){c.$observe("disabled",function(a){c.$set("tabindex",a?-1:null)})}}}),angular.module("ui.bootstrap.accordion",["ui.bootstrap.collapse","ui.bootstrap.tabindex"]).constant("uibAccordionConfig",{closeOthers:!0}).controller("UibAccordionController",["$scope","$attrs","uibAccordionConfig",function(a,b,c){this.groups=[],this.closeOthers=function(d){var e=angular.isDefined(b.closeOthers)?a.$eval(b.closeOthers):c.closeOthers;e&&angular.forEach(this.groups,function(a){a!==d&&(a.isOpen=!1)})},this.addGroup=function(a){var b=this;this.groups.push(a),a.$on("$destroy",function(c){b.removeGroup(a)})},this.removeGroup=function(a){var b=this.groups.indexOf(a);-1!==b&&this.groups.splice(b,1)}}]).directive("uibAccordion",function(){return{controller:"UibAccordionController",controllerAs:"accordion",transclude:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/accordion/accordion.html"}}}).directive("uibAccordionGroup",function(){return{require:"^uibAccordion",transclude:!0,restrict:"A",templateUrl:function(a,b){return b.templateUrl||"uib/template/accordion/accordion-group.html"},scope:{heading:"@",panelClass:"@?",isOpen:"=?",isDisabled:"=?"},controller:function(){this.setHeading=function(a){this.heading=a}},link:function(a,b,c,d){b.addClass("panel"),d.addGroup(a),a.openClass=c.openClass||"panel-open",a.panelClass=c.panelClass||"panel-default",a.$watch("isOpen",function(c){b.toggleClass(a.openClass,!!c),c&&d.closeOthers(a)}),a.toggleOpen=function(b){a.isDisabled||b&&32!==b.which||(a.isOpen=!a.isOpen)};var e="accordiongroup-"+a.$id+"-"+Math.floor(1e4*Math.random());a.headingId=e+"-tab",a.panelId=e+"-panel"}}}).directive("uibAccordionHeading",function(){return{transclude:!0,template:"",replace:!0,require:"^uibAccordionGroup",link:function(a,b,c,d,e){d.setHeading(e(a,angular.noop))}}}).directive("uibAccordionTransclude",function(){function a(){return"uib-accordion-header,data-uib-accordion-header,x-uib-accordion-header,uib\\:accordion-header,[uib-accordion-header],[data-uib-accordion-header],[x-uib-accordion-header]"}return{require:"^uibAccordionGroup",link:function(b,c,d,e){b.$watch(function(){return e[d.uibAccordionTransclude]},function(b){if(b){var d=angular.element(c[0].querySelector(a()));d.html(""),d.append(b)}})}}}),angular.module("ui.bootstrap.alert",[]).controller("UibAlertController",["$scope","$element","$attrs","$interpolate","$timeout",function(a,b,c,d,e){a.closeable=!!c.close,b.addClass("alert"),c.$set("role","alert"),a.closeable&&b.addClass("alert-dismissible");var f=angular.isDefined(c.dismissOnTimeout)?d(c.dismissOnTimeout)(a.$parent):null;f&&e(function(){a.close()},parseInt(f,10))}]).directive("uibAlert",function(){return{controller:"UibAlertController",controllerAs:"alert",restrict:"A",templateUrl:function(a,b){return b.templateUrl||"uib/template/alert/alert.html"},transclude:!0,scope:{close:"&"}}}),angular.module("ui.bootstrap.buttons",[]).constant("uibButtonConfig",{activeClass:"active",toggleEvent:"click"}).controller("UibButtonsController",["uibButtonConfig",function(a){this.activeClass=a.activeClass||"active",this.toggleEvent=a.toggleEvent||"click"}]).directive("uibBtnRadio",["$parse",function(a){return{require:["uibBtnRadio","ngModel"],controller:"UibButtonsController",controllerAs:"buttons",link:function(b,c,d,e){var f=e[0],g=e[1],h=a(d.uibUncheckable);c.find("input").css({display:"none"}),g.$render=function(){c.toggleClass(f.activeClass,angular.equals(g.$modelValue,b.$eval(d.uibBtnRadio)))},c.on(f.toggleEvent,function(){if(!d.disabled){var a=c.hasClass(f.activeClass);a&&!angular.isDefined(d.uncheckable)||b.$apply(function(){g.$setViewValue(a?null:b.$eval(d.uibBtnRadio)),g.$render()})}}),d.uibUncheckable&&b.$watch(h,function(a){d.$set("uncheckable",a?"":void 0)})}}}]).directive("uibBtnCheckbox",function(){return{require:["uibBtnCheckbox","ngModel"],controller:"UibButtonsController",controllerAs:"button",link:function(a,b,c,d){function e(){return g(c.btnCheckboxTrue,!0)}function f(){return g(c.btnCheckboxFalse,!1)}function g(b,c){return angular.isDefined(b)?a.$eval(b):c}var h=d[0],i=d[1];b.find("input").css({display:"none"}),i.$render=function(){b.toggleClass(h.activeClass,angular.equals(i.$modelValue,e()))},b.on(h.toggleEvent,function(){c.disabled||a.$apply(function(){i.$setViewValue(b.hasClass(h.activeClass)?f():e()),i.$render()})})}}}),angular.module("ui.bootstrap.carousel",[]).controller("UibCarouselController",["$scope","$element","$interval","$timeout","$animate",function(a,b,c,d,e){function f(){for(;t.length;)t.shift()}function g(a){for(var b=0;b1){q[d].element.data(r,c.direction);var j=p.getCurrentIndex();angular.isNumber(j)&&q[j].element&&q[j].element.data(r,c.direction),a.$currentTransition=!0,e.on("addClass",q[d].element,function(b,c){if("close"===c&&(a.$currentTransition=null,e.off("addClass",b),t.length)){var d=t.pop().slide,g=d.index,i=g>p.getCurrentIndex()?"next":"prev";f(),h(d,g,i)}})}a.active=c.index,s=c.index,g(d),l()}}function i(a){for(var b=0;b0&&(n=c(m,b))}function m(){var b=+a.interval;o&&!isNaN(b)&&b>0&&q.length?a.next():a.pause()}var n,o,p=this,q=p.slides=a.slides=[],r="uib-slideDirection",s=a.active,t=[],u=!1;b.addClass("carousel"),p.addSlide=function(b,c){q.push({slide:b,element:c}),q.sort(function(a,b){return+a.slide.index-+b.slide.index}),(b.index===a.active||1===q.length&&!angular.isNumber(a.active))&&(a.$currentTransition&&(a.$currentTransition=null),s=b.index,a.active=b.index,g(s),p.select(q[i(b)]),1===q.length&&a.play())},p.getCurrentIndex=function(){for(var a=0;a0&&s===c?c>=q.length?(s=q.length-1,a.active=s,g(s),p.select(q[q.length-1])):(s=c,a.active=s,g(s),p.select(q[c])):s>c&&(s--,a.active=s),0===q.length&&(s=null,a.active=null,f())},p.select=a.select=function(b,c){var d=i(b.slide);void 0===c&&(c=d>p.getCurrentIndex()?"next":"prev"),b.slide.index===s||a.$currentTransition?b&&b.slide.index!==s&&a.$currentTransition&&t.push(q[d]):h(b.slide,d,c)},a.indexOfSlide=function(a){return+a.slide.index},a.isActive=function(b){return a.active===b.slide.index},a.isPrevDisabled=function(){return 0===a.active&&a.noWrap()},a.isNextDisabled=function(){return a.active===q.length-1&&a.noWrap()},a.pause=function(){a.noPause||(o=!1,j())},a.play=function(){o||(o=!0,l())},b.on("mouseenter",a.pause),b.on("mouseleave",a.play),a.$on("$destroy",function(){u=!0,j()}),a.$watch("noTransition",function(a){e.enabled(b,!a)}),a.$watch("interval",l),a.$watchCollection("slides",k),a.$watch("active",function(a){if(angular.isNumber(a)&&s!==a){for(var b=0;b-1){var f=!1;a=a.split("");for(var g=e;g-1){a=a.split(""),c[e]="("+d.regex+")",a[e]="$";for(var f=e+1,g=e+d.key.length;g>f;f++)c[f]="",a[f]="$";a=a.join(""),b.push({index:e,key:d.key,apply:d.apply,matcher:d.regex})}}),{regex:new RegExp("^"+c.join("")+"$"),map:d(b,"index")}}function h(a){for(var b,c,d=[],e=0;e=a.length||"'"!==a.charAt(e+1))&&(d.push(i(a,c,e)),c=null);else if(e===a.length)for(;cc?!1:1===b&&c>28?29===c&&(a%4===0&&a%100!==0||a%400===0):3===b||5===b||8===b||10===b?31>c:!0}function l(a){return parseInt(a,10)}function m(a,b){return a&&b?q(a,b):a}function n(a,b){return a&&b?q(a,b,!0):a}function o(a,b){a=a.replace(/:/g,"");var c=Date.parse("Jan 01, 1970 00:00:00 "+a)/6e4;return isNaN(c)?b:c}function p(a,b){return a=new Date(a.getTime()),a.setMinutes(a.getMinutes()+b),a}function q(a,b,c){c=c?-1:1;var d=a.getTimezoneOffset(),e=o(b,d);return p(a,c*(e-d))}var r,s,t=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;this.init=function(){r=b.id,this.parsers={},this.formatters={},s=[{key:"yyyy",regex:"\\d{4}",apply:function(a){this.year=+a},formatter:function(a){var b=new Date;return b.setFullYear(Math.abs(a.getFullYear())),c(b,"yyyy")}},{key:"yy",regex:"\\d{2}",apply:function(a){a=+a,this.year=69>a?a+2e3:a+1900},formatter:function(a){var b=new Date;return b.setFullYear(Math.abs(a.getFullYear())),c(b,"yy")}},{key:"y",regex:"\\d{1,4}",apply:function(a){this.year=+a},formatter:function(a){var b=new Date;return b.setFullYear(Math.abs(a.getFullYear())),c(b,"y")}},{key:"M!",regex:"0?[1-9]|1[0-2]",apply:function(a){this.month=a-1},formatter:function(a){var b=a.getMonth();return/^[0-9]$/.test(b)?c(a,"MM"):c(a,"M")}},{key:"MMMM",regex:b.DATETIME_FORMATS.MONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.MONTH.indexOf(a)},formatter:function(a){return c(a,"MMMM")}},{key:"MMM",regex:b.DATETIME_FORMATS.SHORTMONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.SHORTMONTH.indexOf(a)},formatter:function(a){return c(a,"MMM")}},{key:"MM",regex:"0[1-9]|1[0-2]",apply:function(a){this.month=a-1},formatter:function(a){return c(a,"MM")}},{key:"M",regex:"[1-9]|1[0-2]",apply:function(a){this.month=a-1},formatter:function(a){return c(a,"M")}},{key:"d!",regex:"[0-2]?[0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a},formatter:function(a){var b=a.getDate();return/^[1-9]$/.test(b)?c(a,"dd"):c(a,"d")}},{key:"dd",regex:"[0-2][0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a},formatter:function(a){return c(a,"dd")}},{key:"d",regex:"[1-2]?[0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a},formatter:function(a){return c(a,"d")}},{key:"EEEE",regex:b.DATETIME_FORMATS.DAY.join("|"),formatter:function(a){return c(a,"EEEE")}},{key:"EEE",regex:b.DATETIME_FORMATS.SHORTDAY.join("|"),formatter:function(a){return c(a,"EEE")}},{key:"HH",regex:"(?:0|1)[0-9]|2[0-3]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"HH")}},{key:"hh",regex:"0[0-9]|1[0-2]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"hh")}},{key:"H",regex:"1?[0-9]|2[0-3]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"H")}},{key:"h",regex:"[0-9]|1[0-2]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"h")}},{key:"mm",regex:"[0-5][0-9]",apply:function(a){this.minutes=+a},formatter:function(a){return c(a,"mm")}},{key:"m",regex:"[0-9]|[1-5][0-9]",apply:function(a){this.minutes=+a},formatter:function(a){return c(a,"m")}},{key:"sss",regex:"[0-9][0-9][0-9]",apply:function(a){this.milliseconds=+a},formatter:function(a){return c(a,"sss")}},{key:"ss",regex:"[0-5][0-9]",apply:function(a){this.seconds=+a},formatter:function(a){return c(a,"ss")}},{key:"s",regex:"[0-9]|[1-5][0-9]",apply:function(a){this.seconds=+a},formatter:function(a){return c(a,"s")}},{key:"a",regex:b.DATETIME_FORMATS.AMPMS.join("|"),apply:function(a){12===this.hours&&(this.hours=0),"PM"===a&&(this.hours+=12)},formatter:function(a){return c(a,"a")}},{key:"Z",regex:"[+-]\\d{4}",apply:function(a){var b=a.match(/([+-])(\d{2})(\d{2})/),c=b[1],d=b[2],e=b[3];this.hours+=l(c+d),this.minutes+=l(c+e)},formatter:function(a){return c(a,"Z")}},{key:"ww",regex:"[0-4][0-9]|5[0-3]",formatter:function(a){return c(a,"ww")}},{key:"w",regex:"[0-9]|[1-4][0-9]|5[0-3]",formatter:function(a){return c(a,"w")}},{key:"GGGG",regex:b.DATETIME_FORMATS.ERANAMES.join("|").replace(/\s/g,"\\s"),formatter:function(a){return c(a,"GGGG")}},{key:"GGG",regex:b.DATETIME_FORMATS.ERAS.join("|"),formatter:function(a){return c(a,"GGG")}},{key:"GG",regex:b.DATETIME_FORMATS.ERAS.join("|"),formatter:function(a){return c(a,"GG")}},{key:"G",regex:b.DATETIME_FORMATS.ERAS.join("|"),formatter:function(a){return c(a,"G")}}],angular.version.major>=1&&angular.version.minor>4&&s.push({key:"LLLL",regex:b.DATETIME_FORMATS.STANDALONEMONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.STANDALONEMONTH.indexOf(a)},formatter:function(a){return c(a,"LLLL")}})},this.init(),this.getParser=function(a){var b=f(a);return b&&b.apply||null},this.overrideParser=function(a,b){var c=f(a);c&&angular.isFunction(b)&&(this.parsers={},c.apply=b)}.bind(this),this.filter=function(a,c){if(!angular.isDate(a)||isNaN(a)||!c)return"";c=b.DATETIME_FORMATS[c]||c,b.id!==r&&this.init(),this.formatters[c]||(this.formatters[c]=h(c));var d=this.formatters[c];return d.reduce(function(b,c){return b+c(a)},"")},this.parse=function(c,d,e){if(!angular.isString(c)||!d)return c;d=b.DATETIME_FORMATS[d]||d,d=d.replace(t,"\\$&"),b.id!==r&&this.init(),this.parsers[d]||(this.parsers[d]=g(d,"apply"));var f=this.parsers[d],h=f.regex,i=f.map,j=c.match(h),l=!1;if(j&&j.length){var m,n;angular.isDate(e)&&!isNaN(e.getTime())?m={year:e.getFullYear(),month:e.getMonth(),date:e.getDate(),hours:e.getHours(),minutes:e.getMinutes(),seconds:e.getSeconds(),milliseconds:e.getMilliseconds()}:(e&&a.warn("dateparser:","baseDate is not a valid date"),m={year:1900,month:0,date:1,hours:0,minutes:0,seconds:0,milliseconds:0});for(var o=1,p=j.length;p>o;o++){var q=i[o-1];"Z"===q.matcher&&(l=!0),q.apply&&q.apply.call(m,j[o])}var s=l?Date.prototype.setUTCFullYear:Date.prototype.setFullYear,u=l?Date.prototype.setUTCHours:Date.prototype.setHours;return k(m.year,m.month,m.date)&&(!angular.isDate(e)||isNaN(e.getTime())||l?(n=new Date(0),s.call(n,m.year,m.month,m.date),u.call(n,m.hours||0,m.minutes||0,m.seconds||0,m.milliseconds||0)):(n=new Date(e),s.call(n,m.year,m.month,m.date),u.call(n,m.hours,m.minutes,m.seconds,m.milliseconds))),n}},this.toTimezone=m,this.fromTimezone=n,this.timezoneToOffset=o,this.addDateMinutes=p,this.convertTimezoneToLocal=q}]),angular.module("ui.bootstrap.isClass",[]).directive("uibIsClass",["$animate",function(a){var b=/^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/,c=/^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/;return{restrict:"A",compile:function(d,e){function f(a,b,c){i.push(a),j.push({scope:a,element:b}),o.forEach(function(b,c){g(b,a)}),a.$on("$destroy",h)}function g(b,d){var e=b.match(c),f=d.$eval(e[1]),g=e[2],h=k[b];if(!h){var i=function(b){var c=null;j.some(function(a){var d=a.scope.$eval(m);return d===b?(c=a,!0):void 0}),h.lastActivated!==c&&(h.lastActivated&&a.removeClass(h.lastActivated.element,f),c&&a.addClass(c.element,f),h.lastActivated=c)};k[b]=h={lastActivated:null,scope:d,watchFn:i,compareWithExp:g,watcher:d.$watch(g,i)}}h.watchFn(d.$eval(g))}function h(a){var b=a.targetScope,c=i.indexOf(b);if(i.splice(c,1),j.splice(c,1),i.length){var d=i[0];angular.forEach(k,function(a){a.scope===b&&(a.watcher=d.$watch(a.compareWithExp,a.watchFn),a.scope=d)})}else k={}}var i=[],j=[],k={},l=e.uibIsClass.match(b),m=l[2],n=l[1],o=n.split(",");return f}}}]),angular.module("ui.bootstrap.datepicker",["ui.bootstrap.dateparser","ui.bootstrap.isClass"]).value("$datepickerSuppressError",!1).value("$datepickerLiteralWarning",!0).constant("uibDatepickerConfig",{datepickerMode:"day",formatDay:"dd",formatMonth:"MMMM",formatYear:"yyyy",formatDayHeader:"EEE",formatDayTitle:"MMMM yyyy",formatMonthTitle:"yyyy",maxDate:null,maxMode:"year",minDate:null,minMode:"day",monthColumns:3,ngModelOptions:{},shortcutPropagation:!1,showWeeks:!0,yearColumns:5,yearRows:4}).controller("UibDatepickerController",["$scope","$element","$attrs","$parse","$interpolate","$locale","$log","dateFilter","uibDatepickerConfig","$datepickerLiteralWarning","$datepickerSuppressError","uibDateParser",function(a,b,c,d,e,f,g,h,i,j,k,l){function m(b){a.datepickerMode=b,a.datepickerOptions.datepickerMode=b}var n=this,o={$setViewValue:angular.noop},p={},q=[];b.addClass("uib-datepicker"),c.$set("role","application"),a.datepickerOptions||(a.datepickerOptions={}),this.modes=["day","month","year"],["customClass","dateDisabled","datepickerMode","formatDay","formatDayHeader","formatDayTitle","formatMonth","formatMonthTitle","formatYear","maxDate","maxMode","minDate","minMode","monthColumns","showWeeks","shortcutPropagation","startingDay","yearColumns","yearRows"].forEach(function(b){switch(b){case"customClass":case"dateDisabled":a[b]=a.datepickerOptions[b]||angular.noop;break;case"datepickerMode":a.datepickerMode=angular.isDefined(a.datepickerOptions.datepickerMode)?a.datepickerOptions.datepickerMode:i.datepickerMode;break;case"formatDay":case"formatDayHeader":case"formatDayTitle":case"formatMonth":case"formatMonthTitle":case"formatYear":n[b]=angular.isDefined(a.datepickerOptions[b])?e(a.datepickerOptions[b])(a.$parent):i[b];break;case"monthColumns":case"showWeeks":case"shortcutPropagation":case"yearColumns":case"yearRows":n[b]=angular.isDefined(a.datepickerOptions[b])?a.datepickerOptions[b]:i[b];break;case"startingDay":angular.isDefined(a.datepickerOptions.startingDay)?n.startingDay=a.datepickerOptions.startingDay:angular.isNumber(i.startingDay)?n.startingDay=i.startingDay:n.startingDay=(f.DATETIME_FORMATS.FIRSTDAYOFWEEK+8)%7;break;case"maxDate":case"minDate":a.$watch("datepickerOptions."+b,function(a){a?angular.isDate(a)?n[b]=l.fromTimezone(new Date(a),p.timezone):(j&&g.warn("Literal date support has been deprecated, please switch to date object usage"),n[b]=new Date(h(a,"medium"))):n[b]=i[b]?l.fromTimezone(new Date(i[b]),p.timezone):null,n.refreshView()});break;case"maxMode":case"minMode":a.datepickerOptions[b]?a.$watch(function(){return a.datepickerOptions[b]},function(c){n[b]=a[b]=angular.isDefined(c)?c:a.datepickerOptions[b],("minMode"===b&&n.modes.indexOf(a.datepickerOptions.datepickerMode)n.modes.indexOf(n[b]))&&(a.datepickerMode=n[b],a.datepickerOptions.datepickerMode=n[b])}):n[b]=a[b]=i[b]||null}}),a.uniqueId="datepicker-"+a.$id+"-"+Math.floor(1e4*Math.random()),a.disabled=angular.isDefined(c.disabled)||!1,angular.isDefined(c.ngDisabled)&&q.push(a.$parent.$watch(c.ngDisabled,function(b){a.disabled=b,n.refreshView()})),a.isActive=function(b){return 0===n.compare(b.date,n.activeDate)?(a.activeDateId=b.uid,!0):!1},this.init=function(b){o=b,p=b.$options||a.datepickerOptions.ngModelOptions||i.ngModelOptions,a.datepickerOptions.initDate?(n.activeDate=l.fromTimezone(a.datepickerOptions.initDate,p.timezone)||new Date,a.$watch("datepickerOptions.initDate",function(a){a&&(o.$isEmpty(o.$modelValue)||o.$invalid)&&(n.activeDate=l.fromTimezone(a,p.timezone),n.refreshView())})):n.activeDate=new Date;var c=o.$modelValue?new Date(o.$modelValue):new Date;this.activeDate=isNaN(c)?l.fromTimezone(new Date,p.timezone):l.fromTimezone(c,p.timezone),o.$render=function(){n.render()}},this.render=function(){if(o.$viewValue){var a=new Date(o.$viewValue),b=!isNaN(a);b?this.activeDate=l.fromTimezone(a,p.timezone):k||g.error('Datepicker directive: "ng-model" value must be a Date object')}this.refreshView()},this.refreshView=function(){if(this.element){a.selectedDt=null,this._refreshView(),a.activeDt&&(a.activeDateId=a.activeDt.uid);var b=o.$viewValue?new Date(o.$viewValue):null;b=l.fromTimezone(b,p.timezone),o.$setValidity("dateDisabled",!b||this.element&&!this.isDisabled(b))}},this.createDateObject=function(b,c){var d=o.$viewValue?new Date(o.$viewValue):null;d=l.fromTimezone(d,p.timezone);var e=new Date;e=l.fromTimezone(e,p.timezone);var f=this.compare(b,e),g={date:b,label:l.filter(b,c),selected:d&&0===this.compare(b,d),disabled:this.isDisabled(b),past:0>f,current:0===f,future:f>0,customClass:this.customClass(b)||null};return d&&0===this.compare(b,d)&&(a.selectedDt=g),n.activeDate&&0===this.compare(g.date,n.activeDate)&&(a.activeDt=g),g},this.isDisabled=function(b){return a.disabled||this.minDate&&this.compare(b,this.minDate)<0||this.maxDate&&this.compare(b,this.maxDate)>0||a.dateDisabled&&a.dateDisabled({date:b,mode:a.datepickerMode})},this.customClass=function(b){return a.customClass({date:b,mode:a.datepickerMode})},this.split=function(a,b){for(var c=[];a.length>0;)c.push(a.splice(0,b));return c},a.select=function(b){if(a.datepickerMode===n.minMode){var c=o.$viewValue?l.fromTimezone(new Date(o.$viewValue),p.timezone):new Date(0,0,0,0,0,0,0);c.setFullYear(b.getFullYear(),b.getMonth(),b.getDate()),c=l.toTimezone(c,p.timezone),o.$setViewValue(c),o.$render()}else n.activeDate=b,m(n.modes[n.modes.indexOf(a.datepickerMode)-1]),a.$emit("uib:datepicker.mode");a.$broadcast("uib:datepicker.focus")},a.move=function(a){var b=n.activeDate.getFullYear()+a*(n.step.years||0),c=n.activeDate.getMonth()+a*(n.step.months||0);n.activeDate.setFullYear(b,c,1),n.refreshView()},a.toggleMode=function(b){b=b||1,a.datepickerMode===n.maxMode&&1===b||a.datepickerMode===n.minMode&&-1===b||(m(n.modes[n.modes.indexOf(a.datepickerMode)+b]),a.$emit("uib:datepicker.mode"))},a.keys={13:"enter",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down"};var r=function(){n.element[0].focus()};a.$on("uib:datepicker.focus",r),a.keydown=function(b){var c=a.keys[b.which];if(c&&!b.shiftKey&&!b.altKey&&!a.disabled)if(b.preventDefault(),n.shortcutPropagation||b.stopPropagation(),"enter"===c||"space"===c){if(n.isDisabled(n.activeDate))return;a.select(n.activeDate)}else!b.ctrlKey||"up"!==c&&"down"!==c?(n.handleKeyDown(c,b),n.refreshView()):a.toggleMode("up"===c?1:-1)},b.on("keydown",function(b){a.$apply(function(){a.keydown(b)})}),a.$on("$destroy",function(){for(;q.length;)q.shift()()})}]).controller("UibDaypickerController",["$scope","$element","dateFilter",function(a,b,c){function d(a,b){return 1!==b||a%4!==0||a%100===0&&a%400!==0?f[b]:29}function e(a){var b=new Date(a);b.setDate(b.getDate()+4-(b.getDay()||7));var c=b.getTime();return b.setMonth(0),b.setDate(1),Math.floor(Math.round((c-b)/864e5)/7)+1}var f=[31,28,31,30,31,30,31,31,30,31,30,31];this.step={months:1},this.element=b,this.init=function(b){angular.extend(b,this),a.showWeeks=b.showWeeks,b.refreshView()},this.getDates=function(a,b){for(var c,d=new Array(b),e=new Date(a),f=0;b>f;)c=new Date(e),d[f++]=c,e.setDate(e.getDate()+1);return d},this._refreshView=function(){var b=this.activeDate.getFullYear(),d=this.activeDate.getMonth(),f=new Date(this.activeDate);f.setFullYear(b,d,1);var g=this.startingDay-f.getDay(),h=g>0?7-g:-g,i=new Date(f);h>0&&i.setDate(-h+1);for(var j=this.getDates(i,42),k=0;42>k;k++)j[k]=angular.extend(this.createDateObject(j[k],this.formatDay),{secondary:j[k].getMonth()!==d,uid:a.uniqueId+"-"+k});a.labels=new Array(7);for(var l=0;7>l;l++)a.labels[l]={abbr:c(j[l].date,this.formatDayHeader),full:c(j[l].date,"EEEE")};if(a.title=c(this.activeDate,this.formatDayTitle),a.rows=this.split(j,7),a.showWeeks){a.weekNumbers=[];for(var m=(11-this.startingDay)%7,n=a.rows.length,o=0;n>o;o++)a.weekNumbers.push(e(a.rows[o][m].date))}},this.compare=function(a,b){var c=new Date(a.getFullYear(),a.getMonth(),a.getDate()),d=new Date(b.getFullYear(),b.getMonth(),b.getDate());return c.setFullYear(a.getFullYear()),d.setFullYear(b.getFullYear()),c-d},this.handleKeyDown=function(a,b){var c=this.activeDate.getDate();if("left"===a)c-=1;else if("up"===a)c-=7;else if("right"===a)c+=1;else if("down"===a)c+=7;else if("pageup"===a||"pagedown"===a){var e=this.activeDate.getMonth()+("pageup"===a?-1:1);this.activeDate.setMonth(e,1),c=Math.min(d(this.activeDate.getFullYear(),this.activeDate.getMonth()),c)}else"home"===a?c=1:"end"===a&&(c=d(this.activeDate.getFullYear(),this.activeDate.getMonth()));this.activeDate.setDate(c)}}]).controller("UibMonthpickerController",["$scope","$element","dateFilter",function(a,b,c){this.step={years:1},this.element=b,this.init=function(a){angular.extend(a,this),a.refreshView()},this._refreshView=function(){for(var b,d=new Array(12),e=this.activeDate.getFullYear(),f=0;12>f;f++)b=new Date(this.activeDate),b.setFullYear(e,f,1),d[f]=angular.extend(this.createDateObject(b,this.formatMonth),{uid:a.uniqueId+"-"+f});a.title=c(this.activeDate,this.formatMonthTitle),a.rows=this.split(d,this.monthColumns),a.yearHeaderColspan=this.monthColumns>3?this.monthColumns-2:1},this.compare=function(a,b){var c=new Date(a.getFullYear(),a.getMonth()),d=new Date(b.getFullYear(),b.getMonth());return c.setFullYear(a.getFullYear()),d.setFullYear(b.getFullYear()),c-d},this.handleKeyDown=function(a,b){var c=this.activeDate.getMonth();if("left"===a)c-=1;else if("up"===a)c-=this.monthColumns;else if("right"===a)c+=1;else if("down"===a)c+=this.monthColumns;else if("pageup"===a||"pagedown"===a){var d=this.activeDate.getFullYear()+("pageup"===a?-1:1);this.activeDate.setFullYear(d)}else"home"===a?c=0:"end"===a&&(c=11);this.activeDate.setMonth(c)}}]).controller("UibYearpickerController",["$scope","$element","dateFilter",function(a,b,c){function d(a){return parseInt((a-1)/f,10)*f+1}var e,f;this.element=b,this.yearpickerInit=function(){e=this.yearColumns,f=this.yearRows*e,this.step={years:f}},this._refreshView=function(){for(var b,c=new Array(f),g=0,h=d(this.activeDate.getFullYear());f>g;g++)b=new Date(this.activeDate),b.setFullYear(h+g,0,1),c[g]=angular.extend(this.createDateObject(b,this.formatYear),{uid:a.uniqueId+"-"+g});a.title=[c[0].label,c[f-1].label].join(" - "),a.rows=this.split(c,e),a.columns=e},this.compare=function(a,b){return a.getFullYear()-b.getFullYear()},this.handleKeyDown=function(a,b){var c=this.activeDate.getFullYear();"left"===a?c-=1:"up"===a?c-=e:"right"===a?c+=1:"down"===a?c+=e:"pageup"===a||"pagedown"===a?c+=("pageup"===a?-1:1)*f:"home"===a?c=d(this.activeDate.getFullYear()):"end"===a&&(c=d(this.activeDate.getFullYear())+f-1),this.activeDate.setFullYear(c)}}]).directive("uibDatepicker",function(){return{templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/datepicker.html"},scope:{datepickerOptions:"=?"},require:["uibDatepicker","^ngModel"],restrict:"A",controller:"UibDatepickerController",controllerAs:"datepicker",link:function(a,b,c,d){var e=d[0],f=d[1];e.init(f)}}}).directive("uibDaypicker",function(){return{templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/day.html"},require:["^uibDatepicker","uibDaypicker"],restrict:"A",controller:"UibDaypickerController",link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibMonthpicker",function(){return{templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/month.html"},require:["^uibDatepicker","uibMonthpicker"],restrict:"A",controller:"UibMonthpickerController", +link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibYearpicker",function(){return{templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/year.html"},require:["^uibDatepicker","uibYearpicker"],restrict:"A",controller:"UibYearpickerController",link:function(a,b,c,d){var e=d[0];angular.extend(e,d[1]),e.yearpickerInit(),e.refreshView()}}}),angular.module("ui.bootstrap.position",[]).factory("$uibPosition",["$document","$window",function(a,b){var c,d,e={normal:/(auto|scroll)/,hidden:/(auto|scroll|hidden)/},f={auto:/\s?auto?\s?/i,primary:/^(top|bottom|left|right)$/,secondary:/^(top|bottom|left|right|center)$/,vertical:/^(top|bottom)$/},g=/(HTML|BODY)/;return{getRawNode:function(a){return a.nodeName?a:a[0]||a},parseStyle:function(a){return a=parseFloat(a),isFinite(a)?a:0},offsetParent:function(c){function d(a){return"static"===(b.getComputedStyle(a).position||"static")}c=this.getRawNode(c);for(var e=c.offsetParent||a[0].documentElement;e&&e!==a[0].documentElement&&d(e);)e=e.offsetParent;return e||a[0].documentElement},scrollbarWidth:function(e){if(e){if(angular.isUndefined(d)){var f=a.find("body");f.addClass("uib-position-body-scrollbar-measure"),d=b.innerWidth-f[0].clientWidth,d=isFinite(d)?d:0,f.removeClass("uib-position-body-scrollbar-measure")}return d}if(angular.isUndefined(c)){var g=angular.element('
      ');a.find("body").append(g),c=g[0].offsetWidth-g[0].clientWidth,c=isFinite(c)?c:0,g.remove()}return c},scrollbarPadding:function(a){a=this.getRawNode(a);var c=b.getComputedStyle(a),d=this.parseStyle(c.paddingRight),e=this.parseStyle(c.paddingBottom),f=this.scrollParent(a,!1,!0),h=this.scrollbarWidth(g.test(f.tagName));return{scrollbarWidth:h,widthOverflow:f.scrollWidth>f.clientWidth,right:d+h,originalRight:d,heightOverflow:f.scrollHeight>f.clientHeight,bottom:e+h,originalBottom:e}},isScrollable:function(a,c){a=this.getRawNode(a);var d=c?e.hidden:e.normal,f=b.getComputedStyle(a);return d.test(f.overflow+f.overflowY+f.overflowX)},scrollParent:function(c,d,f){c=this.getRawNode(c);var g=d?e.hidden:e.normal,h=a[0].documentElement,i=b.getComputedStyle(c);if(f&&g.test(i.overflow+i.overflowY+i.overflowX))return c;var j="absolute"===i.position,k=c.parentElement||h;if(k===h||"fixed"===i.position)return h;for(;k.parentElement&&k!==h;){var l=b.getComputedStyle(k);if(j&&"static"!==l.position&&(j=!1),!j&&g.test(l.overflow+l.overflowY+l.overflowX))break;k=k.parentElement}return k},position:function(c,d){c=this.getRawNode(c);var e=this.offset(c);if(d){var f=b.getComputedStyle(c);e.top-=this.parseStyle(f.marginTop),e.left-=this.parseStyle(f.marginLeft)}var g=this.offsetParent(c),h={top:0,left:0};return g!==a[0].documentElement&&(h=this.offset(g),h.top+=g.clientTop-g.scrollTop,h.left+=g.clientLeft-g.scrollLeft),{width:Math.round(angular.isNumber(e.width)?e.width:c.offsetWidth),height:Math.round(angular.isNumber(e.height)?e.height:c.offsetHeight),top:Math.round(e.top-h.top),left:Math.round(e.left-h.left)}},offset:function(c){c=this.getRawNode(c);var d=c.getBoundingClientRect();return{width:Math.round(angular.isNumber(d.width)?d.width:c.offsetWidth),height:Math.round(angular.isNumber(d.height)?d.height:c.offsetHeight),top:Math.round(d.top+(b.pageYOffset||a[0].documentElement.scrollTop)),left:Math.round(d.left+(b.pageXOffset||a[0].documentElement.scrollLeft))}},viewportOffset:function(c,d,e){c=this.getRawNode(c),e=e!==!1;var f=c.getBoundingClientRect(),g={top:0,left:0,bottom:0,right:0},h=d?a[0].documentElement:this.scrollParent(c),i=h.getBoundingClientRect();if(g.top=i.top+h.clientTop,g.left=i.left+h.clientLeft,h===a[0].documentElement&&(g.top+=b.pageYOffset,g.left+=b.pageXOffset),g.bottom=g.top+h.clientHeight,g.right=g.left+h.clientWidth,e){var j=b.getComputedStyle(h);g.top+=this.parseStyle(j.paddingTop),g.bottom-=this.parseStyle(j.paddingBottom),g.left+=this.parseStyle(j.paddingLeft),g.right-=this.parseStyle(j.paddingRight)}return{top:Math.round(f.top-g.top),bottom:Math.round(g.bottom-f.bottom),left:Math.round(f.left-g.left),right:Math.round(g.right-f.right)}},parsePlacement:function(a){var b=f.auto.test(a);return b&&(a=a.replace(f.auto,"")),a=a.split("-"),a[0]=a[0]||"top",f.primary.test(a[0])||(a[0]="top"),a[1]=a[1]||"center",f.secondary.test(a[1])||(a[1]="center"),b?a[2]=!0:a[2]=!1,a},positionElements:function(a,c,d,e){a=this.getRawNode(a),c=this.getRawNode(c);var g=angular.isDefined(c.offsetWidth)?c.offsetWidth:c.prop("offsetWidth"),h=angular.isDefined(c.offsetHeight)?c.offsetHeight:c.prop("offsetHeight");d=this.parsePlacement(d);var i=e?this.offset(a):this.position(a),j={top:0,left:0,placement:""};if(d[2]){var k=this.viewportOffset(a,e),l=b.getComputedStyle(c),m={width:g+Math.round(Math.abs(this.parseStyle(l.marginLeft)+this.parseStyle(l.marginRight))),height:h+Math.round(Math.abs(this.parseStyle(l.marginTop)+this.parseStyle(l.marginBottom)))};if(d[0]="top"===d[0]&&m.height>k.top&&m.height<=k.bottom?"bottom":"bottom"===d[0]&&m.height>k.bottom&&m.height<=k.top?"top":"left"===d[0]&&m.width>k.left&&m.width<=k.right?"right":"right"===d[0]&&m.width>k.right&&m.width<=k.left?"left":d[0],d[1]="top"===d[1]&&m.height-i.height>k.bottom&&m.height-i.height<=k.top?"bottom":"bottom"===d[1]&&m.height-i.height>k.top&&m.height-i.height<=k.bottom?"top":"left"===d[1]&&m.width-i.width>k.right&&m.width-i.width<=k.left?"right":"right"===d[1]&&m.width-i.width>k.left&&m.width-i.width<=k.right?"left":d[1],"center"===d[1])if(f.vertical.test(d[0])){var n=i.width/2-g/2;k.left+n<0&&m.width-i.width<=k.right?d[1]="left":k.right+n<0&&m.width-i.width<=k.left&&(d[1]="right")}else{var o=i.height/2-m.height/2;k.top+o<0&&m.height-i.height<=k.bottom?d[1]="top":k.bottom+o<0&&m.height-i.height<=k.top&&(d[1]="bottom")}}switch(d[0]){case"top":j.top=i.top-h;break;case"bottom":j.top=i.top+i.height;break;case"left":j.left=i.left-g;break;case"right":j.left=i.left+i.width}switch(d[1]){case"top":j.top=i.top;break;case"bottom":j.top=i.top+i.height-h;break;case"left":j.left=i.left;break;case"right":j.left=i.left+i.width-g;break;case"center":f.vertical.test(d[0])?j.left=i.left+i.width/2-g/2:j.top=i.top+i.height/2-h/2}return j.top=Math.round(j.top),j.left=Math.round(j.left),j.placement="center"===d[1]?d[0]:d[0]+"-"+d[1],j},adjustTop:function(a,b,c,d){return-1!==a.indexOf("top")&&c!==d?{top:b.top-d+"px"}:void 0},positionArrow:function(a,c){a=this.getRawNode(a);var d=a.querySelector(".tooltip-inner, .popover-inner");if(d){var e=angular.element(d).hasClass("tooltip-inner"),g=e?a.querySelector(".tooltip-arrow"):a.querySelector(".arrow");if(g){var h={top:"",bottom:"",left:"",right:""};if(c=this.parsePlacement(c),"center"===c[1])return void angular.element(g).css(h);var i="border-"+c[0]+"-width",j=b.getComputedStyle(g)[i],k="border-";k+=f.vertical.test(c[0])?c[0]+"-"+c[1]:c[1]+"-"+c[0],k+="-radius";var l=b.getComputedStyle(e?d:a)[k];switch(c[0]){case"top":h.bottom=e?"0":"-"+j;break;case"bottom":h.top=e?"0":"-"+j;break;case"left":h.right=e?"0":"-"+j;break;case"right":h.left=e?"0":"-"+j}h[c[1]]=l,angular.element(g).css(h)}}}}}]),angular.module("ui.bootstrap.datepickerPopup",["ui.bootstrap.datepicker","ui.bootstrap.position"]).value("$datepickerPopupLiteralWarning",!0).constant("uibDatepickerPopupConfig",{altInputFormats:[],appendToBody:!1,clearText:"Clear",closeOnDateSelection:!0,closeText:"Done",currentText:"Today",datepickerPopup:"yyyy-MM-dd",datepickerPopupTemplateUrl:"uib/template/datepickerPopup/popup.html",datepickerTemplateUrl:"uib/template/datepicker/datepicker.html",html5Types:{date:"yyyy-MM-dd","datetime-local":"yyyy-MM-ddTHH:mm:ss.sss",month:"yyyy-MM"},onOpenFocus:!0,showButtonBar:!0,placement:"auto bottom-left"}).controller("UibDatepickerPopupController",["$scope","$element","$attrs","$compile","$log","$parse","$window","$document","$rootScope","$uibPosition","dateFilter","uibDateParser","uibDatepickerPopupConfig","$timeout","uibDatepickerConfig","$datepickerPopupLiteralWarning",function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p){function q(b){var c=l.parse(b,w,a.date);if(isNaN(c))for(var d=0;d
      "),C.attr({"ng-model":"date","ng-change":"dateSelection(date)","template-url":A}),D=angular.element(C.children()[0]),D.attr("template-url",B),a.datepickerOptions||(a.datepickerOptions={}),J&&"month"===c.type&&(a.datepickerOptions.datepickerMode="month",a.datepickerOptions.minMode="month"),D.attr("datepicker-options","datepickerOptions"),J?F.$formatters.push(function(b){return a.date=l.fromTimezone(b,G.timezone),b}):(F.$$parserName="date",F.$validators.date=s,F.$parsers.unshift(r),F.$formatters.push(function(b){return F.$isEmpty(b)?(a.date=b,b):(angular.isNumber(b)&&(b=new Date(b)),a.date=l.fromTimezone(b,G.timezone),l.filter(a.date,w))})),F.$viewChangeListeners.push(function(){a.date=q(F.$viewValue)}),b.on("keydown",u),H=d(C)(a),C.remove(),y?h.find("body").append(H):b.after(H),a.$on("$destroy",function(){for(a.isOpen===!0&&(i.$$phase||a.$apply(function(){a.isOpen=!1})),H.remove(),b.off("keydown",u),h.off("click",t),E&&E.off("scroll",v),angular.element(g).off("resize",v);K.length;)K.shift()()})},a.getText=function(b){return a[b+"Text"]||m[b+"Text"]},a.isDisabled=function(b){"today"===b&&(b=l.fromTimezone(new Date,G.timezone));var c={};return angular.forEach(["minDate","maxDate"],function(b){a.datepickerOptions[b]?angular.isDate(a.datepickerOptions[b])?c[b]=new Date(a.datepickerOptions[b]):(p&&e.warn("Literal date support has been deprecated, please switch to date object usage"),c[b]=new Date(k(a.datepickerOptions[b],"medium"))):c[b]=null}),a.datepickerOptions&&c.minDate&&a.compare(b,c.minDate)<0||c.maxDate&&a.compare(b,c.maxDate)>0},a.compare=function(a,b){return new Date(a.getFullYear(),a.getMonth(),a.getDate())-new Date(b.getFullYear(),b.getMonth(),b.getDate())},a.dateSelection=function(c){a.date=c;var d=a.date?l.filter(a.date,w):null;b.val(d),F.$setViewValue(d),x&&(a.isOpen=!1,b[0].focus())},a.keydown=function(c){27===c.which&&(c.stopPropagation(),a.isOpen=!1,b[0].focus())},a.select=function(b,c){if(c.stopPropagation(),"today"===b){var d=new Date;angular.isDate(a.date)?(b=new Date(a.date),b.setFullYear(d.getFullYear(),d.getMonth(),d.getDate())):(b=l.fromTimezone(d,G.timezone),b.setHours(0,0,0,0))}a.dateSelection(b)},a.close=function(c){c.stopPropagation(),a.isOpen=!1,b[0].focus()},a.disabled=angular.isDefined(c.disabled)||!1,c.ngDisabled&&K.push(a.$parent.$watch(f(c.ngDisabled),function(b){a.disabled=b})),a.$watch("isOpen",function(d){d?a.disabled?a.isOpen=!1:n(function(){v(),z&&a.$broadcast("uib:datepicker.focus"),h.on("click",t);var d=c.popupPlacement?c.popupPlacement:m.placement;y||j.parsePlacement(d)[2]?(E=E||angular.element(j.scrollParent(b)),E&&E.on("scroll",v)):E=null,angular.element(g).on("resize",v)},0,!1):(h.off("click",t),E&&E.off("scroll",v),angular.element(g).off("resize",v))}),a.$on("uib:datepicker.mode",function(){n(v,0,!1)})}]).directive("uibDatepickerPopup",function(){return{require:["ngModel","uibDatepickerPopup"],controller:"UibDatepickerPopupController",scope:{datepickerOptions:"=?",isOpen:"=?",currentText:"@",clearText:"@",closeText:"@"},link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibDatepickerPopupWrap",function(){return{restrict:"A",transclude:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/datepickerPopup/popup.html"}}}),angular.module("ui.bootstrap.debounce",[]).factory("$$debounce",["$timeout",function(a){return function(b,c){var d;return function(){var e=this,f=Array.prototype.slice.call(arguments);d&&a.cancel(d),d=a(function(){b.apply(e,f)},c)}}}]),angular.module("ui.bootstrap.multiMap",[]).factory("$$multiMap",function(){return{createNew:function(){var a={};return{entries:function(){return Object.keys(a).map(function(b){return{key:b,value:a[b]}})},get:function(b){return a[b]},hasKey:function(b){return!!a[b]},keys:function(){return Object.keys(a)},put:function(b,c){a[b]||(a[b]=[]),a[b].push(c)},remove:function(b,c){var d=a[b];if(d){var e=d.indexOf(c);-1!==e&&d.splice(e,1),d.length||delete a[b]}}}}}}),angular.module("ui.bootstrap.dropdown",["ui.bootstrap.multiMap","ui.bootstrap.position"]).constant("uibDropdownConfig",{appendToOpenClass:"uib-dropdown-open",openClass:"open"}).service("uibDropdownService",["$document","$rootScope","$$multiMap",function(a,b,c){var d=null,e=c.createNew();this.isOnlyOpen=function(a,b){var c=e.get(b);if(c){var d=c.reduce(function(b,c){return c.scope===a?c:b},{});if(d)return 1===c.length}return!1},this.open=function(b,c,g){if(d||a.on("click",f),d&&d!==b&&(d.isOpen=!1),d=b,g){var h=e.get(g);if(h){var i=h.map(function(a){return a.scope});-1===i.indexOf(b)&&e.put(g,{scope:b})}else e.put(g,{scope:b})}},this.close=function(b,c,g){if(d===b&&(a.off("click",f),a.off("keydown",this.keybindFilter),d=null),g){var h=e.get(g);if(h){var i=h.reduce(function(a,c){return c.scope===b?c:a},{});i&&e.remove(g,i)}}};var f=function(a){if(d&&!(a&&"disabled"===d.getAutoClose()||a&&3===a.which)){var c=d.getToggleElement();if(!(a&&c&&c[0].contains(a.target))){var e=d.getDropdownElement();a&&"outsideClick"===d.getAutoClose()&&e&&e[0].contains(a.target)||(d.focusToggleElement(),d.isOpen=!1,b.$$phase||d.$apply())}}};this.keybindFilter=function(a){if(d){var b=d.getDropdownElement(),c=d.getToggleElement(),e=b&&b[0].contains(a.target),g=c&&c[0].contains(a.target);27===a.which?(a.stopPropagation(),d.focusToggleElement(),f()):d.isKeynavEnabled()&&-1!==[38,40].indexOf(a.which)&&d.isOpen&&(e||g)&&(a.preventDefault(),a.stopPropagation(),d.focusDropdownEntry(a.which))}}}]).controller("UibDropdownController",["$scope","$element","$attrs","$parse","uibDropdownConfig","uibDropdownService","$animate","$uibPosition","$document","$compile","$templateRequest",function(a,b,c,d,e,f,g,h,i,j,k){var l,m,n=this,o=a.$new(),p=e.appendToOpenClass,q=e.openClass,r=angular.noop,s=c.onToggle?d(c.onToggle):angular.noop,t=!1,u=null,v=!1,w=i.find("body");b.addClass("dropdown"),this.init=function(){if(c.isOpen&&(m=d(c.isOpen),r=m.assign,a.$watch(m,function(a){o.isOpen=!!a})),angular.isDefined(c.dropdownAppendTo)){var e=d(c.dropdownAppendTo)(o);e&&(u=angular.element(e))}t=angular.isDefined(c.dropdownAppendToBody),v=angular.isDefined(c.keyboardNav),t&&!u&&(u=w),u&&n.dropdownMenu&&(u.append(n.dropdownMenu),b.on("$destroy",function(){n.dropdownMenu.remove()}))},this.toggle=function(a){return o.isOpen=arguments.length?!!a:!o.isOpen,angular.isFunction(r)&&r(o,o.isOpen),o.isOpen},this.isOpen=function(){return o.isOpen},o.getToggleElement=function(){return n.toggleElement},o.getAutoClose=function(){return c.autoClose||"always"},o.getElement=function(){return b},o.isKeynavEnabled=function(){return v},o.focusDropdownEntry=function(a){var c=n.dropdownMenu?angular.element(n.dropdownMenu).find("a"):b.find("ul").eq(0).find("a");switch(a){case 40:angular.isNumber(n.selectedOption)?n.selectedOption=n.selectedOption===c.length-1?n.selectedOption:n.selectedOption+1:n.selectedOption=0;break;case 38:angular.isNumber(n.selectedOption)?n.selectedOption=0===n.selectedOption?0:n.selectedOption-1:n.selectedOption=c.length-1}c[n.selectedOption].focus()},o.getDropdownElement=function(){return n.dropdownMenu},o.focusToggleElement=function(){n.toggleElement&&n.toggleElement[0].focus()},o.$watch("isOpen",function(c,d){if(u&&n.dropdownMenu){var e,m,v,w=h.positionElements(b,n.dropdownMenu,"bottom-left",!0),x=0;if(e={top:w.top+"px",display:c?"block":"none"},m=n.dropdownMenu.hasClass("dropdown-menu-right"),m?(e.left="auto",v=h.scrollbarPadding(u),v.heightOverflow&&v.scrollbarWidth&&(x=v.scrollbarWidth),e.right=window.innerWidth-x-(w.left+b.prop("offsetWidth"))+"px"):(e.left=w.left+"px",e.right="auto"),!t){var y=h.offset(u);e.top=w.top-y.top+"px",m?e.right=window.innerWidth-(w.left-y.left+b.prop("offsetWidth"))+"px":e.left=w.left-y.left+"px"}n.dropdownMenu.css(e)}var z=u?u:b,A=u?p:q,B=z.hasClass(A),C=f.isOnlyOpen(a,u);if(B===!c){var D;D=u?C?"removeClass":"addClass":c?"addClass":"removeClass",g[D](z,A).then(function(){angular.isDefined(c)&&c!==d&&s(a,{open:!!c})})}if(c)n.dropdownMenuTemplateUrl?k(n.dropdownMenuTemplateUrl).then(function(a){l=o.$new(),j(a.trim())(l,function(a){var b=a;n.dropdownMenu.replaceWith(b),n.dropdownMenu=b,i.on("keydown",f.keybindFilter)})}):i.on("keydown",f.keybindFilter),o.focusToggleElement(),f.open(o,b,u);else{if(f.close(o,b,u),n.dropdownMenuTemplateUrl){l&&l.$destroy();var E=angular.element('');n.dropdownMenu.replaceWith(E),n.dropdownMenu=E}n.selectedOption=null}angular.isFunction(r)&&r(a,c)})}]).directive("uibDropdown",function(){return{controller:"UibDropdownController",link:function(a,b,c,d){d.init()}}}).directive("uibDropdownMenu",function(){return{restrict:"A",require:"?^uibDropdown",link:function(a,b,c,d){if(d&&!angular.isDefined(c.dropdownNested)){b.addClass("dropdown-menu");var e=c.templateUrl;e&&(d.dropdownMenuTemplateUrl=e),d.dropdownMenu||(d.dropdownMenu=b)}}}}).directive("uibDropdownToggle",function(){return{require:"?^uibDropdown",link:function(a,b,c,d){if(d){b.addClass("dropdown-toggle"),d.toggleElement=b;var e=function(e){e.preventDefault(),b.hasClass("disabled")||c.disabled||a.$apply(function(){d.toggle()})};b.on("click",e),b.attr({"aria-haspopup":!0,"aria-expanded":!1}),a.$watch(d.isOpen,function(a){b.attr("aria-expanded",!!a)}),a.$on("$destroy",function(){b.off("click",e)})}}}}),angular.module("ui.bootstrap.stackedMap",[]).factory("$$stackedMap",function(){return{createNew:function(){var a=[];return{add:function(b,c){a.push({key:b,value:c})},get:function(b){for(var c=0;c-1&&A>a&&(a=A),a}function m(a,b){var c=x.get(a).value,d=c.appendTo;x.remove(a),B=x.top(),B&&(A=parseInt(B.value.modalDomEl.attr("index"),10)),p(c.modalDomEl,c.modalScope,function(){var b=c.openedClass||w;y.remove(b,a);var e=y.hasKey(b);d.toggleClass(b,e),!e&&v&&v.heightOverflow&&v.scrollbarWidth&&(v.originalRight?d.css({paddingRight:v.originalRight+"px"}):d.css({paddingRight:""}),v=null),n(!0)},c.closedDeferred),o(),b&&b.focus?b.focus():d.focus&&d.focus()}function n(a){var b;x.length()>0&&(b=x.top().value,b.modalDomEl.toggleClass(b.windowTopClass||"",a))}function o(){if(t&&-1===l()){var a=u;p(t,u,function(){a=null}),t=void 0,u=void 0}}function p(b,c,d,e){function g(){g.done||(g.done=!0,a.leave(b).then(function(){d&&d(),b.remove(),e&&e.resolve()}),c.$destroy())}var h,i=null,j=function(){return h||(h=f.defer(),i=h.promise),function(){h.resolve()}};return c.$broadcast(z.NOW_CLOSING_EVENT,j),f.when(i).then(g)}function q(a){if(a.isDefaultPrevented())return a;var b=x.top();if(b)switch(a.which){case 27:b.value.keyboard&&(a.preventDefault(),e.$apply(function(){z.dismiss(b.key,"escape key press")}));break;case 9:var c=z.loadFocusElementList(b),d=!1;a.shiftKey?(z.isFocusInFirstItem(a,c)||z.isModalFocused(a,b))&&(d=z.focusLastFocusableElement(c)):z.isFocusInLastItem(a,c)&&(d=z.focusFirstFocusableElement(c)),d&&(a.preventDefault(),a.stopPropagation())}}function r(a,b,c){return!a.value.modalScope.$broadcast("modal.closing",b,c).defaultPrevented}function s(){Array.prototype.forEach.call(document.querySelectorAll("["+C+"]"),function(a){var b=parseInt(a.getAttribute(C),10),c=b-1;a.setAttribute(C,c),c||(a.removeAttribute(C),a.removeAttribute("aria-hidden"))})}var t,u,v,w="modal-open",x=h.createNew(),y=g.createNew(),z={NOW_CLOSING_EVENT:"modal.stack.now-closing"},A=0,B=null,C="data-bootstrap-modal-aria-hidden-count",D="a[href], area[href], input:not([disabled]):not([tabindex='-1']), button:not([disabled]):not([tabindex='-1']),select:not([disabled]):not([tabindex='-1']), textarea:not([disabled]):not([tabindex='-1']), iframe, object, embed, *[tabindex]:not([tabindex='-1']), *[contenteditable=true]",E=/[A-Z]/g;return e.$watch(l,function(a){u&&(u.index=a)}),c.on("keydown",q),e.$on("$destroy",function(){c.off("keydown",q)}),z.open=function(b,f){function g(a){function b(a){var b=a.parent()?a.parent().children():[];return Array.prototype.filter.call(b,function(b){return b!==a[0]})}if(a&&"BODY"!==a[0].tagName)return b(a).forEach(function(a){var b="true"===a.getAttribute("aria-hidden"),c=parseInt(a.getAttribute(C),10);c||(c=b?1:0),a.setAttribute(C,c+1),a.setAttribute("aria-hidden","true")}),g(a.parent())}var h=c[0].activeElement,k=f.openedClass||w;n(!1),B=x.top(),x.add(b,{deferred:f.deferred,renderDeferred:f.renderDeferred,closedDeferred:f.closedDeferred,modalScope:f.scope,backdrop:f.backdrop,keyboard:f.keyboard,openedClass:f.openedClass,windowTopClass:f.windowTopClass,animation:f.animation,appendTo:f.appendTo}),y.put(k,b);var m=f.appendTo,o=l();if(!m.length)throw new Error("appendTo element not found. Make sure that the element passed is in DOM.");o>=0&&!t&&(u=e.$new(!0),u.modalOptions=f,u.index=o,t=angular.element('
      '),t.attr({"class":"modal-backdrop","ng-style":"{'z-index': 1040 + (index && 1 || 0) + index*10}","uib-modal-animation-class":"fade","modal-in-class":"in"}),f.backdropClass&&t.addClass(f.backdropClass),f.animation&&t.attr("modal-animation","true"),d(t)(u),a.enter(t,m),i.isScrollable(m)&&(v=i.scrollbarPadding(m),v.heightOverflow&&v.scrollbarWidth&&m.css({paddingRight:v.right+"px"})));var p;f.component?(p=document.createElement(j(f.component.name)),p=angular.element(p),p.attr({resolve:"$resolve","modal-instance":"$uibModalInstance",close:"$close($value)",dismiss:"$dismiss($value)"})):p=f.content,A=B?parseInt(B.value.modalDomEl.attr("index"),10)+1:0;var q=angular.element('
      ');q.attr({"class":"modal","template-url":f.windowTemplateUrl,"window-top-class":f.windowTopClass,role:"dialog","aria-labelledby":f.ariaLabelledBy,"aria-describedby":f.ariaDescribedBy,size:f.size,index:A,animate:"animate","ng-style":"{'z-index': 1050 + $$topModalIndex*10, display: 'block'}",tabindex:-1,"uib-modal-animation-class":"fade","modal-in-class":"in"}).append(p),f.windowClass&&q.addClass(f.windowClass),f.animation&&q.attr("modal-animation","true"),m.addClass(k),f.scope&&(f.scope.$$topModalIndex=A),a.enter(d(q)(f.scope),m),x.top().value.modalDomEl=q,x.top().value.modalOpener=h,g(q)},z.close=function(a,b){var c=x.get(a);return s(),c&&r(c,b,!0)?(c.value.modalScope.$$uibDestructionScheduled=!0,c.value.deferred.resolve(b),m(a,c.value.modalOpener),!0):!c},z.dismiss=function(a,b){var c=x.get(a);return s(),c&&r(c,b,!1)?(c.value.modalScope.$$uibDestructionScheduled=!0,c.value.deferred.reject(b),m(a,c.value.modalOpener),!0):!c},z.dismissAll=function(a){for(var b=this.getTop();b&&this.dismiss(b.key,a);)b=this.getTop()},z.getTop=function(){return x.top()},z.modalRendered=function(a){var b=x.get(a);b&&b.value.renderDeferred.resolve()},z.focusFirstFocusableElement=function(a){return a.length>0?(a[0].focus(),!0):!1},z.focusLastFocusableElement=function(a){return a.length>0?(a[a.length-1].focus(),!0):!1},z.isModalFocused=function(a,b){if(a&&b){var c=b.value.modalDomEl;if(c&&c.length)return(a.target||a.srcElement)===c[0]}return!1},z.isFocusInFirstItem=function(a,b){return b.length>0?(a.target||a.srcElement)===b[0]:!1},z.isFocusInLastItem=function(a,b){return b.length>0?(a.target||a.srcElement)===b[b.length-1]:!1},z.loadFocusElementList=function(a){if(a){var b=a.value.modalDomEl;if(b&&b.length){var c=b[0].querySelectorAll(D);return c?Array.prototype.filter.call(c,function(a){return k(a)}):c}}},z}]).provider("$uibModal",function(){var a={options:{animation:!0,backdrop:!0,keyboard:!0},$get:["$rootScope","$q","$document","$templateRequest","$controller","$uibResolve","$uibModalStack",function(b,c,d,e,f,g,h){function i(a){return a.template?c.when(a.template):e(angular.isFunction(a.templateUrl)?a.templateUrl():a.templateUrl)}var j={},k=null;return j.getPromiseChain=function(){return k},j.open=function(e){function j(){return q}var l=c.defer(),m=c.defer(),n=c.defer(),o=c.defer(),p={result:l.promise,opened:m.promise,closed:n.promise,rendered:o.promise,close:function(a){return h.close(p,a)},dismiss:function(a){return h.dismiss(p,a)}};if(e=angular.extend({},a.options,e),e.resolve=e.resolve||{},e.appendTo=e.appendTo||d.find("body").eq(0),!e.component&&!e.template&&!e.templateUrl)throw new Error("One of component or template or templateUrl options is required.");var q;q=e.component?c.when(g.resolve(e.resolve,{},null,null)):c.all([i(e),g.resolve(e.resolve,{},null,null)]);var r;return r=k=c.all([k]).then(j,j).then(function(a){function c(b,c,d,e){b.$scope=g,b.$scope.$resolve={},d?b.$scope.$uibModalInstance=p:b.$uibModalInstance=p;var f=c?a[1]:a;angular.forEach(f,function(a,c){e&&(b[c]=a),b.$scope.$resolve[c]=a})}var d=e.scope||b,g=d.$new();g.$close=p.close,g.$dismiss=p.dismiss,g.$on("$destroy",function(){g.$$uibDestructionScheduled||g.$dismiss("$uibUnscheduledDestruction")});var i,j,k={scope:g,deferred:l,renderDeferred:o,closedDeferred:n,animation:e.animation,backdrop:e.backdrop,keyboard:e.keyboard,backdropClass:e.backdropClass,windowTopClass:e.windowTopClass,windowClass:e.windowClass,windowTemplateUrl:e.windowTemplateUrl,ariaLabelledBy:e.ariaLabelledBy,ariaDescribedBy:e.ariaDescribedBy,size:e.size,openedClass:e.openedClass,appendTo:e.appendTo},q={},r={};e.component?(c(q,!1,!0,!1),q.name=e.component,k.component=q):e.controller&&(c(r,!0,!1,!0),j=f(e.controller,r,!0,e.controllerAs),e.controllerAs&&e.bindToController&&(i=j.instance,i.$close=g.$close,i.$dismiss=g.$dismiss,angular.extend(i,{$resolve:r.$scope.$resolve},d)),i=j(),angular.isFunction(i.$onInit)&&i.$onInit()),e.component||(k.content=a[0]),h.open(p,k),m.resolve(!0)},function(a){m.reject(a),l.reject(a)})["finally"](function(){k===r&&(k=null)}),p},j}]};return a}),angular.module("ui.bootstrap.paging",[]).factory("uibPaging",["$parse",function(a){return{create:function(b,c,d){b.setNumPages=d.numPages?a(d.numPages).assign:angular.noop,b.ngModelCtrl={$setViewValue:angular.noop},b._watchers=[],b.init=function(a,e){b.ngModelCtrl=a,b.config=e,a.$render=function(){b.render()},d.itemsPerPage?b._watchers.push(c.$parent.$watch(d.itemsPerPage,function(a){b.itemsPerPage=parseInt(a,10),c.totalPages=b.calculateTotalPages(),b.updatePage()})):b.itemsPerPage=e.itemsPerPage,c.$watch("totalItems",function(a,d){(angular.isDefined(a)||a!==d)&&(c.totalPages=b.calculateTotalPages(),b.updatePage())})},b.calculateTotalPages=function(){var a=b.itemsPerPage<1?1:Math.ceil(c.totalItems/b.itemsPerPage);return Math.max(a||0,1)},b.render=function(){c.page=parseInt(b.ngModelCtrl.$viewValue,10)||1},c.selectPage=function(a,d){d&&d.preventDefault();var e=!c.ngDisabled||!d;e&&c.page!==a&&a>0&&a<=c.totalPages&&(d&&d.target&&d.target.blur(),b.ngModelCtrl.$setViewValue(a), +b.ngModelCtrl.$render())},c.getText=function(a){return c[a+"Text"]||b.config[a+"Text"]},c.noPrevious=function(){return 1===c.page},c.noNext=function(){return c.page===c.totalPages},b.updatePage=function(){b.setNumPages(c.$parent,c.totalPages),c.page>c.totalPages?c.selectPage(c.totalPages):b.ngModelCtrl.$render()},c.$on("$destroy",function(){for(;b._watchers.length;)b._watchers.shift()()})}}}]),angular.module("ui.bootstrap.pager",["ui.bootstrap.paging","ui.bootstrap.tabindex"]).controller("UibPagerController",["$scope","$attrs","uibPaging","uibPagerConfig",function(a,b,c,d){a.align=angular.isDefined(b.align)?a.$parent.$eval(b.align):d.align,c.create(this,a,b)}]).constant("uibPagerConfig",{itemsPerPage:10,previousText:"« Previous",nextText:"Next »",align:!0}).directive("uibPager",["uibPagerConfig",function(a){return{scope:{totalItems:"=",previousText:"@",nextText:"@",ngDisabled:"="},require:["uibPager","?ngModel"],restrict:"A",controller:"UibPagerController",controllerAs:"pager",templateUrl:function(a,b){return b.templateUrl||"uib/template/pager/pager.html"},link:function(b,c,d,e){c.addClass("pager");var f=e[0],g=e[1];g&&f.init(g,a)}}}]),angular.module("ui.bootstrap.pagination",["ui.bootstrap.paging","ui.bootstrap.tabindex"]).controller("UibPaginationController",["$scope","$attrs","$parse","uibPaging","uibPaginationConfig",function(a,b,c,d,e){function f(a,b,c){return{number:a,text:b,active:c}}function g(a,b){var c=[],d=1,e=b,g=angular.isDefined(i)&&b>i;g&&(j?(d=Math.max(a-Math.floor(i/2),1),e=d+i-1,e>b&&(e=b,d=e-i+1)):(d=(Math.ceil(a/i)-1)*i+1,e=Math.min(d+i-1,b)));for(var h=d;e>=h;h++){var n=f(h,m(h),h===a);c.push(n)}if(g&&i>0&&(!j||k||l)){if(d>1){if(!l||d>3){var o=f(d-1,"...",!1);c.unshift(o)}if(l){if(3===d){var p=f(2,"2",!1);c.unshift(p)}var q=f(1,"1",!1);c.unshift(q)}}if(b>e){if(!l||b-2>e){var r=f(e+1,"...",!1);c.push(r)}if(l){if(e===b-2){var s=f(b-1,b-1,!1);c.push(s)}var t=f(b,b,!1);c.push(t)}}}return c}var h=this,i=angular.isDefined(b.maxSize)?a.$parent.$eval(b.maxSize):e.maxSize,j=angular.isDefined(b.rotate)?a.$parent.$eval(b.rotate):e.rotate,k=angular.isDefined(b.forceEllipses)?a.$parent.$eval(b.forceEllipses):e.forceEllipses,l=angular.isDefined(b.boundaryLinkNumbers)?a.$parent.$eval(b.boundaryLinkNumbers):e.boundaryLinkNumbers,m=angular.isDefined(b.pageLabel)?function(c){return a.$parent.$eval(b.pageLabel,{$page:c})}:angular.identity;a.boundaryLinks=angular.isDefined(b.boundaryLinks)?a.$parent.$eval(b.boundaryLinks):e.boundaryLinks,a.directionLinks=angular.isDefined(b.directionLinks)?a.$parent.$eval(b.directionLinks):e.directionLinks,d.create(this,a,b),b.maxSize&&h._watchers.push(a.$parent.$watch(c(b.maxSize),function(a){i=parseInt(a,10),h.render()}));var n=this.render;this.render=function(){n(),a.page>0&&a.page<=a.totalPages&&(a.pages=g(a.page,a.totalPages))}}]).constant("uibPaginationConfig",{itemsPerPage:10,boundaryLinks:!1,boundaryLinkNumbers:!1,directionLinks:!0,firstText:"First",previousText:"Previous",nextText:"Next",lastText:"Last",rotate:!0,forceEllipses:!1}).directive("uibPagination",["$parse","uibPaginationConfig",function(a,b){return{scope:{totalItems:"=",firstText:"@",previousText:"@",nextText:"@",lastText:"@",ngDisabled:"="},require:["uibPagination","?ngModel"],restrict:"A",controller:"UibPaginationController",controllerAs:"pagination",templateUrl:function(a,b){return b.templateUrl||"uib/template/pagination/pagination.html"},link:function(a,c,d,e){c.addClass("pagination");var f=e[0],g=e[1];g&&f.init(g,b)}}}]),angular.module("ui.bootstrap.tooltip",["ui.bootstrap.position","ui.bootstrap.stackedMap"]).provider("$uibTooltip",function(){function a(a){var b=/[A-Z]/g,c="-";return a.replace(b,function(a,b){return(b?c:"")+a.toLowerCase()})}var b={placement:"top",placementClassPrefix:"",animation:!0,popupDelay:0,popupCloseDelay:0,useContentExp:!1},c={mouseenter:"mouseleave",click:"click",outsideClick:"outsideClick",focus:"blur",none:""},d={};this.options=function(a){angular.extend(d,a)},this.setTriggers=function(a){angular.extend(c,a)},this.$get=["$window","$compile","$timeout","$document","$uibPosition","$interpolate","$rootScope","$parse","$$stackedMap",function(e,f,g,h,i,j,k,l,m){function n(a){if(27===a.which){var b=o.top();b&&(b.value.close(),b=null)}}var o=m.createNew();return h.on("keyup",n),k.$on("$destroy",function(){h.off("keyup",n)}),function(e,k,m,n){function p(a){var b=(a||n.trigger||m).split(" "),d=b.map(function(a){return c[a]||a});return{show:b,hide:d}}n=angular.extend({},b,d,n);var q=a(e),r=j.startSymbol(),s=j.endSymbol(),t="
      ';return{compile:function(a,b){var c=f(t);return function(a,b,d,f){function j(){O.isOpen?q():m()}function m(){N&&!a.$eval(d[k+"Enable"])||(u(),x(),O.popupDelay?G||(G=g(r,O.popupDelay,!1)):r())}function q(){s(),O.popupCloseDelay?H||(H=g(t,O.popupCloseDelay,!1)):t()}function r(){return s(),u(),O.content?(v(),void O.$evalAsync(function(){O.isOpen=!0,y(!0),T()})):angular.noop}function s(){G&&(g.cancel(G),G=null),I&&(g.cancel(I),I=null)}function t(){O&&O.$evalAsync(function(){O&&(O.isOpen=!1,y(!1),O.animation?F||(F=g(w,150,!1)):w())})}function u(){H&&(g.cancel(H),H=null),F&&(g.cancel(F),F=null)}function v(){D||(E=O.$new(),D=c(E,function(a){L?h.find("body").append(a):b.after(a)}),o.add(O,{close:t}),z())}function w(){s(),u(),A(),D&&(D.remove(),D=null,J&&g.cancel(J)),o.remove(O),E&&(E.$destroy(),E=null)}function x(){O.title=d[k+"Title"],R?O.content=R(a):O.content=d[e],O.popupClass=d[k+"Class"],O.placement=angular.isDefined(d[k+"Placement"])?d[k+"Placement"]:n.placement;var b=i.parsePlacement(O.placement);K=b[1]?b[0]+"-"+b[1]:b[0];var c=parseInt(d[k+"PopupDelay"],10),f=parseInt(d[k+"PopupCloseDelay"],10);O.popupDelay=isNaN(c)?n.popupDelay:c,O.popupCloseDelay=isNaN(f)?n.popupCloseDelay:f}function y(b){Q&&angular.isFunction(Q.assign)&&Q.assign(a,b)}function z(){S.length=0,R?(S.push(a.$watch(R,function(a){O.content=a,!a&&O.isOpen&&t()})),S.push(E.$watch(function(){P||(P=!0,E.$$postDigest(function(){P=!1,O&&O.isOpen&&T()}))}))):S.push(d.$observe(e,function(a){O.content=a,!a&&O.isOpen?t():T()})),S.push(d.$observe(k+"Title",function(a){O.title=a,O.isOpen&&T()})),S.push(d.$observe(k+"Placement",function(a){O.placement=a?a:n.placement,O.isOpen&&T()}))}function A(){S.length&&(angular.forEach(S,function(a){a()}),S.length=0)}function B(a){O&&O.isOpen&&D&&(b[0].contains(a.target)||D[0].contains(a.target)||q())}function C(){var c=[],e=[],f=a.$eval(d[k+"Trigger"]);U(),angular.isObject(f)?(Object.keys(f).forEach(function(a){c.push(a),e.push(f[a])}),M={show:c,hide:e}):M=p(f),"none"!==M.show&&M.show.forEach(function(a,c){"outsideClick"===a?(b.on("click",j),h.on("click",B)):a===M.hide[c]?b.on(a,j):a&&(b.on(a,m),b.on(M.hide[c],q)),b.on("keypress",function(a){27===a.which&&q()})})}var D,E,F,G,H,I,J,K,L=angular.isDefined(n.appendToBody)?n.appendToBody:!1,M=p(void 0),N=angular.isDefined(d[k+"Enable"]),O=a.$new(!0),P=!1,Q=angular.isDefined(d[k+"IsOpen"])?l(d[k+"IsOpen"]):!1,R=n.useContentExp?l(d[e]):!1,S=[],T=function(){D&&D.html()&&(I||(I=g(function(){var a=i.positionElements(b,D,O.placement,L),c=angular.isDefined(D.offsetHeight)?D.offsetHeight:D.prop("offsetHeight"),d=L?i.offset(b):i.position(b);D.css({top:a.top+"px",left:a.left+"px"});var e=a.placement.split("-");D.hasClass(e[0])||(D.removeClass(K.split("-")[0]),D.addClass(e[0])),D.hasClass(n.placementClassPrefix+a.placement)||(D.removeClass(n.placementClassPrefix+K),D.addClass(n.placementClassPrefix+a.placement)),J=g(function(){var a=angular.isDefined(D.offsetHeight)?D.offsetHeight:D.prop("offsetHeight"),b=i.adjustTop(e,d,c,a);b&&D.css(b),J=null},0,!1),D.hasClass("uib-position-measure")?(i.positionArrow(D,a.placement),D.removeClass("uib-position-measure")):K!==a.placement&&i.positionArrow(D,a.placement),K=a.placement,I=null},0,!1)))};O.origScope=a,O.isOpen=!1,O.contentExp=function(){return O.content},d.$observe("disabled",function(a){a&&s(),a&&O.isOpen&&t()}),Q&&a.$watch(Q,function(a){O&&!a===O.isOpen&&j()});var U=function(){M.show.forEach(function(a){"outsideClick"===a?b.off("click",j):(b.off(a,m),b.off(a,j))}),M.hide.forEach(function(a){"outsideClick"===a?h.off("click",B):b.off(a,q)})};C();var V=a.$eval(d[k+"Animation"]);O.animation=angular.isDefined(V)?!!V:n.animation;var W,X=k+"AppendToBody";W=X in d&&void 0===d[X]?!0:a.$eval(d[X]),L=angular.isDefined(W)?W:L,a.$on("$destroy",function(){U(),w(),O=null})}}}}}]}).directive("uibTooltipTemplateTransclude",["$animate","$sce","$compile","$templateRequest",function(a,b,c,d){return{link:function(e,f,g){var h,i,j,k=e.$eval(g.tooltipTemplateTranscludeScope),l=0,m=function(){i&&(i.remove(),i=null),h&&(h.$destroy(),h=null),j&&(a.leave(j).then(function(){i=null}),i=j,j=null)};e.$watch(b.parseAsResourceUrl(g.uibTooltipTemplateTransclude),function(b){var g=++l;b?(d(b,!0).then(function(d){if(g===l){var e=k.$new(),i=d,n=c(i)(e,function(b){m(),a.enter(b,f)});h=e,j=n,h.$emit("$includeContentLoaded",b)}},function(){g===l&&(m(),e.$emit("$includeContentError",b))}),e.$emit("$includeContentRequested",b)):m()}),e.$on("$destroy",m)}}}]).directive("uibTooltipClasses",["$uibPosition",function(a){return{restrict:"A",link:function(b,c,d){if(b.placement){var e=a.parsePlacement(b.placement);c.addClass(e[0])}b.popupClass&&c.addClass(b.popupClass),b.animation&&c.addClass(d.tooltipAnimationClass)}}}]).directive("uibTooltipPopup",function(){return{restrict:"A",scope:{content:"@"},templateUrl:"uib/template/tooltip/tooltip-popup.html"}}).directive("uibTooltip",["$uibTooltip",function(a){return a("uibTooltip","tooltip","mouseenter")}]).directive("uibTooltipTemplatePopup",function(){return{restrict:"A",scope:{contentExp:"&",originScope:"&"},templateUrl:"uib/template/tooltip/tooltip-template-popup.html"}}).directive("uibTooltipTemplate",["$uibTooltip",function(a){return a("uibTooltipTemplate","tooltip","mouseenter",{useContentExp:!0})}]).directive("uibTooltipHtmlPopup",function(){return{restrict:"A",scope:{contentExp:"&"},templateUrl:"uib/template/tooltip/tooltip-html-popup.html"}}).directive("uibTooltipHtml",["$uibTooltip",function(a){return a("uibTooltipHtml","tooltip","mouseenter",{useContentExp:!0})}]),angular.module("ui.bootstrap.popover",["ui.bootstrap.tooltip"]).directive("uibPopoverTemplatePopup",function(){return{restrict:"A",scope:{uibTitle:"@",contentExp:"&",originScope:"&"},templateUrl:"uib/template/popover/popover-template.html"}}).directive("uibPopoverTemplate",["$uibTooltip",function(a){return a("uibPopoverTemplate","popover","click",{useContentExp:!0})}]).directive("uibPopoverHtmlPopup",function(){return{restrict:"A",scope:{contentExp:"&",uibTitle:"@"},templateUrl:"uib/template/popover/popover-html.html"}}).directive("uibPopoverHtml",["$uibTooltip",function(a){return a("uibPopoverHtml","popover","click",{useContentExp:!0})}]).directive("uibPopoverPopup",function(){return{restrict:"A",scope:{uibTitle:"@",content:"@"},templateUrl:"uib/template/popover/popover.html"}}).directive("uibPopover",["$uibTooltip",function(a){return a("uibPopover","popover","click")}]),angular.module("ui.bootstrap.progressbar",[]).constant("uibProgressConfig",{animate:!0,max:100}).controller("UibProgressController",["$scope","$attrs","uibProgressConfig",function(a,b,c){function d(){return angular.isDefined(a.maxParam)?a.maxParam:c.max}var e=this,f=angular.isDefined(b.animate)?a.$parent.$eval(b.animate):c.animate;this.bars=[],a.max=d(),this.addBar=function(a,b,c){f||b.css({transition:"none"}),this.bars.push(a),a.max=d(),a.title=c&&angular.isDefined(c.title)?c.title:"progressbar",a.$watch("value",function(b){a.recalculatePercentage()}),a.recalculatePercentage=function(){var b=e.bars.reduce(function(a,b){return b.percent=+(100*b.value/b.max).toFixed(2),a+b.percent},0);b>100&&(a.percent-=b-100)},a.$on("$destroy",function(){b=null,e.removeBar(a)})},this.removeBar=function(a){this.bars.splice(this.bars.indexOf(a),1),this.bars.forEach(function(a){a.recalculatePercentage()})},a.$watch("maxParam",function(a){e.bars.forEach(function(a){a.max=d(),a.recalculatePercentage()})})}]).directive("uibProgress",function(){return{replace:!0,transclude:!0,controller:"UibProgressController",require:"uibProgress",scope:{maxParam:"=?max"},templateUrl:"uib/template/progressbar/progress.html"}}).directive("uibBar",function(){return{replace:!0,transclude:!0,require:"^uibProgress",scope:{value:"=",type:"@"},templateUrl:"uib/template/progressbar/bar.html",link:function(a,b,c,d){d.addBar(a,b,c)}}}).directive("uibProgressbar",function(){return{replace:!0,transclude:!0,controller:"UibProgressController",scope:{value:"=",maxParam:"=?max",type:"@"},templateUrl:"uib/template/progressbar/progressbar.html",link:function(a,b,c,d){d.addBar(a,angular.element(b.children()[0]),{title:c.title})}}}),angular.module("ui.bootstrap.rating",[]).constant("uibRatingConfig",{max:5,stateOn:null,stateOff:null,enableReset:!0,titles:["one","two","three","four","five"]}).controller("UibRatingController",["$scope","$attrs","uibRatingConfig",function(a,b,c){var d={$setViewValue:angular.noop},e=this;this.init=function(e){d=e,d.$render=this.render,d.$formatters.push(function(a){return angular.isNumber(a)&&a<<0!==a&&(a=Math.round(a)),a}),this.stateOn=angular.isDefined(b.stateOn)?a.$parent.$eval(b.stateOn):c.stateOn,this.stateOff=angular.isDefined(b.stateOff)?a.$parent.$eval(b.stateOff):c.stateOff,this.enableReset=angular.isDefined(b.enableReset)?a.$parent.$eval(b.enableReset):c.enableReset;var f=angular.isDefined(b.titles)?a.$parent.$eval(b.titles):c.titles;this.titles=angular.isArray(f)&&f.length>0?f:c.titles;var g=angular.isDefined(b.ratingStates)?a.$parent.$eval(b.ratingStates):new Array(angular.isDefined(b.max)?a.$parent.$eval(b.max):c.max);a.range=this.buildTemplateObjects(g)},this.buildTemplateObjects=function(a){for(var b=0,c=a.length;c>b;b++)a[b]=angular.extend({index:b},{stateOn:this.stateOn,stateOff:this.stateOff,title:this.getTitle(b)},a[b]);return a},this.getTitle=function(a){return a>=this.titles.length?a+1:this.titles[a]},a.rate=function(b){if(!a.readonly&&b>=0&&b<=a.range.length){var c=e.enableReset&&d.$viewValue===b?0:b;d.$setViewValue(c),d.$render()}},a.enter=function(b){a.readonly||(a.value=b),a.onHover({value:b})},a.reset=function(){a.value=d.$viewValue,a.onLeave()},a.onKeydown=function(b){/(37|38|39|40)/.test(b.which)&&(b.preventDefault(),b.stopPropagation(),a.rate(a.value+(38===b.which||39===b.which?1:-1)))},this.render=function(){a.value=d.$viewValue,a.title=e.getTitle(a.value-1)}}]).directive("uibRating",function(){return{require:["uibRating","ngModel"],restrict:"A",scope:{readonly:"=?readOnly",onHover:"&",onLeave:"&"},controller:"UibRatingController",templateUrl:"uib/template/rating/rating.html",link:function(a,b,c,d){var e=d[0],f=d[1];e.init(f)}}}),angular.module("ui.bootstrap.tabs",[]).controller("UibTabsetController",["$scope",function(a){function b(a){for(var b=0;bb.index?1:a.index0&&13>b:b>=0&&24>b;return c&&""!==a.hours?(a.showMeridian&&(12===b&&(b=0),a.meridian===y[1]&&(b+=12)),b):void 0}function i(){var b=+a.minutes,c=b>=0&&60>b;return c&&""!==a.minutes?b:void 0}function j(){var b=+a.seconds;return b>=0&&60>b?b:void 0}function k(a,b){return null===a?"":angular.isDefined(a)&&a.toString().length<2&&!b?"0"+a:a.toString()}function l(a){m(),x.$setViewValue(new Date(v)),n(a)}function m(){s&&s.$setValidity("hours",!0),t&&t.$setValidity("minutes",!0),u&&u.$setValidity("seconds",!0),x.$setValidity("time",!0),a.invalidHours=!1,a.invalidMinutes=!1,a.invalidSeconds=!1}function n(b){if(x.$modelValue){var c=v.getHours(),d=v.getMinutes(),e=v.getSeconds();a.showMeridian&&(c=0===c||12===c?12:c%12),a.hours="h"===b?c:k(c,!z),"m"!==b&&(a.minutes=k(d)),a.meridian=v.getHours()<12?y[0]:y[1],"s"!==b&&(a.seconds=k(e)),a.meridian=v.getHours()<12?y[0]:y[1]}else a.hours=null,a.minutes=null,a.seconds=null,a.meridian=y[0]}function o(a){v=q(v,a),l()}function p(a,b){return q(a,60*b)}function q(a,b){var c=new Date(a.getTime()+1e3*b),d=new Date(a);return d.setHours(c.getHours(),c.getMinutes(),c.getSeconds()),d}function r(){return(null===a.hours||""===a.hours)&&(null===a.minutes||""===a.minutes)&&(!a.showSeconds||a.showSeconds&&(null===a.seconds||""===a.seconds))}var s,t,u,v=new Date,w=[],x={$setViewValue:angular.noop},y=angular.isDefined(c.meridians)?a.$parent.$eval(c.meridians):g.meridians||f.DATETIME_FORMATS.AMPMS,z=angular.isDefined(c.padHours)?a.$parent.$eval(c.padHours):!0;a.tabindex=angular.isDefined(c.tabindex)?c.tabindex:0,b.removeAttr("tabindex"),this.init=function(b,d){x=b,x.$render=this.render,x.$formatters.unshift(function(a){return a?new Date(a):null});var e=d.eq(0),f=d.eq(1),h=d.eq(2);s=e.controller("ngModel"),t=f.controller("ngModel"),u=h.controller("ngModel");var i=angular.isDefined(c.mousewheel)?a.$parent.$eval(c.mousewheel):g.mousewheel;i&&this.setupMousewheelEvents(e,f,h);var j=angular.isDefined(c.arrowkeys)?a.$parent.$eval(c.arrowkeys):g.arrowkeys;j&&this.setupArrowkeyEvents(e,f,h),a.readonlyInput=angular.isDefined(c.readonlyInput)?a.$parent.$eval(c.readonlyInput):g.readonlyInput,this.setupInputEvents(e,f,h)};var A=g.hourStep;c.hourStep&&w.push(a.$parent.$watch(d(c.hourStep),function(a){A=+a}));var B=g.minuteStep;c.minuteStep&&w.push(a.$parent.$watch(d(c.minuteStep),function(a){B=+a}));var C;w.push(a.$parent.$watch(d(c.min),function(a){var b=new Date(a);C=isNaN(b)?void 0:b}));var D;w.push(a.$parent.$watch(d(c.max),function(a){var b=new Date(a);D=isNaN(b)?void 0:b}));var E=!1;c.ngDisabled&&w.push(a.$parent.$watch(d(c.ngDisabled),function(a){E=a})),a.noIncrementHours=function(){var a=p(v,60*A);return E||a>D||v>a&&C>a},a.noDecrementHours=function(){var a=p(v,60*-A);return E||C>a||a>v&&a>D},a.noIncrementMinutes=function(){var a=p(v,B);return E||a>D||v>a&&C>a},a.noDecrementMinutes=function(){var a=p(v,-B);return E||C>a||a>v&&a>D},a.noIncrementSeconds=function(){var a=q(v,F);return E||a>D||v>a&&C>a},a.noDecrementSeconds=function(){var a=q(v,-F);return E||C>a||a>v&&a>D},a.noToggleMeridian=function(){return v.getHours()<12?E||p(v,720)>D:E||p(v,-720)0};b.on("mousewheel wheel",function(b){E||a.$apply(e(b)?a.incrementHours():a.decrementHours()),b.preventDefault()}),c.on("mousewheel wheel",function(b){E||a.$apply(e(b)?a.incrementMinutes():a.decrementMinutes()),b.preventDefault()}),d.on("mousewheel wheel",function(b){E||a.$apply(e(b)?a.incrementSeconds():a.decrementSeconds()),b.preventDefault()})},this.setupArrowkeyEvents=function(b,c,d){b.on("keydown",function(b){E||(38===b.which?(b.preventDefault(),a.incrementHours(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementHours(),a.$apply()))}),c.on("keydown",function(b){E||(38===b.which?(b.preventDefault(),a.incrementMinutes(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementMinutes(),a.$apply()))}),d.on("keydown",function(b){E||(38===b.which?(b.preventDefault(),a.incrementSeconds(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementSeconds(),a.$apply()))})},this.setupInputEvents=function(b,c,d){if(a.readonlyInput)return a.updateHours=angular.noop,a.updateMinutes=angular.noop,void(a.updateSeconds=angular.noop);var e=function(b,c,d){x.$setViewValue(null),x.$setValidity("time",!1),angular.isDefined(b)&&(a.invalidHours=b,s&&s.$setValidity("hours",!1)),angular.isDefined(c)&&(a.invalidMinutes=c,t&&t.$setValidity("minutes",!1)),angular.isDefined(d)&&(a.invalidSeconds=d,u&&u.$setValidity("seconds",!1))};a.updateHours=function(){var a=h(),b=i();x.$setDirty(),angular.isDefined(a)&&angular.isDefined(b)?(v.setHours(a),v.setMinutes(b),C>v||v>D?e(!0):l("h")):e(!0)},b.on("blur",function(b){x.$setTouched(),r()?m():null===a.hours||""===a.hours?e(!0):!a.invalidHours&&a.hours<10&&a.$apply(function(){a.hours=k(a.hours,!z)})}),a.updateMinutes=function(){var a=i(),b=h();x.$setDirty(),angular.isDefined(a)&&angular.isDefined(b)?(v.setHours(b),v.setMinutes(a),C>v||v>D?e(void 0,!0):l("m")):e(void 0,!0)},c.on("blur",function(b){x.$setTouched(),r()?m():null===a.minutes?e(void 0,!0):!a.invalidMinutes&&a.minutes<10&&a.$apply(function(){a.minutes=k(a.minutes)})}),a.updateSeconds=function(){var a=j();x.$setDirty(),angular.isDefined(a)?(v.setSeconds(a),l("s")):e(void 0,void 0,!0)},d.on("blur",function(b){r()?m():!a.invalidSeconds&&a.seconds<10&&a.$apply(function(){a.seconds=k(a.seconds)})})},this.render=function(){var b=x.$viewValue;isNaN(b)?(x.$setValidity("time",!1),e.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')):(b&&(v=b),C>v||v>D?(x.$setValidity("time",!1),a.invalidHours=!0,a.invalidMinutes=!0):m(),n())},a.showSpinners=angular.isDefined(c.showSpinners)?a.$parent.$eval(c.showSpinners):g.showSpinners,a.incrementHours=function(){a.noIncrementHours()||o(60*A*60)},a.decrementHours=function(){a.noDecrementHours()||o(60*-A*60)},a.incrementMinutes=function(){a.noIncrementMinutes()||o(60*B)},a.decrementMinutes=function(){a.noDecrementMinutes()||o(60*-B)},a.incrementSeconds=function(){a.noIncrementSeconds()||o(F)},a.decrementSeconds=function(){a.noDecrementSeconds()||o(-F)},a.toggleMeridian=function(){var b=i(),c=h();a.noToggleMeridian()||(angular.isDefined(b)&&angular.isDefined(c)?o(720*(v.getHours()<12?60:-60)):a.meridian=a.meridian===y[0]?y[1]:y[0])},a.blur=function(){x.$setTouched()},a.$on("$destroy",function(){for(;w.length;)w.shift()()})}]).directive("uibTimepicker",["uibTimepickerConfig",function(a){return{require:["uibTimepicker","?^ngModel"],restrict:"A",controller:"UibTimepickerController",controllerAs:"timepicker",scope:{},templateUrl:function(b,c){return c.templateUrl||a.templateUrl},link:function(a,b,c,d){var e=d[0],f=d[1];f&&e.init(f,b.find("input"))}}}]),angular.module("ui.bootstrap.typeahead",["ui.bootstrap.debounce","ui.bootstrap.position"]).factory("uibTypeaheadParser",["$parse",function(a){var b=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;return{parse:function(c){var d=c.match(b);if(!d)throw new Error('Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_" but got "'+c+'".');return{itemName:d[3],source:a(d[4]),viewMapper:a(d[2]||d[1]),modelMapper:a(d[1])}}}}]).controller("UibTypeaheadController",["$scope","$element","$attrs","$compile","$parse","$q","$timeout","$document","$window","$rootScope","$$debounce","$uibPosition","uibTypeaheadParser",function(a,b,c,d,e,f,g,h,i,j,k,l,m){function n(){O.moveInProgress||(O.moveInProgress=!0,O.$digest()),Z()}function o(){O.position=E?l.offset(b):l.position(b),O.position.top+=b.prop("offsetHeight")}var p,q,r=[9,13,27,38,40],s=200,t=a.$eval(c.typeaheadMinLength);t||0===t||(t=1),a.$watch(c.typeaheadMinLength,function(a){t=a||0===a?a:1});var u=a.$eval(c.typeaheadWaitMs)||0,v=a.$eval(c.typeaheadEditable)!==!1;a.$watch(c.typeaheadEditable,function(a){v=a!==!1});var w,x,y=e(c.typeaheadLoading).assign||angular.noop,z=c.typeaheadShouldSelect?e(c.typeaheadShouldSelect):function(a,b){var c=b.$event;return 13===c.which||9===c.which},A=e(c.typeaheadOnSelect),B=angular.isDefined(c.typeaheadSelectOnBlur)?a.$eval(c.typeaheadSelectOnBlur):!1,C=e(c.typeaheadNoResults).assign||angular.noop,D=c.typeaheadInputFormatter?e(c.typeaheadInputFormatter):void 0,E=c.typeaheadAppendToBody?a.$eval(c.typeaheadAppendToBody):!1,F=c.typeaheadAppendTo?a.$eval(c.typeaheadAppendTo):null,G=a.$eval(c.typeaheadFocusFirst)!==!1,H=c.typeaheadSelectOnExact?a.$eval(c.typeaheadSelectOnExact):!1,I=e(c.typeaheadIsOpen).assign||angular.noop,J=a.$eval(c.typeaheadShowHint)||!1,K=e(c.ngModel),L=e(c.ngModel+"($$$p)"),M=function(b,c){return angular.isFunction(K(a))&&q&&q.$options&&q.$options.getterSetter?L(b,{$$$p:c}):K.assign(b,c)},N=m.parse(c.uibTypeahead),O=a.$new(),P=a.$on("$destroy",function(){O.$destroy()});O.$on("$destroy",P);var Q="typeahead-"+O.$id+"-"+Math.floor(1e4*Math.random());b.attr({"aria-autocomplete":"list","aria-expanded":!1,"aria-owns":Q});var R,S;J&&(R=angular.element("
      "),R.css("position","relative"),b.after(R),S=b.clone(),S.attr("placeholder",""),S.attr("tabindex","-1"),S.val(""),S.css({position:"absolute",top:"0px",left:"0px","border-color":"transparent","box-shadow":"none",opacity:1,background:"none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)",color:"#999"}),b.css({position:"relative","vertical-align":"top","background-color":"transparent"}),S.attr("id")&&S.removeAttr("id"),R.append(S),S.after(b));var T=angular.element("
      ");T.attr({id:Q,matches:"matches",active:"activeIdx",select:"select(activeIdx, evt)","move-in-progress":"moveInProgress",query:"query",position:"position","assign-is-open":"assignIsOpen(isOpen)",debounce:"debounceUpdate"}),angular.isDefined(c.typeaheadTemplateUrl)&&T.attr("template-url",c.typeaheadTemplateUrl),angular.isDefined(c.typeaheadPopupTemplateUrl)&&T.attr("popup-template-url",c.typeaheadPopupTemplateUrl);var U=function(){J&&S.val("")},V=function(){O.matches=[],O.activeIdx=-1,b.attr("aria-expanded",!1),U()},W=function(a){return Q+"-option-"+a};O.$watch("activeIdx",function(a){0>a?b.removeAttr("aria-activedescendant"):b.attr("aria-activedescendant",W(a))});var X=function(a,b){return O.matches.length>b&&a?a.toUpperCase()===O.matches[b].label.toUpperCase():!1},Y=function(c,d){var e={$viewValue:c};y(a,!0),C(a,!1),f.when(N.source(a,e)).then(function(f){var g=c===p.$viewValue;if(g&&w)if(f&&f.length>0){O.activeIdx=G?0:-1,C(a,!1),O.matches.length=0;for(var h=0;h0&&i.slice(0,c.length).toUpperCase()===c.toUpperCase()?S.val(c+i.slice(c.length)):S.val("")}}else V(),C(a,!0);g&&y(a,!1)},function(){V(),y(a,!1),C(a,!0)})};E&&(angular.element(i).on("resize",n),h.find("body").on("scroll",n));var Z=k(function(){O.matches.length&&o(),O.moveInProgress=!1},s);O.moveInProgress=!1,O.query=void 0;var $,_=function(a){$=g(function(){Y(a)},u)},aa=function(){$&&g.cancel($)};V(),O.assignIsOpen=function(b){I(a,b)},O.select=function(d,e){var f,h,i={};x=!0,i[N.itemName]=h=O.matches[d].model,f=N.modelMapper(a,i),M(a,f),p.$setValidity("editable",!0),p.$setValidity("parse",!0),A(a,{$item:h,$model:f,$label:N.viewMapper(a,i),$event:e}),V(),O.$eval(c.typeaheadFocusOnSelect)!==!1&&g(function(){b[0].focus()},0,!1)},b.on("keydown",function(b){if(0!==O.matches.length&&-1!==r.indexOf(b.which)){var c=z(a,{$event:b});if(-1===O.activeIdx&&c||9===b.which&&b.shiftKey)return V(),void O.$digest();b.preventDefault();var d;switch(b.which){case 27:b.stopPropagation(),V(),a.$digest();break;case 38:O.activeIdx=(O.activeIdx>0?O.activeIdx:O.matches.length)-1,O.$digest(),d=T[0].querySelectorAll(".uib-typeahead-match")[O.activeIdx],d.parentNode.scrollTop=d.offsetTop;break;case 40:O.activeIdx=(O.activeIdx+1)%O.matches.length,O.$digest(),d=T[0].querySelectorAll(".uib-typeahead-match")[O.activeIdx],d.parentNode.scrollTop=d.offsetTop;break;default:c&&O.$apply(function(){angular.isNumber(O.debounceUpdate)||angular.isObject(O.debounceUpdate)?k(function(){O.select(O.activeIdx,b)},angular.isNumber(O.debounceUpdate)?O.debounceUpdate:O.debounceUpdate["default"]):O.select(O.activeIdx,b)})}}}),b.on("focus",function(a){w=!0,0!==t||p.$viewValue||g(function(){Y(p.$viewValue,a)},0)}),b.on("blur",function(a){B&&O.matches.length&&-1!==O.activeIdx&&!x&&(x=!0,O.$apply(function(){angular.isObject(O.debounceUpdate)&&angular.isNumber(O.debounceUpdate.blur)?k(function(){O.select(O.activeIdx,a)},O.debounceUpdate.blur):O.select(O.activeIdx,a)})),!v&&p.$error.editable&&(p.$setViewValue(),O.$apply(function(){p.$setValidity("editable",!0), +p.$setValidity("parse",!0)}),b.val("")),w=!1,x=!1});var ba=function(c){b[0]!==c.target&&3!==c.which&&0!==O.matches.length&&(V(),j.$$phase||a.$digest())};h.on("click",ba),a.$on("$destroy",function(){h.off("click",ba),(E||F)&&ca.remove(),E&&(angular.element(i).off("resize",n),h.find("body").off("scroll",n)),T.remove(),J&&R.remove()});var ca=d(T)(O);E?h.find("body").append(ca):F?angular.element(F).eq(0).append(ca):b.after(ca),this.init=function(b,c){p=b,q=c,O.debounceUpdate=p.$options&&e(p.$options.debounce)(a),p.$parsers.unshift(function(b){return w=!0,0===t||b&&b.length>=t?u>0?(aa(),_(b)):Y(b):(y(a,!1),aa(),V()),v?b:b?void p.$setValidity("editable",!1):(p.$setValidity("editable",!0),null)}),p.$formatters.push(function(b){var c,d,e={};return v||p.$setValidity("editable",!0),D?(e.$model=b,D(a,e)):(e[N.itemName]=b,c=N.viewMapper(a,e),e[N.itemName]=void 0,d=N.viewMapper(a,e),c!==d?c:b)})}}]).directive("uibTypeahead",function(){return{controller:"UibTypeaheadController",require:["ngModel","^?ngModelOptions","uibTypeahead"],link:function(a,b,c,d){d[2].init(d[0],d[1])}}}).directive("uibTypeaheadPopup",["$$debounce",function(a){return{scope:{matches:"=",query:"=",active:"=",position:"&",moveInProgress:"=",select:"&",assignIsOpen:"&",debounce:"&"},replace:!0,templateUrl:function(a,b){return b.popupTemplateUrl||"uib/template/typeahead/typeahead-popup.html"},link:function(b,c,d){b.templateUrl=d.templateUrl,b.isOpen=function(){var a=b.matches.length>0;return b.assignIsOpen({isOpen:a}),a},b.isActive=function(a){return b.active===a},b.selectActive=function(a){b.active=a},b.selectMatch=function(c,d){var e=b.debounce();angular.isNumber(e)||angular.isObject(e)?a(function(){b.select({activeIdx:c,evt:d})},angular.isNumber(e)?e:e["default"]):b.select({activeIdx:c,evt:d})}}}}]).directive("uibTypeaheadMatch",["$templateRequest","$compile","$parse",function(a,b,c){return{scope:{index:"=",match:"=",query:"="},link:function(d,e,f){var g=c(f.templateUrl)(d.$parent)||"uib/template/typeahead/typeahead-match.html";a(g).then(function(a){var c=angular.element(a.trim());e.replaceWith(c),b(c)(d)})}}}]).filter("uibTypeaheadHighlight",["$sce","$injector","$log",function(a,b,c){function d(a){return a.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")}function e(a){return/<.*>/g.test(a)}var f;return f=b.has("$sanitize"),function(b,g){return!f&&e(b)&&c.warn("Unsafe use of typeahead please use ngSanitize"),b=g?(""+b).replace(new RegExp(d(g),"gi"),"$&"):b,f||(b=a.trustAsHtml(b)),b}}]),angular.module("uib/template/accordion/accordion-group.html",[]).run(["$templateCache",function(a){a.put("uib/template/accordion/accordion-group.html",'\n
      \n
      \n
      \n')}]),angular.module("uib/template/accordion/accordion.html",[]).run(["$templateCache",function(a){a.put("uib/template/accordion/accordion.html",'
      ')}]),angular.module("uib/template/alert/alert.html",[]).run(["$templateCache",function(a){a.put("uib/template/alert/alert.html",'\n
      \n')}]),angular.module("uib/template/carousel/carousel.html",[]).run(["$templateCache",function(a){a.put("uib/template/carousel/carousel.html",'\n\n \n previous\n\n\n \n next\n\n\n')}]),angular.module("uib/template/carousel/slide.html",[]).run(["$templateCache",function(a){a.put("uib/template/carousel/slide.html",'
      \n')}]),angular.module("uib/template/datepicker/datepicker.html",[]).run(["$templateCache",function(a){a.put("uib/template/datepicker/datepicker.html",'
      \n
      \n
      \n
      \n
      \n')}]),angular.module("uib/template/datepicker/day.html",[]).run(["$templateCache",function(a){a.put("uib/template/datepicker/day.html",'\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
      {{::label.abbr}}
      {{ weekNumbers[$index] }}\n \n
      \n')}]),angular.module("uib/template/datepicker/month.html",[]).run(["$templateCache",function(a){a.put("uib/template/datepicker/month.html",'\n \n \n \n \n \n \n \n \n \n \n \n \n
      \n \n
      \n')}]),angular.module("uib/template/datepicker/year.html",[]).run(["$templateCache",function(a){a.put("uib/template/datepicker/year.html",'\n \n \n \n \n \n \n \n \n \n \n \n \n
      \n \n
      \n')}]),angular.module("uib/template/datepickerPopup/popup.html",[]).run(["$templateCache",function(a){a.put("uib/template/datepickerPopup/popup.html",'\n')}]),angular.module("uib/template/modal/window.html",[]).run(["$templateCache",function(a){a.put("uib/template/modal/window.html","
      \n")}]),angular.module("uib/template/pager/pager.html",[]).run(["$templateCache",function(a){a.put("uib/template/pager/pager.html",'
    • {{::getText(\'previous\')}}
    • \n
    • {{::getText(\'next\')}}
    • \n')}]),angular.module("uib/template/pagination/pagination.html",[]).run(["$templateCache",function(a){a.put("uib/template/pagination/pagination.html",'
    • {{::getText(\'first\')}}
    • \n
    • {{::getText(\'previous\')}}
    • \n
    • {{page.text}}
    • \n
    • {{::getText(\'next\')}}
    • \n
    • {{::getText(\'last\')}}
    • \n')}]),angular.module("uib/template/tooltip/tooltip-html-popup.html",[]).run(["$templateCache",function(a){a.put("uib/template/tooltip/tooltip-html-popup.html",'
      \n
      \n')}]),angular.module("uib/template/tooltip/tooltip-popup.html",[]).run(["$templateCache",function(a){a.put("uib/template/tooltip/tooltip-popup.html",'
      \n
      \n')}]),angular.module("uib/template/tooltip/tooltip-template-popup.html",[]).run(["$templateCache",function(a){a.put("uib/template/tooltip/tooltip-template-popup.html",'
      \n
      \n')}]),angular.module("uib/template/popover/popover-html.html",[]).run(["$templateCache",function(a){a.put("uib/template/popover/popover-html.html",'
      \n\n
      \n

      \n
      \n
      \n')}]),angular.module("uib/template/popover/popover-template.html",[]).run(["$templateCache",function(a){a.put("uib/template/popover/popover-template.html",'
      \n\n
      \n

      \n
      \n
      \n')}]),angular.module("uib/template/popover/popover.html",[]).run(["$templateCache",function(a){a.put("uib/template/popover/popover.html",'
      \n\n
      \n

      \n
      \n
      \n')}]),angular.module("uib/template/progressbar/bar.html",[]).run(["$templateCache",function(a){a.put("uib/template/progressbar/bar.html",'
      \n')}]),angular.module("uib/template/progressbar/progress.html",[]).run(["$templateCache",function(a){a.put("uib/template/progressbar/progress.html",'
      ')}]),angular.module("uib/template/progressbar/progressbar.html",[]).run(["$templateCache",function(a){a.put("uib/template/progressbar/progressbar.html",'
      \n
      \n
      \n')}]),angular.module("uib/template/rating/rating.html",[]).run(["$templateCache",function(a){a.put("uib/template/rating/rating.html",'\n ({{ $index < value ? \'*\' : \' \' }})\n \n\n')}]),angular.module("uib/template/tabs/tab.html",[]).run(["$templateCache",function(a){a.put("uib/template/tabs/tab.html",'\n')}]),angular.module("uib/template/tabs/tabset.html",[]).run(["$templateCache",function(a){a.put("uib/template/tabs/tabset.html",'
      \n \n
      \n
      \n
      \n
      \n
      \n')}]),angular.module("uib/template/timepicker/timepicker.html",[]).run(["$templateCache",function(a){a.put("uib/template/timepicker/timepicker.html",'\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
        
      \n \n :\n \n :\n \n
        
      \n')}]),angular.module("uib/template/typeahead/typeahead-match.html",[]).run(["$templateCache",function(a){a.put("uib/template/typeahead/typeahead-match.html",'\n')}]),angular.module("uib/template/typeahead/typeahead-popup.html",[]).run(["$templateCache",function(a){a.put("uib/template/typeahead/typeahead-popup.html",'\n')}]),angular.module("ui.bootstrap.carousel").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibCarouselCss&&angular.element(document).find("head").prepend(''),angular.$$uibCarouselCss=!0}),angular.module("ui.bootstrap.datepicker").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibDatepickerCss&&angular.element(document).find("head").prepend(''),angular.$$uibDatepickerCss=!0}),angular.module("ui.bootstrap.position").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibPositionCss&&angular.element(document).find("head").prepend(''),angular.$$uibPositionCss=!0}),angular.module("ui.bootstrap.datepickerPopup").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibDatepickerpopupCss&&angular.element(document).find("head").prepend(''),angular.$$uibDatepickerpopupCss=!0}),angular.module("ui.bootstrap.tooltip").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTooltipCss&&angular.element(document).find("head").prepend(''),angular.$$uibTooltipCss=!0}),angular.module("ui.bootstrap.timepicker").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTimepickerCss&&angular.element(document).find("head").prepend(''),angular.$$uibTimepickerCss=!0}),angular.module("ui.bootstrap.typeahead").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTypeaheadCss&&angular.element(document).find("head").prepend(''),angular.$$uibTypeaheadCss=!0}); \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap.js b/ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap.js new file mode 100644 index 00000000..785c5b34 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap.js @@ -0,0 +1,7351 @@ +/* + * angular-ui-bootstrap + * http://angular-ui.github.io/bootstrap/ + + * Version: 2.4.0 - 2016-12-29 + * License: MIT + */angular.module("ui.bootstrap", ["ui.bootstrap.collapse","ui.bootstrap.tabindex","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.datepicker","ui.bootstrap.position","ui.bootstrap.datepickerPopup","ui.bootstrap.debounce","ui.bootstrap.multiMap","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]); +angular.module('ui.bootstrap.collapse', []) + + .directive('uibCollapse', ['$animate', '$q', '$parse', '$injector', function($animate, $q, $parse, $injector) { + var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null; + return { + link: function(scope, element, attrs) { + var expandingExpr = $parse(attrs.expanding), + expandedExpr = $parse(attrs.expanded), + collapsingExpr = $parse(attrs.collapsing), + collapsedExpr = $parse(attrs.collapsed), + horizontal = false, + css = {}, + cssTo = {}; + + init(); + + function init() { + horizontal = !!('horizontal' in attrs); + if (horizontal) { + css = { + width: '' + }; + cssTo = {width: '0'}; + } else { + css = { + height: '' + }; + cssTo = {height: '0'}; + } + if (!scope.$eval(attrs.uibCollapse)) { + element.addClass('in') + .addClass('collapse') + .attr('aria-expanded', true) + .attr('aria-hidden', false) + .css(css); + } + } + + function getScrollFromElement(element) { + if (horizontal) { + return {width: element.scrollWidth + 'px'}; + } + return {height: element.scrollHeight + 'px'}; + } + + function expand() { + if (element.hasClass('collapse') && element.hasClass('in')) { + return; + } + + $q.resolve(expandingExpr(scope)) + .then(function() { + element.removeClass('collapse') + .addClass('collapsing') + .attr('aria-expanded', true) + .attr('aria-hidden', false); + + if ($animateCss) { + $animateCss(element, { + addClass: 'in', + easing: 'ease', + css: { + overflow: 'hidden' + }, + to: getScrollFromElement(element[0]) + }).start()['finally'](expandDone); + } else { + $animate.addClass(element, 'in', { + css: { + overflow: 'hidden' + }, + to: getScrollFromElement(element[0]) + }).then(expandDone); + } + }); + } + + function expandDone() { + element.removeClass('collapsing') + .addClass('collapse') + .css(css); + expandedExpr(scope); + } + + function collapse() { + if (!element.hasClass('collapse') && !element.hasClass('in')) { + return collapseDone(); + } + + $q.resolve(collapsingExpr(scope)) + .then(function() { + element + // IMPORTANT: The width must be set before adding "collapsing" class. + // Otherwise, the browser attempts to animate from width 0 (in + // collapsing class) to the given width here. + .css(getScrollFromElement(element[0])) + // initially all panel collapse have the collapse class, this removal + // prevents the animation from jumping to collapsed state + .removeClass('collapse') + .addClass('collapsing') + .attr('aria-expanded', false) + .attr('aria-hidden', true); + + if ($animateCss) { + $animateCss(element, { + removeClass: 'in', + to: cssTo + }).start()['finally'](collapseDone); + } else { + $animate.removeClass(element, 'in', { + to: cssTo + }).then(collapseDone); + } + }); + } + + function collapseDone() { + element.css(cssTo); // Required so that collapse works when animation is disabled + element.removeClass('collapsing') + .addClass('collapse'); + collapsedExpr(scope); + } + + scope.$watch(attrs.uibCollapse, function(shouldCollapse) { + if (shouldCollapse) { + collapse(); + } else { + expand(); + } + }); + } + }; + }]); + +angular.module('ui.bootstrap.tabindex', []) + +.directive('uibTabindexToggle', function() { + return { + restrict: 'A', + link: function(scope, elem, attrs) { + attrs.$observe('disabled', function(disabled) { + attrs.$set('tabindex', disabled ? -1 : null); + }); + } + }; +}); + +angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse', 'ui.bootstrap.tabindex']) + +.constant('uibAccordionConfig', { + closeOthers: true +}) + +.controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) { + // This array keeps track of the accordion groups + this.groups = []; + + // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to + this.closeOthers = function(openGroup) { + var closeOthers = angular.isDefined($attrs.closeOthers) ? + $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers; + if (closeOthers) { + angular.forEach(this.groups, function(group) { + if (group !== openGroup) { + group.isOpen = false; + } + }); + } + }; + + // This is called from the accordion-group directive to add itself to the accordion + this.addGroup = function(groupScope) { + var that = this; + this.groups.push(groupScope); + + groupScope.$on('$destroy', function(event) { + that.removeGroup(groupScope); + }); + }; + + // This is called from the accordion-group directive when to remove itself + this.removeGroup = function(group) { + var index = this.groups.indexOf(group); + if (index !== -1) { + this.groups.splice(index, 1); + } + }; +}]) + +// The accordion directive simply sets up the directive controller +// and adds an accordion CSS class to itself element. +.directive('uibAccordion', function() { + return { + controller: 'UibAccordionController', + controllerAs: 'accordion', + transclude: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/accordion/accordion.html'; + } + }; +}) + +// The accordion-group directive indicates a block of html that will expand and collapse in an accordion +.directive('uibAccordionGroup', function() { + return { + require: '^uibAccordion', // We need this directive to be inside an accordion + transclude: true, // It transcludes the contents of the directive into the template + restrict: 'A', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/accordion/accordion-group.html'; + }, + scope: { + heading: '@', // Interpolate the heading attribute onto this scope + panelClass: '@?', // Ditto with panelClass + isOpen: '=?', + isDisabled: '=?' + }, + controller: function() { + this.setHeading = function(element) { + this.heading = element; + }; + }, + link: function(scope, element, attrs, accordionCtrl) { + element.addClass('panel'); + accordionCtrl.addGroup(scope); + + scope.openClass = attrs.openClass || 'panel-open'; + scope.panelClass = attrs.panelClass || 'panel-default'; + scope.$watch('isOpen', function(value) { + element.toggleClass(scope.openClass, !!value); + if (value) { + accordionCtrl.closeOthers(scope); + } + }); + + scope.toggleOpen = function($event) { + if (!scope.isDisabled) { + if (!$event || $event.which === 32) { + scope.isOpen = !scope.isOpen; + } + } + }; + + var id = 'accordiongroup-' + scope.$id + '-' + Math.floor(Math.random() * 10000); + scope.headingId = id + '-tab'; + scope.panelId = id + '-panel'; + } + }; +}) + +// Use accordion-heading below an accordion-group to provide a heading containing HTML +.directive('uibAccordionHeading', function() { + return { + transclude: true, // Grab the contents to be used as the heading + template: '', // In effect remove this element! + replace: true, + require: '^uibAccordionGroup', + link: function(scope, element, attrs, accordionGroupCtrl, transclude) { + // Pass the heading to the accordion-group controller + // so that it can be transcluded into the right place in the template + // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat] + accordionGroupCtrl.setHeading(transclude(scope, angular.noop)); + } + }; +}) + +// Use in the accordion-group template to indicate where you want the heading to be transcluded +// You must provide the property on the accordion-group controller that will hold the transcluded element +.directive('uibAccordionTransclude', function() { + return { + require: '^uibAccordionGroup', + link: function(scope, element, attrs, controller) { + scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) { + if (heading) { + var elem = angular.element(element[0].querySelector(getHeaderSelectors())); + elem.html(''); + elem.append(heading); + } + }); + } + }; + + function getHeaderSelectors() { + return 'uib-accordion-header,' + + 'data-uib-accordion-header,' + + 'x-uib-accordion-header,' + + 'uib\\:accordion-header,' + + '[uib-accordion-header],' + + '[data-uib-accordion-header],' + + '[x-uib-accordion-header]'; + } +}); + +angular.module('ui.bootstrap.alert', []) + +.controller('UibAlertController', ['$scope', '$element', '$attrs', '$interpolate', '$timeout', function($scope, $element, $attrs, $interpolate, $timeout) { + $scope.closeable = !!$attrs.close; + $element.addClass('alert'); + $attrs.$set('role', 'alert'); + if ($scope.closeable) { + $element.addClass('alert-dismissible'); + } + + var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ? + $interpolate($attrs.dismissOnTimeout)($scope.$parent) : null; + + if (dismissOnTimeout) { + $timeout(function() { + $scope.close(); + }, parseInt(dismissOnTimeout, 10)); + } +}]) + +.directive('uibAlert', function() { + return { + controller: 'UibAlertController', + controllerAs: 'alert', + restrict: 'A', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/alert/alert.html'; + }, + transclude: true, + scope: { + close: '&' + } + }; +}); + +angular.module('ui.bootstrap.buttons', []) + +.constant('uibButtonConfig', { + activeClass: 'active', + toggleEvent: 'click' +}) + +.controller('UibButtonsController', ['uibButtonConfig', function(buttonConfig) { + this.activeClass = buttonConfig.activeClass || 'active'; + this.toggleEvent = buttonConfig.toggleEvent || 'click'; +}]) + +.directive('uibBtnRadio', ['$parse', function($parse) { + return { + require: ['uibBtnRadio', 'ngModel'], + controller: 'UibButtonsController', + controllerAs: 'buttons', + link: function(scope, element, attrs, ctrls) { + var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + var uncheckableExpr = $parse(attrs.uibUncheckable); + + element.find('input').css({display: 'none'}); + + //model -> UI + ngModelCtrl.$render = function() { + element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio))); + }; + + //ui->model + element.on(buttonsCtrl.toggleEvent, function() { + if (attrs.disabled) { + return; + } + + var isActive = element.hasClass(buttonsCtrl.activeClass); + + if (!isActive || angular.isDefined(attrs.uncheckable)) { + scope.$apply(function() { + ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio)); + ngModelCtrl.$render(); + }); + } + }); + + if (attrs.uibUncheckable) { + scope.$watch(uncheckableExpr, function(uncheckable) { + attrs.$set('uncheckable', uncheckable ? '' : undefined); + }); + } + } + }; +}]) + +.directive('uibBtnCheckbox', function() { + return { + require: ['uibBtnCheckbox', 'ngModel'], + controller: 'UibButtonsController', + controllerAs: 'button', + link: function(scope, element, attrs, ctrls) { + var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + element.find('input').css({display: 'none'}); + + function getTrueValue() { + return getCheckboxValue(attrs.btnCheckboxTrue, true); + } + + function getFalseValue() { + return getCheckboxValue(attrs.btnCheckboxFalse, false); + } + + function getCheckboxValue(attribute, defaultValue) { + return angular.isDefined(attribute) ? scope.$eval(attribute) : defaultValue; + } + + //model -> UI + ngModelCtrl.$render = function() { + element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue())); + }; + + //ui->model + element.on(buttonsCtrl.toggleEvent, function() { + if (attrs.disabled) { + return; + } + + scope.$apply(function() { + ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue()); + ngModelCtrl.$render(); + }); + }); + } + }; +}); + +angular.module('ui.bootstrap.carousel', []) + +.controller('UibCarouselController', ['$scope', '$element', '$interval', '$timeout', '$animate', function($scope, $element, $interval, $timeout, $animate) { + var self = this, + slides = self.slides = $scope.slides = [], + SLIDE_DIRECTION = 'uib-slideDirection', + currentIndex = $scope.active, + currentInterval, isPlaying, bufferedTransitions = []; + + var destroyed = false; + $element.addClass('carousel'); + + self.addSlide = function(slide, element) { + slides.push({ + slide: slide, + element: element + }); + slides.sort(function(a, b) { + return +a.slide.index - +b.slide.index; + }); + //if this is the first slide or the slide is set to active, select it + if (slide.index === $scope.active || slides.length === 1 && !angular.isNumber($scope.active)) { + if ($scope.$currentTransition) { + $scope.$currentTransition = null; + } + + currentIndex = slide.index; + $scope.active = slide.index; + setActive(currentIndex); + self.select(slides[findSlideIndex(slide)]); + if (slides.length === 1) { + $scope.play(); + } + } + }; + + self.getCurrentIndex = function() { + for (var i = 0; i < slides.length; i++) { + if (slides[i].slide.index === currentIndex) { + return i; + } + } + }; + + self.next = $scope.next = function() { + var newIndex = (self.getCurrentIndex() + 1) % slides.length; + + if (newIndex === 0 && $scope.noWrap()) { + $scope.pause(); + return; + } + + return self.select(slides[newIndex], 'next'); + }; + + self.prev = $scope.prev = function() { + var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1; + + if ($scope.noWrap() && newIndex === slides.length - 1) { + $scope.pause(); + return; + } + + return self.select(slides[newIndex], 'prev'); + }; + + self.removeSlide = function(slide) { + var index = findSlideIndex(slide); + + var bufferedIndex = bufferedTransitions.indexOf(slides[index]); + if (bufferedIndex !== -1) { + bufferedTransitions.splice(bufferedIndex, 1); + } + + //get the index of the slide inside the carousel + slides.splice(index, 1); + if (slides.length > 0 && currentIndex === index) { + if (index >= slides.length) { + currentIndex = slides.length - 1; + $scope.active = currentIndex; + setActive(currentIndex); + self.select(slides[slides.length - 1]); + } else { + currentIndex = index; + $scope.active = currentIndex; + setActive(currentIndex); + self.select(slides[index]); + } + } else if (currentIndex > index) { + currentIndex--; + $scope.active = currentIndex; + } + + //clean the active value when no more slide + if (slides.length === 0) { + currentIndex = null; + $scope.active = null; + clearBufferedTransitions(); + } + }; + + /* direction: "prev" or "next" */ + self.select = $scope.select = function(nextSlide, direction) { + var nextIndex = findSlideIndex(nextSlide.slide); + //Decide direction if it's not given + if (direction === undefined) { + direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev'; + } + //Prevent this user-triggered transition from occurring if there is already one in progress + if (nextSlide.slide.index !== currentIndex && + !$scope.$currentTransition) { + goNext(nextSlide.slide, nextIndex, direction); + } else if (nextSlide && nextSlide.slide.index !== currentIndex && $scope.$currentTransition) { + bufferedTransitions.push(slides[nextIndex]); + } + }; + + /* Allow outside people to call indexOf on slides array */ + $scope.indexOfSlide = function(slide) { + return +slide.slide.index; + }; + + $scope.isActive = function(slide) { + return $scope.active === slide.slide.index; + }; + + $scope.isPrevDisabled = function() { + return $scope.active === 0 && $scope.noWrap(); + }; + + $scope.isNextDisabled = function() { + return $scope.active === slides.length - 1 && $scope.noWrap(); + }; + + $scope.pause = function() { + if (!$scope.noPause) { + isPlaying = false; + resetTimer(); + } + }; + + $scope.play = function() { + if (!isPlaying) { + isPlaying = true; + restartTimer(); + } + }; + + $element.on('mouseenter', $scope.pause); + $element.on('mouseleave', $scope.play); + + $scope.$on('$destroy', function() { + destroyed = true; + resetTimer(); + }); + + $scope.$watch('noTransition', function(noTransition) { + $animate.enabled($element, !noTransition); + }); + + $scope.$watch('interval', restartTimer); + + $scope.$watchCollection('slides', resetTransition); + + $scope.$watch('active', function(index) { + if (angular.isNumber(index) && currentIndex !== index) { + for (var i = 0; i < slides.length; i++) { + if (slides[i].slide.index === index) { + index = i; + break; + } + } + + var slide = slides[index]; + if (slide) { + setActive(index); + self.select(slides[index]); + currentIndex = index; + } + } + }); + + function clearBufferedTransitions() { + while (bufferedTransitions.length) { + bufferedTransitions.shift(); + } + } + + function getSlideByIndex(index) { + for (var i = 0, l = slides.length; i < l; ++i) { + if (slides[i].index === index) { + return slides[i]; + } + } + } + + function setActive(index) { + for (var i = 0; i < slides.length; i++) { + slides[i].slide.active = i === index; + } + } + + function goNext(slide, index, direction) { + if (destroyed) { + return; + } + + angular.extend(slide, {direction: direction}); + angular.extend(slides[currentIndex].slide || {}, {direction: direction}); + if ($animate.enabled($element) && !$scope.$currentTransition && + slides[index].element && self.slides.length > 1) { + slides[index].element.data(SLIDE_DIRECTION, slide.direction); + var currentIdx = self.getCurrentIndex(); + + if (angular.isNumber(currentIdx) && slides[currentIdx].element) { + slides[currentIdx].element.data(SLIDE_DIRECTION, slide.direction); + } + + $scope.$currentTransition = true; + $animate.on('addClass', slides[index].element, function(element, phase) { + if (phase === 'close') { + $scope.$currentTransition = null; + $animate.off('addClass', element); + if (bufferedTransitions.length) { + var nextSlide = bufferedTransitions.pop().slide; + var nextIndex = nextSlide.index; + var nextDirection = nextIndex > self.getCurrentIndex() ? 'next' : 'prev'; + clearBufferedTransitions(); + + goNext(nextSlide, nextIndex, nextDirection); + } + } + }); + } + + $scope.active = slide.index; + currentIndex = slide.index; + setActive(index); + + //every time you change slides, reset the timer + restartTimer(); + } + + function findSlideIndex(slide) { + for (var i = 0; i < slides.length; i++) { + if (slides[i].slide === slide) { + return i; + } + } + } + + function resetTimer() { + if (currentInterval) { + $interval.cancel(currentInterval); + currentInterval = null; + } + } + + function resetTransition(slides) { + if (!slides.length) { + $scope.$currentTransition = null; + clearBufferedTransitions(); + } + } + + function restartTimer() { + resetTimer(); + var interval = +$scope.interval; + if (!isNaN(interval) && interval > 0) { + currentInterval = $interval(timerFn, interval); + } + } + + function timerFn() { + var interval = +$scope.interval; + if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) { + $scope.next(); + } else { + $scope.pause(); + } + } +}]) + +.directive('uibCarousel', function() { + return { + transclude: true, + controller: 'UibCarouselController', + controllerAs: 'carousel', + restrict: 'A', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/carousel/carousel.html'; + }, + scope: { + active: '=', + interval: '=', + noTransition: '=', + noPause: '=', + noWrap: '&' + } + }; +}) + +.directive('uibSlide', ['$animate', function($animate) { + return { + require: '^uibCarousel', + restrict: 'A', + transclude: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/carousel/slide.html'; + }, + scope: { + actual: '=?', + index: '=?' + }, + link: function (scope, element, attrs, carouselCtrl) { + element.addClass('item'); + carouselCtrl.addSlide(scope, element); + //when the scope is destroyed then remove the slide from the current slides array + scope.$on('$destroy', function() { + carouselCtrl.removeSlide(scope); + }); + + scope.$watch('active', function(active) { + $animate[active ? 'addClass' : 'removeClass'](element, 'active'); + }); + } + }; +}]) + +.animation('.item', ['$animateCss', +function($animateCss) { + var SLIDE_DIRECTION = 'uib-slideDirection'; + + function removeClass(element, className, callback) { + element.removeClass(className); + if (callback) { + callback(); + } + } + + return { + beforeAddClass: function(element, className, done) { + if (className === 'active') { + var stopped = false; + var direction = element.data(SLIDE_DIRECTION); + var directionClass = direction === 'next' ? 'left' : 'right'; + var removeClassFn = removeClass.bind(this, element, + directionClass + ' ' + direction, done); + element.addClass(direction); + + $animateCss(element, {addClass: directionClass}) + .start() + .done(removeClassFn); + + return function() { + stopped = true; + }; + } + done(); + }, + beforeRemoveClass: function (element, className, done) { + if (className === 'active') { + var stopped = false; + var direction = element.data(SLIDE_DIRECTION); + var directionClass = direction === 'next' ? 'left' : 'right'; + var removeClassFn = removeClass.bind(this, element, directionClass, done); + + $animateCss(element, {addClass: directionClass}) + .start() + .done(removeClassFn); + + return function() { + stopped = true; + }; + } + done(); + } + }; +}]); + +angular.module('ui.bootstrap.dateparser', []) + +.service('uibDateParser', ['$log', '$locale', 'dateFilter', 'orderByFilter', 'filterFilter', function($log, $locale, dateFilter, orderByFilter, filterFilter) { + // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js + var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; + + var localeId; + var formatCodeToRegex; + + this.init = function() { + localeId = $locale.id; + + this.parsers = {}; + this.formatters = {}; + + formatCodeToRegex = [ + { + key: 'yyyy', + regex: '\\d{4}', + apply: function(value) { this.year = +value; }, + formatter: function(date) { + var _date = new Date(); + _date.setFullYear(Math.abs(date.getFullYear())); + return dateFilter(_date, 'yyyy'); + } + }, + { + key: 'yy', + regex: '\\d{2}', + apply: function(value) { value = +value; this.year = value < 69 ? value + 2000 : value + 1900; }, + formatter: function(date) { + var _date = new Date(); + _date.setFullYear(Math.abs(date.getFullYear())); + return dateFilter(_date, 'yy'); + } + }, + { + key: 'y', + regex: '\\d{1,4}', + apply: function(value) { this.year = +value; }, + formatter: function(date) { + var _date = new Date(); + _date.setFullYear(Math.abs(date.getFullYear())); + return dateFilter(_date, 'y'); + } + }, + { + key: 'M!', + regex: '0?[1-9]|1[0-2]', + apply: function(value) { this.month = value - 1; }, + formatter: function(date) { + var value = date.getMonth(); + if (/^[0-9]$/.test(value)) { + return dateFilter(date, 'MM'); + } + + return dateFilter(date, 'M'); + } + }, + { + key: 'MMMM', + regex: $locale.DATETIME_FORMATS.MONTH.join('|'), + apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }, + formatter: function(date) { return dateFilter(date, 'MMMM'); } + }, + { + key: 'MMM', + regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'), + apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }, + formatter: function(date) { return dateFilter(date, 'MMM'); } + }, + { + key: 'MM', + regex: '0[1-9]|1[0-2]', + apply: function(value) { this.month = value - 1; }, + formatter: function(date) { return dateFilter(date, 'MM'); } + }, + { + key: 'M', + regex: '[1-9]|1[0-2]', + apply: function(value) { this.month = value - 1; }, + formatter: function(date) { return dateFilter(date, 'M'); } + }, + { + key: 'd!', + regex: '[0-2]?[0-9]{1}|3[0-1]{1}', + apply: function(value) { this.date = +value; }, + formatter: function(date) { + var value = date.getDate(); + if (/^[1-9]$/.test(value)) { + return dateFilter(date, 'dd'); + } + + return dateFilter(date, 'd'); + } + }, + { + key: 'dd', + regex: '[0-2][0-9]{1}|3[0-1]{1}', + apply: function(value) { this.date = +value; }, + formatter: function(date) { return dateFilter(date, 'dd'); } + }, + { + key: 'd', + regex: '[1-2]?[0-9]{1}|3[0-1]{1}', + apply: function(value) { this.date = +value; }, + formatter: function(date) { return dateFilter(date, 'd'); } + }, + { + key: 'EEEE', + regex: $locale.DATETIME_FORMATS.DAY.join('|'), + formatter: function(date) { return dateFilter(date, 'EEEE'); } + }, + { + key: 'EEE', + regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|'), + formatter: function(date) { return dateFilter(date, 'EEE'); } + }, + { + key: 'HH', + regex: '(?:0|1)[0-9]|2[0-3]', + apply: function(value) { this.hours = +value; }, + formatter: function(date) { return dateFilter(date, 'HH'); } + }, + { + key: 'hh', + regex: '0[0-9]|1[0-2]', + apply: function(value) { this.hours = +value; }, + formatter: function(date) { return dateFilter(date, 'hh'); } + }, + { + key: 'H', + regex: '1?[0-9]|2[0-3]', + apply: function(value) { this.hours = +value; }, + formatter: function(date) { return dateFilter(date, 'H'); } + }, + { + key: 'h', + regex: '[0-9]|1[0-2]', + apply: function(value) { this.hours = +value; }, + formatter: function(date) { return dateFilter(date, 'h'); } + }, + { + key: 'mm', + regex: '[0-5][0-9]', + apply: function(value) { this.minutes = +value; }, + formatter: function(date) { return dateFilter(date, 'mm'); } + }, + { + key: 'm', + regex: '[0-9]|[1-5][0-9]', + apply: function(value) { this.minutes = +value; }, + formatter: function(date) { return dateFilter(date, 'm'); } + }, + { + key: 'sss', + regex: '[0-9][0-9][0-9]', + apply: function(value) { this.milliseconds = +value; }, + formatter: function(date) { return dateFilter(date, 'sss'); } + }, + { + key: 'ss', + regex: '[0-5][0-9]', + apply: function(value) { this.seconds = +value; }, + formatter: function(date) { return dateFilter(date, 'ss'); } + }, + { + key: 's', + regex: '[0-9]|[1-5][0-9]', + apply: function(value) { this.seconds = +value; }, + formatter: function(date) { return dateFilter(date, 's'); } + }, + { + key: 'a', + regex: $locale.DATETIME_FORMATS.AMPMS.join('|'), + apply: function(value) { + if (this.hours === 12) { + this.hours = 0; + } + + if (value === 'PM') { + this.hours += 12; + } + }, + formatter: function(date) { return dateFilter(date, 'a'); } + }, + { + key: 'Z', + regex: '[+-]\\d{4}', + apply: function(value) { + var matches = value.match(/([+-])(\d{2})(\d{2})/), + sign = matches[1], + hours = matches[2], + minutes = matches[3]; + this.hours += toInt(sign + hours); + this.minutes += toInt(sign + minutes); + }, + formatter: function(date) { + return dateFilter(date, 'Z'); + } + }, + { + key: 'ww', + regex: '[0-4][0-9]|5[0-3]', + formatter: function(date) { return dateFilter(date, 'ww'); } + }, + { + key: 'w', + regex: '[0-9]|[1-4][0-9]|5[0-3]', + formatter: function(date) { return dateFilter(date, 'w'); } + }, + { + key: 'GGGG', + regex: $locale.DATETIME_FORMATS.ERANAMES.join('|').replace(/\s/g, '\\s'), + formatter: function(date) { return dateFilter(date, 'GGGG'); } + }, + { + key: 'GGG', + regex: $locale.DATETIME_FORMATS.ERAS.join('|'), + formatter: function(date) { return dateFilter(date, 'GGG'); } + }, + { + key: 'GG', + regex: $locale.DATETIME_FORMATS.ERAS.join('|'), + formatter: function(date) { return dateFilter(date, 'GG'); } + }, + { + key: 'G', + regex: $locale.DATETIME_FORMATS.ERAS.join('|'), + formatter: function(date) { return dateFilter(date, 'G'); } + } + ]; + + if (angular.version.major >= 1 && angular.version.minor > 4) { + formatCodeToRegex.push({ + key: 'LLLL', + regex: $locale.DATETIME_FORMATS.STANDALONEMONTH.join('|'), + apply: function(value) { this.month = $locale.DATETIME_FORMATS.STANDALONEMONTH.indexOf(value); }, + formatter: function(date) { return dateFilter(date, 'LLLL'); } + }); + } + }; + + this.init(); + + function getFormatCodeToRegex(key) { + return filterFilter(formatCodeToRegex, {key: key}, true)[0]; + } + + this.getParser = function (key) { + var f = getFormatCodeToRegex(key); + return f && f.apply || null; + }; + + this.overrideParser = function (key, parser) { + var f = getFormatCodeToRegex(key); + if (f && angular.isFunction(parser)) { + this.parsers = {}; + f.apply = parser; + } + }.bind(this); + + function createParser(format) { + var map = [], regex = format.split(''); + + // check for literal values + var quoteIndex = format.indexOf('\''); + if (quoteIndex > -1) { + var inLiteral = false; + format = format.split(''); + for (var i = quoteIndex; i < format.length; i++) { + if (inLiteral) { + if (format[i] === '\'') { + if (i + 1 < format.length && format[i+1] === '\'') { // escaped single quote + format[i+1] = '$'; + regex[i+1] = ''; + } else { // end of literal + regex[i] = ''; + inLiteral = false; + } + } + format[i] = '$'; + } else { + if (format[i] === '\'') { // start of literal + format[i] = '$'; + regex[i] = ''; + inLiteral = true; + } + } + } + + format = format.join(''); + } + + angular.forEach(formatCodeToRegex, function(data) { + var index = format.indexOf(data.key); + + if (index > -1) { + format = format.split(''); + + regex[index] = '(' + data.regex + ')'; + format[index] = '$'; // Custom symbol to define consumed part of format + for (var i = index + 1, n = index + data.key.length; i < n; i++) { + regex[i] = ''; + format[i] = '$'; + } + format = format.join(''); + + map.push({ + index: index, + key: data.key, + apply: data.apply, + matcher: data.regex + }); + } + }); + + return { + regex: new RegExp('^' + regex.join('') + '$'), + map: orderByFilter(map, 'index') + }; + } + + function createFormatter(format) { + var formatters = []; + var i = 0; + var formatter, literalIdx; + while (i < format.length) { + if (angular.isNumber(literalIdx)) { + if (format.charAt(i) === '\'') { + if (i + 1 >= format.length || format.charAt(i + 1) !== '\'') { + formatters.push(constructLiteralFormatter(format, literalIdx, i)); + literalIdx = null; + } + } else if (i === format.length) { + while (literalIdx < format.length) { + formatter = constructFormatterFromIdx(format, literalIdx); + formatters.push(formatter); + literalIdx = formatter.endIdx; + } + } + + i++; + continue; + } + + if (format.charAt(i) === '\'') { + literalIdx = i; + i++; + continue; + } + + formatter = constructFormatterFromIdx(format, i); + + formatters.push(formatter.parser); + i = formatter.endIdx; + } + + return formatters; + } + + function constructLiteralFormatter(format, literalIdx, endIdx) { + return function() { + return format.substr(literalIdx + 1, endIdx - literalIdx - 1); + }; + } + + function constructFormatterFromIdx(format, i) { + var currentPosStr = format.substr(i); + for (var j = 0; j < formatCodeToRegex.length; j++) { + if (new RegExp('^' + formatCodeToRegex[j].key).test(currentPosStr)) { + var data = formatCodeToRegex[j]; + return { + endIdx: i + data.key.length, + parser: data.formatter + }; + } + } + + return { + endIdx: i + 1, + parser: function() { + return currentPosStr.charAt(0); + } + }; + } + + this.filter = function(date, format) { + if (!angular.isDate(date) || isNaN(date) || !format) { + return ''; + } + + format = $locale.DATETIME_FORMATS[format] || format; + + if ($locale.id !== localeId) { + this.init(); + } + + if (!this.formatters[format]) { + this.formatters[format] = createFormatter(format); + } + + var formatters = this.formatters[format]; + + return formatters.reduce(function(str, formatter) { + return str + formatter(date); + }, ''); + }; + + this.parse = function(input, format, baseDate) { + if (!angular.isString(input) || !format) { + return input; + } + + format = $locale.DATETIME_FORMATS[format] || format; + format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&'); + + if ($locale.id !== localeId) { + this.init(); + } + + if (!this.parsers[format]) { + this.parsers[format] = createParser(format, 'apply'); + } + + var parser = this.parsers[format], + regex = parser.regex, + map = parser.map, + results = input.match(regex), + tzOffset = false; + if (results && results.length) { + var fields, dt; + if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) { + fields = { + year: baseDate.getFullYear(), + month: baseDate.getMonth(), + date: baseDate.getDate(), + hours: baseDate.getHours(), + minutes: baseDate.getMinutes(), + seconds: baseDate.getSeconds(), + milliseconds: baseDate.getMilliseconds() + }; + } else { + if (baseDate) { + $log.warn('dateparser:', 'baseDate is not a valid date'); + } + fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 }; + } + + for (var i = 1, n = results.length; i < n; i++) { + var mapper = map[i - 1]; + if (mapper.matcher === 'Z') { + tzOffset = true; + } + + if (mapper.apply) { + mapper.apply.call(fields, results[i]); + } + } + + var datesetter = tzOffset ? Date.prototype.setUTCFullYear : + Date.prototype.setFullYear; + var timesetter = tzOffset ? Date.prototype.setUTCHours : + Date.prototype.setHours; + + if (isValid(fields.year, fields.month, fields.date)) { + if (angular.isDate(baseDate) && !isNaN(baseDate.getTime()) && !tzOffset) { + dt = new Date(baseDate); + datesetter.call(dt, fields.year, fields.month, fields.date); + timesetter.call(dt, fields.hours, fields.minutes, + fields.seconds, fields.milliseconds); + } else { + dt = new Date(0); + datesetter.call(dt, fields.year, fields.month, fields.date); + timesetter.call(dt, fields.hours || 0, fields.minutes || 0, + fields.seconds || 0, fields.milliseconds || 0); + } + } + + return dt; + } + }; + + // Check if date is valid for specific month (and year for February). + // Month: 0 = Jan, 1 = Feb, etc + function isValid(year, month, date) { + if (date < 1) { + return false; + } + + if (month === 1 && date > 28) { + return date === 29 && (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0); + } + + if (month === 3 || month === 5 || month === 8 || month === 10) { + return date < 31; + } + + return true; + } + + function toInt(str) { + return parseInt(str, 10); + } + + this.toTimezone = toTimezone; + this.fromTimezone = fromTimezone; + this.timezoneToOffset = timezoneToOffset; + this.addDateMinutes = addDateMinutes; + this.convertTimezoneToLocal = convertTimezoneToLocal; + + function toTimezone(date, timezone) { + return date && timezone ? convertTimezoneToLocal(date, timezone) : date; + } + + function fromTimezone(date, timezone) { + return date && timezone ? convertTimezoneToLocal(date, timezone, true) : date; + } + + //https://github.com/angular/angular.js/blob/622c42169699ec07fc6daaa19fe6d224e5d2f70e/src/Angular.js#L1207 + function timezoneToOffset(timezone, fallback) { + timezone = timezone.replace(/:/g, ''); + var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; + return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; + } + + function addDateMinutes(date, minutes) { + date = new Date(date.getTime()); + date.setMinutes(date.getMinutes() + minutes); + return date; + } + + function convertTimezoneToLocal(date, timezone, reverse) { + reverse = reverse ? -1 : 1; + var dateTimezoneOffset = date.getTimezoneOffset(); + var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); + return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset)); + } +}]); + +// Avoiding use of ng-class as it creates a lot of watchers when a class is to be applied to +// at most one element. +angular.module('ui.bootstrap.isClass', []) +.directive('uibIsClass', [ + '$animate', +function ($animate) { + // 11111111 22222222 + var ON_REGEXP = /^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/; + // 11111111 22222222 + var IS_REGEXP = /^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/; + + var dataPerTracked = {}; + + return { + restrict: 'A', + compile: function(tElement, tAttrs) { + var linkedScopes = []; + var instances = []; + var expToData = {}; + var lastActivated = null; + var onExpMatches = tAttrs.uibIsClass.match(ON_REGEXP); + var onExp = onExpMatches[2]; + var expsStr = onExpMatches[1]; + var exps = expsStr.split(','); + + return linkFn; + + function linkFn(scope, element, attrs) { + linkedScopes.push(scope); + instances.push({ + scope: scope, + element: element + }); + + exps.forEach(function(exp, k) { + addForExp(exp, scope); + }); + + scope.$on('$destroy', removeScope); + } + + function addForExp(exp, scope) { + var matches = exp.match(IS_REGEXP); + var clazz = scope.$eval(matches[1]); + var compareWithExp = matches[2]; + var data = expToData[exp]; + if (!data) { + var watchFn = function(compareWithVal) { + var newActivated = null; + instances.some(function(instance) { + var thisVal = instance.scope.$eval(onExp); + if (thisVal === compareWithVal) { + newActivated = instance; + return true; + } + }); + if (data.lastActivated !== newActivated) { + if (data.lastActivated) { + $animate.removeClass(data.lastActivated.element, clazz); + } + if (newActivated) { + $animate.addClass(newActivated.element, clazz); + } + data.lastActivated = newActivated; + } + }; + expToData[exp] = data = { + lastActivated: null, + scope: scope, + watchFn: watchFn, + compareWithExp: compareWithExp, + watcher: scope.$watch(compareWithExp, watchFn) + }; + } + data.watchFn(scope.$eval(compareWithExp)); + } + + function removeScope(e) { + var removedScope = e.targetScope; + var index = linkedScopes.indexOf(removedScope); + linkedScopes.splice(index, 1); + instances.splice(index, 1); + if (linkedScopes.length) { + var newWatchScope = linkedScopes[0]; + angular.forEach(expToData, function(data) { + if (data.scope === removedScope) { + data.watcher = newWatchScope.$watch(data.compareWithExp, data.watchFn); + data.scope = newWatchScope; + } + }); + } else { + expToData = {}; + } + } + } + }; +}]); +angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.isClass']) + +.value('$datepickerSuppressError', false) + +.value('$datepickerLiteralWarning', true) + +.constant('uibDatepickerConfig', { + datepickerMode: 'day', + formatDay: 'dd', + formatMonth: 'MMMM', + formatYear: 'yyyy', + formatDayHeader: 'EEE', + formatDayTitle: 'MMMM yyyy', + formatMonthTitle: 'yyyy', + maxDate: null, + maxMode: 'year', + minDate: null, + minMode: 'day', + monthColumns: 3, + ngModelOptions: {}, + shortcutPropagation: false, + showWeeks: true, + yearColumns: 5, + yearRows: 4 +}) + +.controller('UibDatepickerController', ['$scope', '$element', '$attrs', '$parse', '$interpolate', '$locale', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerLiteralWarning', '$datepickerSuppressError', 'uibDateParser', + function($scope, $element, $attrs, $parse, $interpolate, $locale, $log, dateFilter, datepickerConfig, $datepickerLiteralWarning, $datepickerSuppressError, dateParser) { + var self = this, + ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl; + ngModelOptions = {}, + watchListeners = []; + + $element.addClass('uib-datepicker'); + $attrs.$set('role', 'application'); + + if (!$scope.datepickerOptions) { + $scope.datepickerOptions = {}; + } + + // Modes chain + this.modes = ['day', 'month', 'year']; + + [ + 'customClass', + 'dateDisabled', + 'datepickerMode', + 'formatDay', + 'formatDayHeader', + 'formatDayTitle', + 'formatMonth', + 'formatMonthTitle', + 'formatYear', + 'maxDate', + 'maxMode', + 'minDate', + 'minMode', + 'monthColumns', + 'showWeeks', + 'shortcutPropagation', + 'startingDay', + 'yearColumns', + 'yearRows' + ].forEach(function(key) { + switch (key) { + case 'customClass': + case 'dateDisabled': + $scope[key] = $scope.datepickerOptions[key] || angular.noop; + break; + case 'datepickerMode': + $scope.datepickerMode = angular.isDefined($scope.datepickerOptions.datepickerMode) ? + $scope.datepickerOptions.datepickerMode : datepickerConfig.datepickerMode; + break; + case 'formatDay': + case 'formatDayHeader': + case 'formatDayTitle': + case 'formatMonth': + case 'formatMonthTitle': + case 'formatYear': + self[key] = angular.isDefined($scope.datepickerOptions[key]) ? + $interpolate($scope.datepickerOptions[key])($scope.$parent) : + datepickerConfig[key]; + break; + case 'monthColumns': + case 'showWeeks': + case 'shortcutPropagation': + case 'yearColumns': + case 'yearRows': + self[key] = angular.isDefined($scope.datepickerOptions[key]) ? + $scope.datepickerOptions[key] : datepickerConfig[key]; + break; + case 'startingDay': + if (angular.isDefined($scope.datepickerOptions.startingDay)) { + self.startingDay = $scope.datepickerOptions.startingDay; + } else if (angular.isNumber(datepickerConfig.startingDay)) { + self.startingDay = datepickerConfig.startingDay; + } else { + self.startingDay = ($locale.DATETIME_FORMATS.FIRSTDAYOFWEEK + 8) % 7; + } + + break; + case 'maxDate': + case 'minDate': + $scope.$watch('datepickerOptions.' + key, function(value) { + if (value) { + if (angular.isDate(value)) { + self[key] = dateParser.fromTimezone(new Date(value), ngModelOptions.timezone); + } else { + if ($datepickerLiteralWarning) { + $log.warn('Literal date support has been deprecated, please switch to date object usage'); + } + + self[key] = new Date(dateFilter(value, 'medium')); + } + } else { + self[key] = datepickerConfig[key] ? + dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.timezone) : + null; + } + + self.refreshView(); + }); + + break; + case 'maxMode': + case 'minMode': + if ($scope.datepickerOptions[key]) { + $scope.$watch(function() { return $scope.datepickerOptions[key]; }, function(value) { + self[key] = $scope[key] = angular.isDefined(value) ? value : $scope.datepickerOptions[key]; + if (key === 'minMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) < self.modes.indexOf(self[key]) || + key === 'maxMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) > self.modes.indexOf(self[key])) { + $scope.datepickerMode = self[key]; + $scope.datepickerOptions.datepickerMode = self[key]; + } + }); + } else { + self[key] = $scope[key] = datepickerConfig[key] || null; + } + + break; + } + }); + + $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000); + + $scope.disabled = angular.isDefined($attrs.disabled) || false; + if (angular.isDefined($attrs.ngDisabled)) { + watchListeners.push($scope.$parent.$watch($attrs.ngDisabled, function(disabled) { + $scope.disabled = disabled; + self.refreshView(); + })); + } + + $scope.isActive = function(dateObject) { + if (self.compare(dateObject.date, self.activeDate) === 0) { + $scope.activeDateId = dateObject.uid; + return true; + } + return false; + }; + + this.init = function(ngModelCtrl_) { + ngModelCtrl = ngModelCtrl_; + ngModelOptions = ngModelCtrl_.$options || + $scope.datepickerOptions.ngModelOptions || + datepickerConfig.ngModelOptions; + if ($scope.datepickerOptions.initDate) { + self.activeDate = dateParser.fromTimezone($scope.datepickerOptions.initDate, ngModelOptions.timezone) || new Date(); + $scope.$watch('datepickerOptions.initDate', function(initDate) { + if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) { + self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.timezone); + self.refreshView(); + } + }); + } else { + self.activeDate = new Date(); + } + + var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : new Date(); + this.activeDate = !isNaN(date) ? + dateParser.fromTimezone(date, ngModelOptions.timezone) : + dateParser.fromTimezone(new Date(), ngModelOptions.timezone); + + ngModelCtrl.$render = function() { + self.render(); + }; + }; + + this.render = function() { + if (ngModelCtrl.$viewValue) { + var date = new Date(ngModelCtrl.$viewValue), + isValid = !isNaN(date); + + if (isValid) { + this.activeDate = dateParser.fromTimezone(date, ngModelOptions.timezone); + } else if (!$datepickerSuppressError) { + $log.error('Datepicker directive: "ng-model" value must be a Date object'); + } + } + this.refreshView(); + }; + + this.refreshView = function() { + if (this.element) { + $scope.selectedDt = null; + this._refreshView(); + if ($scope.activeDt) { + $scope.activeDateId = $scope.activeDt.uid; + } + + var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; + date = dateParser.fromTimezone(date, ngModelOptions.timezone); + ngModelCtrl.$setValidity('dateDisabled', !date || + this.element && !this.isDisabled(date)); + } + }; + + this.createDateObject = function(date, format) { + var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; + model = dateParser.fromTimezone(model, ngModelOptions.timezone); + var today = new Date(); + today = dateParser.fromTimezone(today, ngModelOptions.timezone); + var time = this.compare(date, today); + var dt = { + date: date, + label: dateParser.filter(date, format), + selected: model && this.compare(date, model) === 0, + disabled: this.isDisabled(date), + past: time < 0, + current: time === 0, + future: time > 0, + customClass: this.customClass(date) || null + }; + + if (model && this.compare(date, model) === 0) { + $scope.selectedDt = dt; + } + + if (self.activeDate && this.compare(dt.date, self.activeDate) === 0) { + $scope.activeDt = dt; + } + + return dt; + }; + + this.isDisabled = function(date) { + return $scope.disabled || + this.minDate && this.compare(date, this.minDate) < 0 || + this.maxDate && this.compare(date, this.maxDate) > 0 || + $scope.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode}); + }; + + this.customClass = function(date) { + return $scope.customClass({date: date, mode: $scope.datepickerMode}); + }; + + // Split array into smaller arrays + this.split = function(arr, size) { + var arrays = []; + while (arr.length > 0) { + arrays.push(arr.splice(0, size)); + } + return arrays; + }; + + $scope.select = function(date) { + if ($scope.datepickerMode === self.minMode) { + var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.timezone) : new Date(0, 0, 0, 0, 0, 0, 0); + dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); + dt = dateParser.toTimezone(dt, ngModelOptions.timezone); + ngModelCtrl.$setViewValue(dt); + ngModelCtrl.$render(); + } else { + self.activeDate = date; + setMode(self.modes[self.modes.indexOf($scope.datepickerMode) - 1]); + + $scope.$emit('uib:datepicker.mode'); + } + + $scope.$broadcast('uib:datepicker.focus'); + }; + + $scope.move = function(direction) { + var year = self.activeDate.getFullYear() + direction * (self.step.years || 0), + month = self.activeDate.getMonth() + direction * (self.step.months || 0); + self.activeDate.setFullYear(year, month, 1); + self.refreshView(); + }; + + $scope.toggleMode = function(direction) { + direction = direction || 1; + + if ($scope.datepickerMode === self.maxMode && direction === 1 || + $scope.datepickerMode === self.minMode && direction === -1) { + return; + } + + setMode(self.modes[self.modes.indexOf($scope.datepickerMode) + direction]); + + $scope.$emit('uib:datepicker.mode'); + }; + + // Key event mapper + $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' }; + + var focusElement = function() { + self.element[0].focus(); + }; + + // Listen for focus requests from popup directive + $scope.$on('uib:datepicker.focus', focusElement); + + $scope.keydown = function(evt) { + var key = $scope.keys[evt.which]; + + if (!key || evt.shiftKey || evt.altKey || $scope.disabled) { + return; + } + + evt.preventDefault(); + if (!self.shortcutPropagation) { + evt.stopPropagation(); + } + + if (key === 'enter' || key === 'space') { + if (self.isDisabled(self.activeDate)) { + return; // do nothing + } + $scope.select(self.activeDate); + } else if (evt.ctrlKey && (key === 'up' || key === 'down')) { + $scope.toggleMode(key === 'up' ? 1 : -1); + } else { + self.handleKeyDown(key, evt); + self.refreshView(); + } + }; + + $element.on('keydown', function(evt) { + $scope.$apply(function() { + $scope.keydown(evt); + }); + }); + + $scope.$on('$destroy', function() { + //Clear all watch listeners on destroy + while (watchListeners.length) { + watchListeners.shift()(); + } + }); + + function setMode(mode) { + $scope.datepickerMode = mode; + $scope.datepickerOptions.datepickerMode = mode; + } +}]) + +.controller('UibDaypickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { + var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + this.step = { months: 1 }; + this.element = $element; + function getDaysInMonth(year, month) { + return month === 1 && year % 4 === 0 && + (year % 100 !== 0 || year % 400 === 0) ? 29 : DAYS_IN_MONTH[month]; + } + + this.init = function(ctrl) { + angular.extend(ctrl, this); + scope.showWeeks = ctrl.showWeeks; + ctrl.refreshView(); + }; + + this.getDates = function(startDate, n) { + var dates = new Array(n), current = new Date(startDate), i = 0, date; + while (i < n) { + date = new Date(current); + dates[i++] = date; + current.setDate(current.getDate() + 1); + } + return dates; + }; + + this._refreshView = function() { + var year = this.activeDate.getFullYear(), + month = this.activeDate.getMonth(), + firstDayOfMonth = new Date(this.activeDate); + + firstDayOfMonth.setFullYear(year, month, 1); + + var difference = this.startingDay - firstDayOfMonth.getDay(), + numDisplayedFromPreviousMonth = difference > 0 ? + 7 - difference : - difference, + firstDate = new Date(firstDayOfMonth); + + if (numDisplayedFromPreviousMonth > 0) { + firstDate.setDate(-numDisplayedFromPreviousMonth + 1); + } + + // 42 is the number of days on a six-week calendar + var days = this.getDates(firstDate, 42); + for (var i = 0; i < 42; i ++) { + days[i] = angular.extend(this.createDateObject(days[i], this.formatDay), { + secondary: days[i].getMonth() !== month, + uid: scope.uniqueId + '-' + i + }); + } + + scope.labels = new Array(7); + for (var j = 0; j < 7; j++) { + scope.labels[j] = { + abbr: dateFilter(days[j].date, this.formatDayHeader), + full: dateFilter(days[j].date, 'EEEE') + }; + } + + scope.title = dateFilter(this.activeDate, this.formatDayTitle); + scope.rows = this.split(days, 7); + + if (scope.showWeeks) { + scope.weekNumbers = []; + var thursdayIndex = (4 + 7 - this.startingDay) % 7, + numWeeks = scope.rows.length; + for (var curWeek = 0; curWeek < numWeeks; curWeek++) { + scope.weekNumbers.push( + getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date)); + } + } + }; + + this.compare = function(date1, date2) { + var _date1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()); + var _date2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()); + _date1.setFullYear(date1.getFullYear()); + _date2.setFullYear(date2.getFullYear()); + return _date1 - _date2; + }; + + function getISO8601WeekNumber(date) { + var checkDate = new Date(date); + checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday + var time = checkDate.getTime(); + checkDate.setMonth(0); // Compare with Jan 1 + checkDate.setDate(1); + return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1; + } + + this.handleKeyDown = function(key, evt) { + var date = this.activeDate.getDate(); + + if (key === 'left') { + date = date - 1; + } else if (key === 'up') { + date = date - 7; + } else if (key === 'right') { + date = date + 1; + } else if (key === 'down') { + date = date + 7; + } else if (key === 'pageup' || key === 'pagedown') { + var month = this.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1); + this.activeDate.setMonth(month, 1); + date = Math.min(getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()), date); + } else if (key === 'home') { + date = 1; + } else if (key === 'end') { + date = getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()); + } + this.activeDate.setDate(date); + }; +}]) + +.controller('UibMonthpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { + this.step = { years: 1 }; + this.element = $element; + + this.init = function(ctrl) { + angular.extend(ctrl, this); + ctrl.refreshView(); + }; + + this._refreshView = function() { + var months = new Array(12), + year = this.activeDate.getFullYear(), + date; + + for (var i = 0; i < 12; i++) { + date = new Date(this.activeDate); + date.setFullYear(year, i, 1); + months[i] = angular.extend(this.createDateObject(date, this.formatMonth), { + uid: scope.uniqueId + '-' + i + }); + } + + scope.title = dateFilter(this.activeDate, this.formatMonthTitle); + scope.rows = this.split(months, this.monthColumns); + scope.yearHeaderColspan = this.monthColumns > 3 ? this.monthColumns - 2 : 1; + }; + + this.compare = function(date1, date2) { + var _date1 = new Date(date1.getFullYear(), date1.getMonth()); + var _date2 = new Date(date2.getFullYear(), date2.getMonth()); + _date1.setFullYear(date1.getFullYear()); + _date2.setFullYear(date2.getFullYear()); + return _date1 - _date2; + }; + + this.handleKeyDown = function(key, evt) { + var date = this.activeDate.getMonth(); + + if (key === 'left') { + date = date - 1; + } else if (key === 'up') { + date = date - this.monthColumns; + } else if (key === 'right') { + date = date + 1; + } else if (key === 'down') { + date = date + this.monthColumns; + } else if (key === 'pageup' || key === 'pagedown') { + var year = this.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1); + this.activeDate.setFullYear(year); + } else if (key === 'home') { + date = 0; + } else if (key === 'end') { + date = 11; + } + this.activeDate.setMonth(date); + }; +}]) + +.controller('UibYearpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { + var columns, range; + this.element = $element; + + function getStartingYear(year) { + return parseInt((year - 1) / range, 10) * range + 1; + } + + this.yearpickerInit = function() { + columns = this.yearColumns; + range = this.yearRows * columns; + this.step = { years: range }; + }; + + this._refreshView = function() { + var years = new Array(range), date; + + for (var i = 0, start = getStartingYear(this.activeDate.getFullYear()); i < range; i++) { + date = new Date(this.activeDate); + date.setFullYear(start + i, 0, 1); + years[i] = angular.extend(this.createDateObject(date, this.formatYear), { + uid: scope.uniqueId + '-' + i + }); + } + + scope.title = [years[0].label, years[range - 1].label].join(' - '); + scope.rows = this.split(years, columns); + scope.columns = columns; + }; + + this.compare = function(date1, date2) { + return date1.getFullYear() - date2.getFullYear(); + }; + + this.handleKeyDown = function(key, evt) { + var date = this.activeDate.getFullYear(); + + if (key === 'left') { + date = date - 1; + } else if (key === 'up') { + date = date - columns; + } else if (key === 'right') { + date = date + 1; + } else if (key === 'down') { + date = date + columns; + } else if (key === 'pageup' || key === 'pagedown') { + date += (key === 'pageup' ? - 1 : 1) * range; + } else if (key === 'home') { + date = getStartingYear(this.activeDate.getFullYear()); + } else if (key === 'end') { + date = getStartingYear(this.activeDate.getFullYear()) + range - 1; + } + this.activeDate.setFullYear(date); + }; +}]) + +.directive('uibDatepicker', function() { + return { + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/datepicker/datepicker.html'; + }, + scope: { + datepickerOptions: '=?' + }, + require: ['uibDatepicker', '^ngModel'], + restrict: 'A', + controller: 'UibDatepickerController', + controllerAs: 'datepicker', + link: function(scope, element, attrs, ctrls) { + var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + datepickerCtrl.init(ngModelCtrl); + } + }; +}) + +.directive('uibDaypicker', function() { + return { + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/datepicker/day.html'; + }, + require: ['^uibDatepicker', 'uibDaypicker'], + restrict: 'A', + controller: 'UibDaypickerController', + link: function(scope, element, attrs, ctrls) { + var datepickerCtrl = ctrls[0], + daypickerCtrl = ctrls[1]; + + daypickerCtrl.init(datepickerCtrl); + } + }; +}) + +.directive('uibMonthpicker', function() { + return { + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/datepicker/month.html'; + }, + require: ['^uibDatepicker', 'uibMonthpicker'], + restrict: 'A', + controller: 'UibMonthpickerController', + link: function(scope, element, attrs, ctrls) { + var datepickerCtrl = ctrls[0], + monthpickerCtrl = ctrls[1]; + + monthpickerCtrl.init(datepickerCtrl); + } + }; +}) + +.directive('uibYearpicker', function() { + return { + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/datepicker/year.html'; + }, + require: ['^uibDatepicker', 'uibYearpicker'], + restrict: 'A', + controller: 'UibYearpickerController', + link: function(scope, element, attrs, ctrls) { + var ctrl = ctrls[0]; + angular.extend(ctrl, ctrls[1]); + ctrl.yearpickerInit(); + + ctrl.refreshView(); + } + }; +}); + +angular.module('ui.bootstrap.position', []) + +/** + * A set of utility methods for working with the DOM. + * It is meant to be used where we need to absolute-position elements in + * relation to another element (this is the case for tooltips, popovers, + * typeahead suggestions etc.). + */ + .factory('$uibPosition', ['$document', '$window', function($document, $window) { + /** + * Used by scrollbarWidth() function to cache scrollbar's width. + * Do not access this variable directly, use scrollbarWidth() instead. + */ + var SCROLLBAR_WIDTH; + /** + * scrollbar on body and html element in IE and Edge overlay + * content and should be considered 0 width. + */ + var BODY_SCROLLBAR_WIDTH; + var OVERFLOW_REGEX = { + normal: /(auto|scroll)/, + hidden: /(auto|scroll|hidden)/ + }; + var PLACEMENT_REGEX = { + auto: /\s?auto?\s?/i, + primary: /^(top|bottom|left|right)$/, + secondary: /^(top|bottom|left|right|center)$/, + vertical: /^(top|bottom)$/ + }; + var BODY_REGEX = /(HTML|BODY)/; + + return { + + /** + * Provides a raw DOM element from a jQuery/jQLite element. + * + * @param {element} elem - The element to convert. + * + * @returns {element} A HTML element. + */ + getRawNode: function(elem) { + return elem.nodeName ? elem : elem[0] || elem; + }, + + /** + * Provides a parsed number for a style property. Strips + * units and casts invalid numbers to 0. + * + * @param {string} value - The style value to parse. + * + * @returns {number} A valid number. + */ + parseStyle: function(value) { + value = parseFloat(value); + return isFinite(value) ? value : 0; + }, + + /** + * Provides the closest positioned ancestor. + * + * @param {element} element - The element to get the offest parent for. + * + * @returns {element} The closest positioned ancestor. + */ + offsetParent: function(elem) { + elem = this.getRawNode(elem); + + var offsetParent = elem.offsetParent || $document[0].documentElement; + + function isStaticPositioned(el) { + return ($window.getComputedStyle(el).position || 'static') === 'static'; + } + + while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) { + offsetParent = offsetParent.offsetParent; + } + + return offsetParent || $document[0].documentElement; + }, + + /** + * Provides the scrollbar width, concept from TWBS measureScrollbar() + * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js + * In IE and Edge, scollbar on body and html element overlay and should + * return a width of 0. + * + * @returns {number} The width of the browser scollbar. + */ + scrollbarWidth: function(isBody) { + if (isBody) { + if (angular.isUndefined(BODY_SCROLLBAR_WIDTH)) { + var bodyElem = $document.find('body'); + bodyElem.addClass('uib-position-body-scrollbar-measure'); + BODY_SCROLLBAR_WIDTH = $window.innerWidth - bodyElem[0].clientWidth; + BODY_SCROLLBAR_WIDTH = isFinite(BODY_SCROLLBAR_WIDTH) ? BODY_SCROLLBAR_WIDTH : 0; + bodyElem.removeClass('uib-position-body-scrollbar-measure'); + } + return BODY_SCROLLBAR_WIDTH; + } + + if (angular.isUndefined(SCROLLBAR_WIDTH)) { + var scrollElem = angular.element('
      '); + $document.find('body').append(scrollElem); + SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth; + SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0; + scrollElem.remove(); + } + + return SCROLLBAR_WIDTH; + }, + + /** + * Provides the padding required on an element to replace the scrollbar. + * + * @returns {object} An object with the following properties: + *
        + *
      • **scrollbarWidth**: the width of the scrollbar
      • + *
      • **widthOverflow**: whether the the width is overflowing
      • + *
      • **right**: the amount of right padding on the element needed to replace the scrollbar
      • + *
      • **rightOriginal**: the amount of right padding currently on the element
      • + *
      • **heightOverflow**: whether the the height is overflowing
      • + *
      • **bottom**: the amount of bottom padding on the element needed to replace the scrollbar
      • + *
      • **bottomOriginal**: the amount of bottom padding currently on the element
      • + *
      + */ + scrollbarPadding: function(elem) { + elem = this.getRawNode(elem); + + var elemStyle = $window.getComputedStyle(elem); + var paddingRight = this.parseStyle(elemStyle.paddingRight); + var paddingBottom = this.parseStyle(elemStyle.paddingBottom); + var scrollParent = this.scrollParent(elem, false, true); + var scrollbarWidth = this.scrollbarWidth(BODY_REGEX.test(scrollParent.tagName)); + + return { + scrollbarWidth: scrollbarWidth, + widthOverflow: scrollParent.scrollWidth > scrollParent.clientWidth, + right: paddingRight + scrollbarWidth, + originalRight: paddingRight, + heightOverflow: scrollParent.scrollHeight > scrollParent.clientHeight, + bottom: paddingBottom + scrollbarWidth, + originalBottom: paddingBottom + }; + }, + + /** + * Checks to see if the element is scrollable. + * + * @param {element} elem - The element to check. + * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered, + * default is false. + * + * @returns {boolean} Whether the element is scrollable. + */ + isScrollable: function(elem, includeHidden) { + elem = this.getRawNode(elem); + + var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal; + var elemStyle = $window.getComputedStyle(elem); + return overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX); + }, + + /** + * Provides the closest scrollable ancestor. + * A port of the jQuery UI scrollParent method: + * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js + * + * @param {element} elem - The element to find the scroll parent of. + * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered, + * default is false. + * @param {boolean=} [includeSelf=false] - Should the element being passed be + * included in the scrollable llokup. + * + * @returns {element} A HTML element. + */ + scrollParent: function(elem, includeHidden, includeSelf) { + elem = this.getRawNode(elem); + + var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal; + var documentEl = $document[0].documentElement; + var elemStyle = $window.getComputedStyle(elem); + if (includeSelf && overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX)) { + return elem; + } + var excludeStatic = elemStyle.position === 'absolute'; + var scrollParent = elem.parentElement || documentEl; + + if (scrollParent === documentEl || elemStyle.position === 'fixed') { + return documentEl; + } + + while (scrollParent.parentElement && scrollParent !== documentEl) { + var spStyle = $window.getComputedStyle(scrollParent); + if (excludeStatic && spStyle.position !== 'static') { + excludeStatic = false; + } + + if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) { + break; + } + scrollParent = scrollParent.parentElement; + } + + return scrollParent; + }, + + /** + * Provides read-only equivalent of jQuery's position function: + * http://api.jquery.com/position/ - distance to closest positioned + * ancestor. Does not account for margins by default like jQuery position. + * + * @param {element} elem - The element to caclulate the position on. + * @param {boolean=} [includeMargins=false] - Should margins be accounted + * for, default is false. + * + * @returns {object} An object with the following properties: + *
        + *
      • **width**: the width of the element
      • + *
      • **height**: the height of the element
      • + *
      • **top**: distance to top edge of offset parent
      • + *
      • **left**: distance to left edge of offset parent
      • + *
      + */ + position: function(elem, includeMagins) { + elem = this.getRawNode(elem); + + var elemOffset = this.offset(elem); + if (includeMagins) { + var elemStyle = $window.getComputedStyle(elem); + elemOffset.top -= this.parseStyle(elemStyle.marginTop); + elemOffset.left -= this.parseStyle(elemStyle.marginLeft); + } + var parent = this.offsetParent(elem); + var parentOffset = {top: 0, left: 0}; + + if (parent !== $document[0].documentElement) { + parentOffset = this.offset(parent); + parentOffset.top += parent.clientTop - parent.scrollTop; + parentOffset.left += parent.clientLeft - parent.scrollLeft; + } + + return { + width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth), + height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight), + top: Math.round(elemOffset.top - parentOffset.top), + left: Math.round(elemOffset.left - parentOffset.left) + }; + }, + + /** + * Provides read-only equivalent of jQuery's offset function: + * http://api.jquery.com/offset/ - distance to viewport. Does + * not account for borders, margins, or padding on the body + * element. + * + * @param {element} elem - The element to calculate the offset on. + * + * @returns {object} An object with the following properties: + *
        + *
      • **width**: the width of the element
      • + *
      • **height**: the height of the element
      • + *
      • **top**: distance to top edge of viewport
      • + *
      • **right**: distance to bottom edge of viewport
      • + *
      + */ + offset: function(elem) { + elem = this.getRawNode(elem); + + var elemBCR = elem.getBoundingClientRect(); + return { + width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth), + height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight), + top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)), + left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)) + }; + }, + + /** + * Provides offset distance to the closest scrollable ancestor + * or viewport. Accounts for border and scrollbar width. + * + * Right and bottom dimensions represent the distance to the + * respective edge of the viewport element. If the element + * edge extends beyond the viewport, a negative value will be + * reported. + * + * @param {element} elem - The element to get the viewport offset for. + * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead + * of the first scrollable element, default is false. + * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element + * be accounted for, default is true. + * + * @returns {object} An object with the following properties: + *
        + *
      • **top**: distance to the top content edge of viewport element
      • + *
      • **bottom**: distance to the bottom content edge of viewport element
      • + *
      • **left**: distance to the left content edge of viewport element
      • + *
      • **right**: distance to the right content edge of viewport element
      • + *
      + */ + viewportOffset: function(elem, useDocument, includePadding) { + elem = this.getRawNode(elem); + includePadding = includePadding !== false ? true : false; + + var elemBCR = elem.getBoundingClientRect(); + var offsetBCR = {top: 0, left: 0, bottom: 0, right: 0}; + + var offsetParent = useDocument ? $document[0].documentElement : this.scrollParent(elem); + var offsetParentBCR = offsetParent.getBoundingClientRect(); + + offsetBCR.top = offsetParentBCR.top + offsetParent.clientTop; + offsetBCR.left = offsetParentBCR.left + offsetParent.clientLeft; + if (offsetParent === $document[0].documentElement) { + offsetBCR.top += $window.pageYOffset; + offsetBCR.left += $window.pageXOffset; + } + offsetBCR.bottom = offsetBCR.top + offsetParent.clientHeight; + offsetBCR.right = offsetBCR.left + offsetParent.clientWidth; + + if (includePadding) { + var offsetParentStyle = $window.getComputedStyle(offsetParent); + offsetBCR.top += this.parseStyle(offsetParentStyle.paddingTop); + offsetBCR.bottom -= this.parseStyle(offsetParentStyle.paddingBottom); + offsetBCR.left += this.parseStyle(offsetParentStyle.paddingLeft); + offsetBCR.right -= this.parseStyle(offsetParentStyle.paddingRight); + } + + return { + top: Math.round(elemBCR.top - offsetBCR.top), + bottom: Math.round(offsetBCR.bottom - elemBCR.bottom), + left: Math.round(elemBCR.left - offsetBCR.left), + right: Math.round(offsetBCR.right - elemBCR.right) + }; + }, + + /** + * Provides an array of placement values parsed from a placement string. + * Along with the 'auto' indicator, supported placement strings are: + *
        + *
      • top: element on top, horizontally centered on host element.
      • + *
      • top-left: element on top, left edge aligned with host element left edge.
      • + *
      • top-right: element on top, lerightft edge aligned with host element right edge.
      • + *
      • bottom: element on bottom, horizontally centered on host element.
      • + *
      • bottom-left: element on bottom, left edge aligned with host element left edge.
      • + *
      • bottom-right: element on bottom, right edge aligned with host element right edge.
      • + *
      • left: element on left, vertically centered on host element.
      • + *
      • left-top: element on left, top edge aligned with host element top edge.
      • + *
      • left-bottom: element on left, bottom edge aligned with host element bottom edge.
      • + *
      • right: element on right, vertically centered on host element.
      • + *
      • right-top: element on right, top edge aligned with host element top edge.
      • + *
      • right-bottom: element on right, bottom edge aligned with host element bottom edge.
      • + *
      + * A placement string with an 'auto' indicator is expected to be + * space separated from the placement, i.e: 'auto bottom-left' If + * the primary and secondary placement values do not match 'top, + * bottom, left, right' then 'top' will be the primary placement and + * 'center' will be the secondary placement. If 'auto' is passed, true + * will be returned as the 3rd value of the array. + * + * @param {string} placement - The placement string to parse. + * + * @returns {array} An array with the following values + *
        + *
      • **[0]**: The primary placement.
      • + *
      • **[1]**: The secondary placement.
      • + *
      • **[2]**: If auto is passed: true, else undefined.
      • + *
      + */ + parsePlacement: function(placement) { + var autoPlace = PLACEMENT_REGEX.auto.test(placement); + if (autoPlace) { + placement = placement.replace(PLACEMENT_REGEX.auto, ''); + } + + placement = placement.split('-'); + + placement[0] = placement[0] || 'top'; + if (!PLACEMENT_REGEX.primary.test(placement[0])) { + placement[0] = 'top'; + } + + placement[1] = placement[1] || 'center'; + if (!PLACEMENT_REGEX.secondary.test(placement[1])) { + placement[1] = 'center'; + } + + if (autoPlace) { + placement[2] = true; + } else { + placement[2] = false; + } + + return placement; + }, + + /** + * Provides coordinates for an element to be positioned relative to + * another element. Passing 'auto' as part of the placement parameter + * will enable smart placement - where the element fits. i.e: + * 'auto left-top' will check to see if there is enough space to the left + * of the hostElem to fit the targetElem, if not place right (same for secondary + * top placement). Available space is calculated using the viewportOffset + * function. + * + * @param {element} hostElem - The element to position against. + * @param {element} targetElem - The element to position. + * @param {string=} [placement=top] - The placement for the targetElem, + * default is 'top'. 'center' is assumed as secondary placement for + * 'top', 'left', 'right', and 'bottom' placements. Available placements are: + *
        + *
      • top
      • + *
      • top-right
      • + *
      • top-left
      • + *
      • bottom
      • + *
      • bottom-left
      • + *
      • bottom-right
      • + *
      • left
      • + *
      • left-top
      • + *
      • left-bottom
      • + *
      • right
      • + *
      • right-top
      • + *
      • right-bottom
      • + *
      + * @param {boolean=} [appendToBody=false] - Should the top and left values returned + * be calculated from the body element, default is false. + * + * @returns {object} An object with the following properties: + *
        + *
      • **top**: Value for targetElem top.
      • + *
      • **left**: Value for targetElem left.
      • + *
      • **placement**: The resolved placement.
      • + *
      + */ + positionElements: function(hostElem, targetElem, placement, appendToBody) { + hostElem = this.getRawNode(hostElem); + targetElem = this.getRawNode(targetElem); + + // need to read from prop to support tests. + var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth'); + var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight'); + + placement = this.parsePlacement(placement); + + var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem); + var targetElemPos = {top: 0, left: 0, placement: ''}; + + if (placement[2]) { + var viewportOffset = this.viewportOffset(hostElem, appendToBody); + + var targetElemStyle = $window.getComputedStyle(targetElem); + var adjustedSize = { + width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))), + height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom))) + }; + + placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' : + placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' : + placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' : + placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' : + placement[0]; + + placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' : + placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' : + placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' : + placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' : + placement[1]; + + if (placement[1] === 'center') { + if (PLACEMENT_REGEX.vertical.test(placement[0])) { + var xOverflow = hostElemPos.width / 2 - targetWidth / 2; + if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) { + placement[1] = 'left'; + } else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) { + placement[1] = 'right'; + } + } else { + var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2; + if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) { + placement[1] = 'top'; + } else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) { + placement[1] = 'bottom'; + } + } + } + } + + switch (placement[0]) { + case 'top': + targetElemPos.top = hostElemPos.top - targetHeight; + break; + case 'bottom': + targetElemPos.top = hostElemPos.top + hostElemPos.height; + break; + case 'left': + targetElemPos.left = hostElemPos.left - targetWidth; + break; + case 'right': + targetElemPos.left = hostElemPos.left + hostElemPos.width; + break; + } + + switch (placement[1]) { + case 'top': + targetElemPos.top = hostElemPos.top; + break; + case 'bottom': + targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight; + break; + case 'left': + targetElemPos.left = hostElemPos.left; + break; + case 'right': + targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth; + break; + case 'center': + if (PLACEMENT_REGEX.vertical.test(placement[0])) { + targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2; + } else { + targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2; + } + break; + } + + targetElemPos.top = Math.round(targetElemPos.top); + targetElemPos.left = Math.round(targetElemPos.left); + targetElemPos.placement = placement[1] === 'center' ? placement[0] : placement[0] + '-' + placement[1]; + + return targetElemPos; + }, + + /** + * Provides a way to adjust the top positioning after first + * render to correctly align element to top after content + * rendering causes resized element height + * + * @param {array} placementClasses - The array of strings of classes + * element should have. + * @param {object} containerPosition - The object with container + * position information + * @param {number} initialHeight - The initial height for the elem. + * @param {number} currentHeight - The current height for the elem. + */ + adjustTop: function(placementClasses, containerPosition, initialHeight, currentHeight) { + if (placementClasses.indexOf('top') !== -1 && initialHeight !== currentHeight) { + return { + top: containerPosition.top - currentHeight + 'px' + }; + } + }, + + /** + * Provides a way for positioning tooltip & dropdown + * arrows when using placement options beyond the standard + * left, right, top, or bottom. + * + * @param {element} elem - The tooltip/dropdown element. + * @param {string} placement - The placement for the elem. + */ + positionArrow: function(elem, placement) { + elem = this.getRawNode(elem); + + var innerElem = elem.querySelector('.tooltip-inner, .popover-inner'); + if (!innerElem) { + return; + } + + var isTooltip = angular.element(innerElem).hasClass('tooltip-inner'); + + var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow'); + if (!arrowElem) { + return; + } + + var arrowCss = { + top: '', + bottom: '', + left: '', + right: '' + }; + + placement = this.parsePlacement(placement); + if (placement[1] === 'center') { + // no adjustment necessary - just reset styles + angular.element(arrowElem).css(arrowCss); + return; + } + + var borderProp = 'border-' + placement[0] + '-width'; + var borderWidth = $window.getComputedStyle(arrowElem)[borderProp]; + + var borderRadiusProp = 'border-'; + if (PLACEMENT_REGEX.vertical.test(placement[0])) { + borderRadiusProp += placement[0] + '-' + placement[1]; + } else { + borderRadiusProp += placement[1] + '-' + placement[0]; + } + borderRadiusProp += '-radius'; + var borderRadius = $window.getComputedStyle(isTooltip ? innerElem : elem)[borderRadiusProp]; + + switch (placement[0]) { + case 'top': + arrowCss.bottom = isTooltip ? '0' : '-' + borderWidth; + break; + case 'bottom': + arrowCss.top = isTooltip ? '0' : '-' + borderWidth; + break; + case 'left': + arrowCss.right = isTooltip ? '0' : '-' + borderWidth; + break; + case 'right': + arrowCss.left = isTooltip ? '0' : '-' + borderWidth; + break; + } + + arrowCss[placement[1]] = borderRadius; + + angular.element(arrowElem).css(arrowCss); + } + }; + }]); + +angular.module('ui.bootstrap.datepickerPopup', ['ui.bootstrap.datepicker', 'ui.bootstrap.position']) + +.value('$datepickerPopupLiteralWarning', true) + +.constant('uibDatepickerPopupConfig', { + altInputFormats: [], + appendToBody: false, + clearText: 'Clear', + closeOnDateSelection: true, + closeText: 'Done', + currentText: 'Today', + datepickerPopup: 'yyyy-MM-dd', + datepickerPopupTemplateUrl: 'uib/template/datepickerPopup/popup.html', + datepickerTemplateUrl: 'uib/template/datepicker/datepicker.html', + html5Types: { + date: 'yyyy-MM-dd', + 'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss', + 'month': 'yyyy-MM' + }, + onOpenFocus: true, + showButtonBar: true, + placement: 'auto bottom-left' +}) + +.controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$log', '$parse', '$window', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout', 'uibDatepickerConfig', '$datepickerPopupLiteralWarning', +function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout, datepickerConfig, $datepickerPopupLiteralWarning) { + var cache = {}, + isHtml5DateInput = false; + var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus, + datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl, scrollParentEl, + ngModel, ngModelOptions, $popup, altInputFormats, watchListeners = []; + + this.init = function(_ngModel_) { + ngModel = _ngModel_; + ngModelOptions = angular.isObject(_ngModel_.$options) ? + _ngModel_.$options : + { + timezone: null + }; + closeOnDateSelection = angular.isDefined($attrs.closeOnDateSelection) ? + $scope.$parent.$eval($attrs.closeOnDateSelection) : + datepickerPopupConfig.closeOnDateSelection; + appendToBody = angular.isDefined($attrs.datepickerAppendToBody) ? + $scope.$parent.$eval($attrs.datepickerAppendToBody) : + datepickerPopupConfig.appendToBody; + onOpenFocus = angular.isDefined($attrs.onOpenFocus) ? + $scope.$parent.$eval($attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus; + datepickerPopupTemplateUrl = angular.isDefined($attrs.datepickerPopupTemplateUrl) ? + $attrs.datepickerPopupTemplateUrl : + datepickerPopupConfig.datepickerPopupTemplateUrl; + datepickerTemplateUrl = angular.isDefined($attrs.datepickerTemplateUrl) ? + $attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl; + altInputFormats = angular.isDefined($attrs.altInputFormats) ? + $scope.$parent.$eval($attrs.altInputFormats) : + datepickerPopupConfig.altInputFormats; + + $scope.showButtonBar = angular.isDefined($attrs.showButtonBar) ? + $scope.$parent.$eval($attrs.showButtonBar) : + datepickerPopupConfig.showButtonBar; + + if (datepickerPopupConfig.html5Types[$attrs.type]) { + dateFormat = datepickerPopupConfig.html5Types[$attrs.type]; + isHtml5DateInput = true; + } else { + dateFormat = $attrs.uibDatepickerPopup || datepickerPopupConfig.datepickerPopup; + $attrs.$observe('uibDatepickerPopup', function(value, oldValue) { + var newDateFormat = value || datepickerPopupConfig.datepickerPopup; + // Invalidate the $modelValue to ensure that formatters re-run + // FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764 + if (newDateFormat !== dateFormat) { + dateFormat = newDateFormat; + ngModel.$modelValue = null; + + if (!dateFormat) { + throw new Error('uibDatepickerPopup must have a date format specified.'); + } + } + }); + } + + if (!dateFormat) { + throw new Error('uibDatepickerPopup must have a date format specified.'); + } + + if (isHtml5DateInput && $attrs.uibDatepickerPopup) { + throw new Error('HTML5 date input types do not support custom formats.'); + } + + // popup element used to display calendar + popupEl = angular.element('
      '); + + popupEl.attr({ + 'ng-model': 'date', + 'ng-change': 'dateSelection(date)', + 'template-url': datepickerPopupTemplateUrl + }); + + // datepicker element + datepickerEl = angular.element(popupEl.children()[0]); + datepickerEl.attr('template-url', datepickerTemplateUrl); + + if (!$scope.datepickerOptions) { + $scope.datepickerOptions = {}; + } + + if (isHtml5DateInput) { + if ($attrs.type === 'month') { + $scope.datepickerOptions.datepickerMode = 'month'; + $scope.datepickerOptions.minMode = 'month'; + } + } + + datepickerEl.attr('datepicker-options', 'datepickerOptions'); + + if (!isHtml5DateInput) { + // Internal API to maintain the correct ng-invalid-[key] class + ngModel.$$parserName = 'date'; + ngModel.$validators.date = validator; + ngModel.$parsers.unshift(parseDate); + ngModel.$formatters.push(function(value) { + if (ngModel.$isEmpty(value)) { + $scope.date = value; + return value; + } + + if (angular.isNumber(value)) { + value = new Date(value); + } + + $scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone); + + return dateParser.filter($scope.date, dateFormat); + }); + } else { + ngModel.$formatters.push(function(value) { + $scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone); + return value; + }); + } + + // Detect changes in the view from the text box + ngModel.$viewChangeListeners.push(function() { + $scope.date = parseDateString(ngModel.$viewValue); + }); + + $element.on('keydown', inputKeydownBind); + + $popup = $compile(popupEl)($scope); + // Prevent jQuery cache memory leak (template is now redundant after linking) + popupEl.remove(); + + if (appendToBody) { + $document.find('body').append($popup); + } else { + $element.after($popup); + } + + $scope.$on('$destroy', function() { + if ($scope.isOpen === true) { + if (!$rootScope.$$phase) { + $scope.$apply(function() { + $scope.isOpen = false; + }); + } + } + + $popup.remove(); + $element.off('keydown', inputKeydownBind); + $document.off('click', documentClickBind); + if (scrollParentEl) { + scrollParentEl.off('scroll', positionPopup); + } + angular.element($window).off('resize', positionPopup); + + //Clear all watch listeners on destroy + while (watchListeners.length) { + watchListeners.shift()(); + } + }); + }; + + $scope.getText = function(key) { + return $scope[key + 'Text'] || datepickerPopupConfig[key + 'Text']; + }; + + $scope.isDisabled = function(date) { + if (date === 'today') { + date = dateParser.fromTimezone(new Date(), ngModelOptions.timezone); + } + + var dates = {}; + angular.forEach(['minDate', 'maxDate'], function(key) { + if (!$scope.datepickerOptions[key]) { + dates[key] = null; + } else if (angular.isDate($scope.datepickerOptions[key])) { + dates[key] = new Date($scope.datepickerOptions[key]); + } else { + if ($datepickerPopupLiteralWarning) { + $log.warn('Literal date support has been deprecated, please switch to date object usage'); + } + + dates[key] = new Date(dateFilter($scope.datepickerOptions[key], 'medium')); + } + }); + + return $scope.datepickerOptions && + dates.minDate && $scope.compare(date, dates.minDate) < 0 || + dates.maxDate && $scope.compare(date, dates.maxDate) > 0; + }; + + $scope.compare = function(date1, date2) { + return new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()); + }; + + // Inner change + $scope.dateSelection = function(dt) { + $scope.date = dt; + var date = $scope.date ? dateParser.filter($scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function + $element.val(date); + ngModel.$setViewValue(date); + + if (closeOnDateSelection) { + $scope.isOpen = false; + $element[0].focus(); + } + }; + + $scope.keydown = function(evt) { + if (evt.which === 27) { + evt.stopPropagation(); + $scope.isOpen = false; + $element[0].focus(); + } + }; + + $scope.select = function(date, evt) { + evt.stopPropagation(); + + if (date === 'today') { + var today = new Date(); + if (angular.isDate($scope.date)) { + date = new Date($scope.date); + date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate()); + } else { + date = dateParser.fromTimezone(today, ngModelOptions.timezone); + date.setHours(0, 0, 0, 0); + } + } + $scope.dateSelection(date); + }; + + $scope.close = function(evt) { + evt.stopPropagation(); + + $scope.isOpen = false; + $element[0].focus(); + }; + + $scope.disabled = angular.isDefined($attrs.disabled) || false; + if ($attrs.ngDisabled) { + watchListeners.push($scope.$parent.$watch($parse($attrs.ngDisabled), function(disabled) { + $scope.disabled = disabled; + })); + } + + $scope.$watch('isOpen', function(value) { + if (value) { + if (!$scope.disabled) { + $timeout(function() { + positionPopup(); + + if (onOpenFocus) { + $scope.$broadcast('uib:datepicker.focus'); + } + + $document.on('click', documentClickBind); + + var placement = $attrs.popupPlacement ? $attrs.popupPlacement : datepickerPopupConfig.placement; + if (appendToBody || $position.parsePlacement(placement)[2]) { + scrollParentEl = scrollParentEl || angular.element($position.scrollParent($element)); + if (scrollParentEl) { + scrollParentEl.on('scroll', positionPopup); + } + } else { + scrollParentEl = null; + } + + angular.element($window).on('resize', positionPopup); + }, 0, false); + } else { + $scope.isOpen = false; + } + } else { + $document.off('click', documentClickBind); + if (scrollParentEl) { + scrollParentEl.off('scroll', positionPopup); + } + angular.element($window).off('resize', positionPopup); + } + }); + + function cameltoDash(string) { + return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); }); + } + + function parseDateString(viewValue) { + var date = dateParser.parse(viewValue, dateFormat, $scope.date); + if (isNaN(date)) { + for (var i = 0; i < altInputFormats.length; i++) { + date = dateParser.parse(viewValue, altInputFormats[i], $scope.date); + if (!isNaN(date)) { + return date; + } + } + } + return date; + } + + function parseDate(viewValue) { + if (angular.isNumber(viewValue)) { + // presumably timestamp to date object + viewValue = new Date(viewValue); + } + + if (!viewValue) { + return null; + } + + if (angular.isDate(viewValue) && !isNaN(viewValue)) { + return viewValue; + } + + if (angular.isString(viewValue)) { + var date = parseDateString(viewValue); + if (!isNaN(date)) { + return dateParser.toTimezone(date, ngModelOptions.timezone); + } + } + + return ngModel.$options && ngModel.$options.allowInvalid ? viewValue : undefined; + } + + function validator(modelValue, viewValue) { + var value = modelValue || viewValue; + + if (!$attrs.ngRequired && !value) { + return true; + } + + if (angular.isNumber(value)) { + value = new Date(value); + } + + if (!value) { + return true; + } + + if (angular.isDate(value) && !isNaN(value)) { + return true; + } + + if (angular.isString(value)) { + return !isNaN(parseDateString(value)); + } + + return false; + } + + function documentClickBind(event) { + if (!$scope.isOpen && $scope.disabled) { + return; + } + + var popup = $popup[0]; + var dpContainsTarget = $element[0].contains(event.target); + // The popup node may not be an element node + // In some browsers (IE) only element nodes have the 'contains' function + var popupContainsTarget = popup.contains !== undefined && popup.contains(event.target); + if ($scope.isOpen && !(dpContainsTarget || popupContainsTarget)) { + $scope.$apply(function() { + $scope.isOpen = false; + }); + } + } + + function inputKeydownBind(evt) { + if (evt.which === 27 && $scope.isOpen) { + evt.preventDefault(); + evt.stopPropagation(); + $scope.$apply(function() { + $scope.isOpen = false; + }); + $element[0].focus(); + } else if (evt.which === 40 && !$scope.isOpen) { + evt.preventDefault(); + evt.stopPropagation(); + $scope.$apply(function() { + $scope.isOpen = true; + }); + } + } + + function positionPopup() { + if ($scope.isOpen) { + var dpElement = angular.element($popup[0].querySelector('.uib-datepicker-popup')); + var placement = $attrs.popupPlacement ? $attrs.popupPlacement : datepickerPopupConfig.placement; + var position = $position.positionElements($element, dpElement, placement, appendToBody); + dpElement.css({top: position.top + 'px', left: position.left + 'px'}); + if (dpElement.hasClass('uib-position-measure')) { + dpElement.removeClass('uib-position-measure'); + } + } + } + + $scope.$on('uib:datepicker.mode', function() { + $timeout(positionPopup, 0, false); + }); +}]) + +.directive('uibDatepickerPopup', function() { + return { + require: ['ngModel', 'uibDatepickerPopup'], + controller: 'UibDatepickerPopupController', + scope: { + datepickerOptions: '=?', + isOpen: '=?', + currentText: '@', + clearText: '@', + closeText: '@' + }, + link: function(scope, element, attrs, ctrls) { + var ngModel = ctrls[0], + ctrl = ctrls[1]; + + ctrl.init(ngModel); + } + }; +}) + +.directive('uibDatepickerPopupWrap', function() { + return { + restrict: 'A', + transclude: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/datepickerPopup/popup.html'; + } + }; +}); + +angular.module('ui.bootstrap.debounce', []) +/** + * A helper, internal service that debounces a function + */ + .factory('$$debounce', ['$timeout', function($timeout) { + return function(callback, debounceTime) { + var timeoutPromise; + + return function() { + var self = this; + var args = Array.prototype.slice.call(arguments); + if (timeoutPromise) { + $timeout.cancel(timeoutPromise); + } + + timeoutPromise = $timeout(function() { + callback.apply(self, args); + }, debounceTime); + }; + }; + }]); + +angular.module('ui.bootstrap.multiMap', []) +/** + * A helper, internal data structure that stores all references attached to key + */ + .factory('$$multiMap', function() { + return { + createNew: function() { + var map = {}; + + return { + entries: function() { + return Object.keys(map).map(function(key) { + return { + key: key, + value: map[key] + }; + }); + }, + get: function(key) { + return map[key]; + }, + hasKey: function(key) { + return !!map[key]; + }, + keys: function() { + return Object.keys(map); + }, + put: function(key, value) { + if (!map[key]) { + map[key] = []; + } + + map[key].push(value); + }, + remove: function(key, value) { + var values = map[key]; + + if (!values) { + return; + } + + var idx = values.indexOf(value); + + if (idx !== -1) { + values.splice(idx, 1); + } + + if (!values.length) { + delete map[key]; + } + } + }; + } + }; + }); + +angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.multiMap', 'ui.bootstrap.position']) + +.constant('uibDropdownConfig', { + appendToOpenClass: 'uib-dropdown-open', + openClass: 'open' +}) + +.service('uibDropdownService', ['$document', '$rootScope', '$$multiMap', function($document, $rootScope, $$multiMap) { + var openScope = null; + var openedContainers = $$multiMap.createNew(); + + this.isOnlyOpen = function(dropdownScope, appendTo) { + var openedDropdowns = openedContainers.get(appendTo); + if (openedDropdowns) { + var openDropdown = openedDropdowns.reduce(function(toClose, dropdown) { + if (dropdown.scope === dropdownScope) { + return dropdown; + } + + return toClose; + }, {}); + if (openDropdown) { + return openedDropdowns.length === 1; + } + } + + return false; + }; + + this.open = function(dropdownScope, element, appendTo) { + if (!openScope) { + $document.on('click', closeDropdown); + } + + if (openScope && openScope !== dropdownScope) { + openScope.isOpen = false; + } + + openScope = dropdownScope; + + if (!appendTo) { + return; + } + + var openedDropdowns = openedContainers.get(appendTo); + if (openedDropdowns) { + var openedScopes = openedDropdowns.map(function(dropdown) { + return dropdown.scope; + }); + if (openedScopes.indexOf(dropdownScope) === -1) { + openedContainers.put(appendTo, { + scope: dropdownScope + }); + } + } else { + openedContainers.put(appendTo, { + scope: dropdownScope + }); + } + }; + + this.close = function(dropdownScope, element, appendTo) { + if (openScope === dropdownScope) { + $document.off('click', closeDropdown); + $document.off('keydown', this.keybindFilter); + openScope = null; + } + + if (!appendTo) { + return; + } + + var openedDropdowns = openedContainers.get(appendTo); + if (openedDropdowns) { + var dropdownToClose = openedDropdowns.reduce(function(toClose, dropdown) { + if (dropdown.scope === dropdownScope) { + return dropdown; + } + + return toClose; + }, {}); + if (dropdownToClose) { + openedContainers.remove(appendTo, dropdownToClose); + } + } + }; + + var closeDropdown = function(evt) { + // This method may still be called during the same mouse event that + // unbound this event handler. So check openScope before proceeding. + if (!openScope) { return; } + + if (evt && openScope.getAutoClose() === 'disabled') { return; } + + if (evt && evt.which === 3) { return; } + + var toggleElement = openScope.getToggleElement(); + if (evt && toggleElement && toggleElement[0].contains(evt.target)) { + return; + } + + var dropdownElement = openScope.getDropdownElement(); + if (evt && openScope.getAutoClose() === 'outsideClick' && + dropdownElement && dropdownElement[0].contains(evt.target)) { + return; + } + + openScope.focusToggleElement(); + openScope.isOpen = false; + + if (!$rootScope.$$phase) { + openScope.$apply(); + } + }; + + this.keybindFilter = function(evt) { + if (!openScope) { + // see this.close as ESC could have been pressed which kills the scope so we can not proceed + return; + } + + var dropdownElement = openScope.getDropdownElement(); + var toggleElement = openScope.getToggleElement(); + var dropdownElementTargeted = dropdownElement && dropdownElement[0].contains(evt.target); + var toggleElementTargeted = toggleElement && toggleElement[0].contains(evt.target); + if (evt.which === 27) { + evt.stopPropagation(); + openScope.focusToggleElement(); + closeDropdown(); + } else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen && (dropdownElementTargeted || toggleElementTargeted)) { + evt.preventDefault(); + evt.stopPropagation(); + openScope.focusDropdownEntry(evt.which); + } + }; +}]) + +.controller('UibDropdownController', ['$scope', '$element', '$attrs', '$parse', 'uibDropdownConfig', 'uibDropdownService', '$animate', '$uibPosition', '$document', '$compile', '$templateRequest', function($scope, $element, $attrs, $parse, dropdownConfig, uibDropdownService, $animate, $position, $document, $compile, $templateRequest) { + var self = this, + scope = $scope.$new(), // create a child scope so we are not polluting original one + templateScope, + appendToOpenClass = dropdownConfig.appendToOpenClass, + openClass = dropdownConfig.openClass, + getIsOpen, + setIsOpen = angular.noop, + toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop, + appendToBody = false, + appendTo = null, + keynavEnabled = false, + selectedOption = null, + body = $document.find('body'); + + $element.addClass('dropdown'); + + this.init = function() { + if ($attrs.isOpen) { + getIsOpen = $parse($attrs.isOpen); + setIsOpen = getIsOpen.assign; + + $scope.$watch(getIsOpen, function(value) { + scope.isOpen = !!value; + }); + } + + if (angular.isDefined($attrs.dropdownAppendTo)) { + var appendToEl = $parse($attrs.dropdownAppendTo)(scope); + if (appendToEl) { + appendTo = angular.element(appendToEl); + } + } + + appendToBody = angular.isDefined($attrs.dropdownAppendToBody); + keynavEnabled = angular.isDefined($attrs.keyboardNav); + + if (appendToBody && !appendTo) { + appendTo = body; + } + + if (appendTo && self.dropdownMenu) { + appendTo.append(self.dropdownMenu); + $element.on('$destroy', function handleDestroyEvent() { + self.dropdownMenu.remove(); + }); + } + }; + + this.toggle = function(open) { + scope.isOpen = arguments.length ? !!open : !scope.isOpen; + if (angular.isFunction(setIsOpen)) { + setIsOpen(scope, scope.isOpen); + } + + return scope.isOpen; + }; + + // Allow other directives to watch status + this.isOpen = function() { + return scope.isOpen; + }; + + scope.getToggleElement = function() { + return self.toggleElement; + }; + + scope.getAutoClose = function() { + return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled' + }; + + scope.getElement = function() { + return $element; + }; + + scope.isKeynavEnabled = function() { + return keynavEnabled; + }; + + scope.focusDropdownEntry = function(keyCode) { + var elems = self.dropdownMenu ? //If append to body is used. + angular.element(self.dropdownMenu).find('a') : + $element.find('ul').eq(0).find('a'); + + switch (keyCode) { + case 40: { + if (!angular.isNumber(self.selectedOption)) { + self.selectedOption = 0; + } else { + self.selectedOption = self.selectedOption === elems.length - 1 ? + self.selectedOption : + self.selectedOption + 1; + } + break; + } + case 38: { + if (!angular.isNumber(self.selectedOption)) { + self.selectedOption = elems.length - 1; + } else { + self.selectedOption = self.selectedOption === 0 ? + 0 : self.selectedOption - 1; + } + break; + } + } + elems[self.selectedOption].focus(); + }; + + scope.getDropdownElement = function() { + return self.dropdownMenu; + }; + + scope.focusToggleElement = function() { + if (self.toggleElement) { + self.toggleElement[0].focus(); + } + }; + + scope.$watch('isOpen', function(isOpen, wasOpen) { + if (appendTo && self.dropdownMenu) { + var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true), + css, + rightalign, + scrollbarPadding, + scrollbarWidth = 0; + + css = { + top: pos.top + 'px', + display: isOpen ? 'block' : 'none' + }; + + rightalign = self.dropdownMenu.hasClass('dropdown-menu-right'); + if (!rightalign) { + css.left = pos.left + 'px'; + css.right = 'auto'; + } else { + css.left = 'auto'; + scrollbarPadding = $position.scrollbarPadding(appendTo); + + if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) { + scrollbarWidth = scrollbarPadding.scrollbarWidth; + } + + css.right = window.innerWidth - scrollbarWidth - + (pos.left + $element.prop('offsetWidth')) + 'px'; + } + + // Need to adjust our positioning to be relative to the appendTo container + // if it's not the body element + if (!appendToBody) { + var appendOffset = $position.offset(appendTo); + + css.top = pos.top - appendOffset.top + 'px'; + + if (!rightalign) { + css.left = pos.left - appendOffset.left + 'px'; + } else { + css.right = window.innerWidth - + (pos.left - appendOffset.left + $element.prop('offsetWidth')) + 'px'; + } + } + + self.dropdownMenu.css(css); + } + + var openContainer = appendTo ? appendTo : $element; + var dropdownOpenClass = appendTo ? appendToOpenClass : openClass; + var hasOpenClass = openContainer.hasClass(dropdownOpenClass); + var isOnlyOpen = uibDropdownService.isOnlyOpen($scope, appendTo); + + if (hasOpenClass === !isOpen) { + var toggleClass; + if (appendTo) { + toggleClass = !isOnlyOpen ? 'addClass' : 'removeClass'; + } else { + toggleClass = isOpen ? 'addClass' : 'removeClass'; + } + $animate[toggleClass](openContainer, dropdownOpenClass).then(function() { + if (angular.isDefined(isOpen) && isOpen !== wasOpen) { + toggleInvoker($scope, { open: !!isOpen }); + } + }); + } + + if (isOpen) { + if (self.dropdownMenuTemplateUrl) { + $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) { + templateScope = scope.$new(); + $compile(tplContent.trim())(templateScope, function(dropdownElement) { + var newEl = dropdownElement; + self.dropdownMenu.replaceWith(newEl); + self.dropdownMenu = newEl; + $document.on('keydown', uibDropdownService.keybindFilter); + }); + }); + } else { + $document.on('keydown', uibDropdownService.keybindFilter); + } + + scope.focusToggleElement(); + uibDropdownService.open(scope, $element, appendTo); + } else { + uibDropdownService.close(scope, $element, appendTo); + if (self.dropdownMenuTemplateUrl) { + if (templateScope) { + templateScope.$destroy(); + } + var newEl = angular.element(''); + self.dropdownMenu.replaceWith(newEl); + self.dropdownMenu = newEl; + } + + self.selectedOption = null; + } + + if (angular.isFunction(setIsOpen)) { + setIsOpen($scope, isOpen); + } + }); +}]) + +.directive('uibDropdown', function() { + return { + controller: 'UibDropdownController', + link: function(scope, element, attrs, dropdownCtrl) { + dropdownCtrl.init(); + } + }; +}) + +.directive('uibDropdownMenu', function() { + return { + restrict: 'A', + require: '?^uibDropdown', + link: function(scope, element, attrs, dropdownCtrl) { + if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) { + return; + } + + element.addClass('dropdown-menu'); + + var tplUrl = attrs.templateUrl; + if (tplUrl) { + dropdownCtrl.dropdownMenuTemplateUrl = tplUrl; + } + + if (!dropdownCtrl.dropdownMenu) { + dropdownCtrl.dropdownMenu = element; + } + } + }; +}) + +.directive('uibDropdownToggle', function() { + return { + require: '?^uibDropdown', + link: function(scope, element, attrs, dropdownCtrl) { + if (!dropdownCtrl) { + return; + } + + element.addClass('dropdown-toggle'); + + dropdownCtrl.toggleElement = element; + + var toggleDropdown = function(event) { + event.preventDefault(); + + if (!element.hasClass('disabled') && !attrs.disabled) { + scope.$apply(function() { + dropdownCtrl.toggle(); + }); + } + }; + + element.on('click', toggleDropdown); + + // WAI-ARIA + element.attr({ 'aria-haspopup': true, 'aria-expanded': false }); + scope.$watch(dropdownCtrl.isOpen, function(isOpen) { + element.attr('aria-expanded', !!isOpen); + }); + + scope.$on('$destroy', function() { + element.off('click', toggleDropdown); + }); + } + }; +}); + +angular.module('ui.bootstrap.stackedMap', []) +/** + * A helper, internal data structure that acts as a map but also allows getting / removing + * elements in the LIFO order + */ + .factory('$$stackedMap', function() { + return { + createNew: function() { + var stack = []; + + return { + add: function(key, value) { + stack.push({ + key: key, + value: value + }); + }, + get: function(key) { + for (var i = 0; i < stack.length; i++) { + if (key === stack[i].key) { + return stack[i]; + } + } + }, + keys: function() { + var keys = []; + for (var i = 0; i < stack.length; i++) { + keys.push(stack[i].key); + } + return keys; + }, + top: function() { + return stack[stack.length - 1]; + }, + remove: function(key) { + var idx = -1; + for (var i = 0; i < stack.length; i++) { + if (key === stack[i].key) { + idx = i; + break; + } + } + return stack.splice(idx, 1)[0]; + }, + removeTop: function() { + return stack.pop(); + }, + length: function() { + return stack.length; + } + }; + } + }; + }); +angular.module('ui.bootstrap.modal', ['ui.bootstrap.multiMap', 'ui.bootstrap.stackedMap', 'ui.bootstrap.position']) +/** + * Pluggable resolve mechanism for the modal resolve resolution + * Supports UI Router's $resolve service + */ + .provider('$uibResolve', function() { + var resolve = this; + this.resolver = null; + + this.setResolver = function(resolver) { + this.resolver = resolver; + }; + + this.$get = ['$injector', '$q', function($injector, $q) { + var resolver = resolve.resolver ? $injector.get(resolve.resolver) : null; + return { + resolve: function(invocables, locals, parent, self) { + if (resolver) { + return resolver.resolve(invocables, locals, parent, self); + } + + var promises = []; + + angular.forEach(invocables, function(value) { + if (angular.isFunction(value) || angular.isArray(value)) { + promises.push($q.resolve($injector.invoke(value))); + } else if (angular.isString(value)) { + promises.push($q.resolve($injector.get(value))); + } else { + promises.push($q.resolve(value)); + } + }); + + return $q.all(promises).then(function(resolves) { + var resolveObj = {}; + var resolveIter = 0; + angular.forEach(invocables, function(value, key) { + resolveObj[key] = resolves[resolveIter++]; + }); + + return resolveObj; + }); + } + }; + }]; + }) + +/** + * A helper directive for the $modal service. It creates a backdrop element. + */ + .directive('uibModalBackdrop', ['$animate', '$injector', '$uibModalStack', + function($animate, $injector, $modalStack) { + return { + restrict: 'A', + compile: function(tElement, tAttrs) { + tElement.addClass(tAttrs.backdropClass); + return linkFn; + } + }; + + function linkFn(scope, element, attrs) { + if (attrs.modalInClass) { + $animate.addClass(element, attrs.modalInClass); + + scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { + var done = setIsAsync(); + if (scope.modalOptions.animation) { + $animate.removeClass(element, attrs.modalInClass).then(done); + } else { + done(); + } + }); + } + } + }]) + + .directive('uibModalWindow', ['$uibModalStack', '$q', '$animateCss', '$document', + function($modalStack, $q, $animateCss, $document) { + return { + scope: { + index: '@' + }, + restrict: 'A', + transclude: true, + templateUrl: function(tElement, tAttrs) { + return tAttrs.templateUrl || 'uib/template/modal/window.html'; + }, + link: function(scope, element, attrs) { + element.addClass(attrs.windowTopClass || ''); + scope.size = attrs.size; + + scope.close = function(evt) { + var modal = $modalStack.getTop(); + if (modal && modal.value.backdrop && + modal.value.backdrop !== 'static' && + evt.target === evt.currentTarget) { + evt.preventDefault(); + evt.stopPropagation(); + $modalStack.dismiss(modal.key, 'backdrop click'); + } + }; + + // moved from template to fix issue #2280 + element.on('click', scope.close); + + // This property is only added to the scope for the purpose of detecting when this directive is rendered. + // We can detect that by using this property in the template associated with this directive and then use + // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}. + scope.$isRendered = true; + + // Deferred object that will be resolved when this modal is rendered. + var modalRenderDeferObj = $q.defer(); + // Resolve render promise post-digest + scope.$$postDigest(function() { + modalRenderDeferObj.resolve(); + }); + + modalRenderDeferObj.promise.then(function() { + var animationPromise = null; + + if (attrs.modalInClass) { + animationPromise = $animateCss(element, { + addClass: attrs.modalInClass + }).start(); + + scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { + var done = setIsAsync(); + $animateCss(element, { + removeClass: attrs.modalInClass + }).start().then(done); + }); + } + + + $q.when(animationPromise).then(function() { + // Notify {@link $modalStack} that modal is rendered. + var modal = $modalStack.getTop(); + if (modal) { + $modalStack.modalRendered(modal.key); + } + + /** + * If something within the freshly-opened modal already has focus (perhaps via a + * directive that causes focus) then there's no need to try to focus anything. + */ + if (!($document[0].activeElement && element[0].contains($document[0].activeElement))) { + var inputWithAutofocus = element[0].querySelector('[autofocus]'); + /** + * Auto-focusing of a freshly-opened modal element causes any child elements + * with the autofocus attribute to lose focus. This is an issue on touch + * based devices which will show and then hide the onscreen keyboard. + * Attempts to refocus the autofocus element via JavaScript will not reopen + * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus + * the modal element if the modal does not contain an autofocus element. + */ + if (inputWithAutofocus) { + inputWithAutofocus.focus(); + } else { + element[0].focus(); + } + } + }); + }); + } + }; + }]) + + .directive('uibModalAnimationClass', function() { + return { + compile: function(tElement, tAttrs) { + if (tAttrs.modalAnimation) { + tElement.addClass(tAttrs.uibModalAnimationClass); + } + } + }; + }) + + .directive('uibModalTransclude', ['$animate', function($animate) { + return { + link: function(scope, element, attrs, controller, transclude) { + transclude(scope.$parent, function(clone) { + element.empty(); + $animate.enter(clone, element); + }); + } + }; + }]) + + .factory('$uibModalStack', ['$animate', '$animateCss', '$document', + '$compile', '$rootScope', '$q', '$$multiMap', '$$stackedMap', '$uibPosition', + function($animate, $animateCss, $document, $compile, $rootScope, $q, $$multiMap, $$stackedMap, $uibPosition) { + var OPENED_MODAL_CLASS = 'modal-open'; + + var backdropDomEl, backdropScope; + var openedWindows = $$stackedMap.createNew(); + var openedClasses = $$multiMap.createNew(); + var $modalStack = { + NOW_CLOSING_EVENT: 'modal.stack.now-closing' + }; + var topModalIndex = 0; + var previousTopOpenedModal = null; + var ARIA_HIDDEN_ATTRIBUTE_NAME = 'data-bootstrap-modal-aria-hidden-count'; + + //Modal focus behavior + var tabbableSelector = 'a[href], area[href], input:not([disabled]):not([tabindex=\'-1\']), ' + + 'button:not([disabled]):not([tabindex=\'-1\']),select:not([disabled]):not([tabindex=\'-1\']), textarea:not([disabled]):not([tabindex=\'-1\']), ' + + 'iframe, object, embed, *[tabindex]:not([tabindex=\'-1\']), *[contenteditable=true]'; + var scrollbarPadding; + var SNAKE_CASE_REGEXP = /[A-Z]/g; + + // TODO: extract into common dependency with tooltip + function snake_case(name) { + var separator = '-'; + return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { + return (pos ? separator : '') + letter.toLowerCase(); + }); + } + + function isVisible(element) { + return !!(element.offsetWidth || + element.offsetHeight || + element.getClientRects().length); + } + + function backdropIndex() { + var topBackdropIndex = -1; + var opened = openedWindows.keys(); + for (var i = 0; i < opened.length; i++) { + if (openedWindows.get(opened[i]).value.backdrop) { + topBackdropIndex = i; + } + } + + // If any backdrop exist, ensure that it's index is always + // right below the top modal + if (topBackdropIndex > -1 && topBackdropIndex < topModalIndex) { + topBackdropIndex = topModalIndex; + } + return topBackdropIndex; + } + + $rootScope.$watch(backdropIndex, function(newBackdropIndex) { + if (backdropScope) { + backdropScope.index = newBackdropIndex; + } + }); + + function removeModalWindow(modalInstance, elementToReceiveFocus) { + var modalWindow = openedWindows.get(modalInstance).value; + var appendToElement = modalWindow.appendTo; + + //clean up the stack + openedWindows.remove(modalInstance); + previousTopOpenedModal = openedWindows.top(); + if (previousTopOpenedModal) { + topModalIndex = parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10); + } + + removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() { + var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS; + openedClasses.remove(modalBodyClass, modalInstance); + var areAnyOpen = openedClasses.hasKey(modalBodyClass); + appendToElement.toggleClass(modalBodyClass, areAnyOpen); + if (!areAnyOpen && scrollbarPadding && scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) { + if (scrollbarPadding.originalRight) { + appendToElement.css({paddingRight: scrollbarPadding.originalRight + 'px'}); + } else { + appendToElement.css({paddingRight: ''}); + } + scrollbarPadding = null; + } + toggleTopWindowClass(true); + }, modalWindow.closedDeferred); + checkRemoveBackdrop(); + + //move focus to specified element if available, or else to body + if (elementToReceiveFocus && elementToReceiveFocus.focus) { + elementToReceiveFocus.focus(); + } else if (appendToElement.focus) { + appendToElement.focus(); + } + } + + // Add or remove "windowTopClass" from the top window in the stack + function toggleTopWindowClass(toggleSwitch) { + var modalWindow; + + if (openedWindows.length() > 0) { + modalWindow = openedWindows.top().value; + modalWindow.modalDomEl.toggleClass(modalWindow.windowTopClass || '', toggleSwitch); + } + } + + function checkRemoveBackdrop() { + //remove backdrop if no longer needed + if (backdropDomEl && backdropIndex() === -1) { + var backdropScopeRef = backdropScope; + removeAfterAnimate(backdropDomEl, backdropScope, function() { + backdropScopeRef = null; + }); + backdropDomEl = undefined; + backdropScope = undefined; + } + } + + function removeAfterAnimate(domEl, scope, done, closedDeferred) { + var asyncDeferred; + var asyncPromise = null; + var setIsAsync = function() { + if (!asyncDeferred) { + asyncDeferred = $q.defer(); + asyncPromise = asyncDeferred.promise; + } + + return function asyncDone() { + asyncDeferred.resolve(); + }; + }; + scope.$broadcast($modalStack.NOW_CLOSING_EVENT, setIsAsync); + + // Note that it's intentional that asyncPromise might be null. + // That's when setIsAsync has not been called during the + // NOW_CLOSING_EVENT broadcast. + return $q.when(asyncPromise).then(afterAnimating); + + function afterAnimating() { + if (afterAnimating.done) { + return; + } + afterAnimating.done = true; + + $animate.leave(domEl).then(function() { + if (done) { + done(); + } + + domEl.remove(); + if (closedDeferred) { + closedDeferred.resolve(); + } + }); + + scope.$destroy(); + } + } + + $document.on('keydown', keydownListener); + + $rootScope.$on('$destroy', function() { + $document.off('keydown', keydownListener); + }); + + function keydownListener(evt) { + if (evt.isDefaultPrevented()) { + return evt; + } + + var modal = openedWindows.top(); + if (modal) { + switch (evt.which) { + case 27: { + if (modal.value.keyboard) { + evt.preventDefault(); + $rootScope.$apply(function() { + $modalStack.dismiss(modal.key, 'escape key press'); + }); + } + break; + } + case 9: { + var list = $modalStack.loadFocusElementList(modal); + var focusChanged = false; + if (evt.shiftKey) { + if ($modalStack.isFocusInFirstItem(evt, list) || $modalStack.isModalFocused(evt, modal)) { + focusChanged = $modalStack.focusLastFocusableElement(list); + } + } else { + if ($modalStack.isFocusInLastItem(evt, list)) { + focusChanged = $modalStack.focusFirstFocusableElement(list); + } + } + + if (focusChanged) { + evt.preventDefault(); + evt.stopPropagation(); + } + + break; + } + } + } + } + + $modalStack.open = function(modalInstance, modal) { + var modalOpener = $document[0].activeElement, + modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS; + + toggleTopWindowClass(false); + + // Store the current top first, to determine what index we ought to use + // for the current top modal + previousTopOpenedModal = openedWindows.top(); + + openedWindows.add(modalInstance, { + deferred: modal.deferred, + renderDeferred: modal.renderDeferred, + closedDeferred: modal.closedDeferred, + modalScope: modal.scope, + backdrop: modal.backdrop, + keyboard: modal.keyboard, + openedClass: modal.openedClass, + windowTopClass: modal.windowTopClass, + animation: modal.animation, + appendTo: modal.appendTo + }); + + openedClasses.put(modalBodyClass, modalInstance); + + var appendToElement = modal.appendTo, + currBackdropIndex = backdropIndex(); + + if (!appendToElement.length) { + throw new Error('appendTo element not found. Make sure that the element passed is in DOM.'); + } + + if (currBackdropIndex >= 0 && !backdropDomEl) { + backdropScope = $rootScope.$new(true); + backdropScope.modalOptions = modal; + backdropScope.index = currBackdropIndex; + backdropDomEl = angular.element('
      '); + backdropDomEl.attr({ + 'class': 'modal-backdrop', + 'ng-style': '{\'z-index\': 1040 + (index && 1 || 0) + index*10}', + 'uib-modal-animation-class': 'fade', + 'modal-in-class': 'in' + }); + if (modal.backdropClass) { + backdropDomEl.addClass(modal.backdropClass); + } + + if (modal.animation) { + backdropDomEl.attr('modal-animation', 'true'); + } + $compile(backdropDomEl)(backdropScope); + $animate.enter(backdropDomEl, appendToElement); + if ($uibPosition.isScrollable(appendToElement)) { + scrollbarPadding = $uibPosition.scrollbarPadding(appendToElement); + if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) { + appendToElement.css({paddingRight: scrollbarPadding.right + 'px'}); + } + } + } + + var content; + if (modal.component) { + content = document.createElement(snake_case(modal.component.name)); + content = angular.element(content); + content.attr({ + resolve: '$resolve', + 'modal-instance': '$uibModalInstance', + close: '$close($value)', + dismiss: '$dismiss($value)' + }); + } else { + content = modal.content; + } + + // Set the top modal index based on the index of the previous top modal + topModalIndex = previousTopOpenedModal ? parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10) + 1 : 0; + var angularDomEl = angular.element('
      '); + angularDomEl.attr({ + 'class': 'modal', + 'template-url': modal.windowTemplateUrl, + 'window-top-class': modal.windowTopClass, + 'role': 'dialog', + 'aria-labelledby': modal.ariaLabelledBy, + 'aria-describedby': modal.ariaDescribedBy, + 'size': modal.size, + 'index': topModalIndex, + 'animate': 'animate', + 'ng-style': '{\'z-index\': 1050 + $$topModalIndex*10, display: \'block\'}', + 'tabindex': -1, + 'uib-modal-animation-class': 'fade', + 'modal-in-class': 'in' + }).append(content); + if (modal.windowClass) { + angularDomEl.addClass(modal.windowClass); + } + + if (modal.animation) { + angularDomEl.attr('modal-animation', 'true'); + } + + appendToElement.addClass(modalBodyClass); + if (modal.scope) { + // we need to explicitly add the modal index to the modal scope + // because it is needed by ngStyle to compute the zIndex property. + modal.scope.$$topModalIndex = topModalIndex; + } + $animate.enter($compile(angularDomEl)(modal.scope), appendToElement); + + openedWindows.top().value.modalDomEl = angularDomEl; + openedWindows.top().value.modalOpener = modalOpener; + + applyAriaHidden(angularDomEl); + + function applyAriaHidden(el) { + if (!el || el[0].tagName === 'BODY') { + return; + } + + getSiblings(el).forEach(function(sibling) { + var elemIsAlreadyHidden = sibling.getAttribute('aria-hidden') === 'true', + ariaHiddenCount = parseInt(sibling.getAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME), 10); + + if (!ariaHiddenCount) { + ariaHiddenCount = elemIsAlreadyHidden ? 1 : 0; + } + + sibling.setAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME, ariaHiddenCount + 1); + sibling.setAttribute('aria-hidden', 'true'); + }); + + return applyAriaHidden(el.parent()); + + function getSiblings(el) { + var children = el.parent() ? el.parent().children() : []; + + return Array.prototype.filter.call(children, function(child) { + return child !== el[0]; + }); + } + } + }; + + function broadcastClosing(modalWindow, resultOrReason, closing) { + return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented; + } + + function unhideBackgroundElements() { + Array.prototype.forEach.call( + document.querySelectorAll('[' + ARIA_HIDDEN_ATTRIBUTE_NAME + ']'), + function(hiddenEl) { + var ariaHiddenCount = parseInt(hiddenEl.getAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME), 10), + newHiddenCount = ariaHiddenCount - 1; + hiddenEl.setAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME, newHiddenCount); + + if (!newHiddenCount) { + hiddenEl.removeAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME); + hiddenEl.removeAttribute('aria-hidden'); + } + } + ); + } + + $modalStack.close = function(modalInstance, result) { + var modalWindow = openedWindows.get(modalInstance); + unhideBackgroundElements(); + if (modalWindow && broadcastClosing(modalWindow, result, true)) { + modalWindow.value.modalScope.$$uibDestructionScheduled = true; + modalWindow.value.deferred.resolve(result); + removeModalWindow(modalInstance, modalWindow.value.modalOpener); + return true; + } + + return !modalWindow; + }; + + $modalStack.dismiss = function(modalInstance, reason) { + var modalWindow = openedWindows.get(modalInstance); + unhideBackgroundElements(); + if (modalWindow && broadcastClosing(modalWindow, reason, false)) { + modalWindow.value.modalScope.$$uibDestructionScheduled = true; + modalWindow.value.deferred.reject(reason); + removeModalWindow(modalInstance, modalWindow.value.modalOpener); + return true; + } + return !modalWindow; + }; + + $modalStack.dismissAll = function(reason) { + var topModal = this.getTop(); + while (topModal && this.dismiss(topModal.key, reason)) { + topModal = this.getTop(); + } + }; + + $modalStack.getTop = function() { + return openedWindows.top(); + }; + + $modalStack.modalRendered = function(modalInstance) { + var modalWindow = openedWindows.get(modalInstance); + if (modalWindow) { + modalWindow.value.renderDeferred.resolve(); + } + }; + + $modalStack.focusFirstFocusableElement = function(list) { + if (list.length > 0) { + list[0].focus(); + return true; + } + return false; + }; + + $modalStack.focusLastFocusableElement = function(list) { + if (list.length > 0) { + list[list.length - 1].focus(); + return true; + } + return false; + }; + + $modalStack.isModalFocused = function(evt, modalWindow) { + if (evt && modalWindow) { + var modalDomEl = modalWindow.value.modalDomEl; + if (modalDomEl && modalDomEl.length) { + return (evt.target || evt.srcElement) === modalDomEl[0]; + } + } + return false; + }; + + $modalStack.isFocusInFirstItem = function(evt, list) { + if (list.length > 0) { + return (evt.target || evt.srcElement) === list[0]; + } + return false; + }; + + $modalStack.isFocusInLastItem = function(evt, list) { + if (list.length > 0) { + return (evt.target || evt.srcElement) === list[list.length - 1]; + } + return false; + }; + + $modalStack.loadFocusElementList = function(modalWindow) { + if (modalWindow) { + var modalDomE1 = modalWindow.value.modalDomEl; + if (modalDomE1 && modalDomE1.length) { + var elements = modalDomE1[0].querySelectorAll(tabbableSelector); + return elements ? + Array.prototype.filter.call(elements, function(element) { + return isVisible(element); + }) : elements; + } + } + }; + + return $modalStack; + }]) + + .provider('$uibModal', function() { + var $modalProvider = { + options: { + animation: true, + backdrop: true, //can also be false or 'static' + keyboard: true + }, + $get: ['$rootScope', '$q', '$document', '$templateRequest', '$controller', '$uibResolve', '$uibModalStack', + function ($rootScope, $q, $document, $templateRequest, $controller, $uibResolve, $modalStack) { + var $modal = {}; + + function getTemplatePromise(options) { + return options.template ? $q.when(options.template) : + $templateRequest(angular.isFunction(options.templateUrl) ? + options.templateUrl() : options.templateUrl); + } + + var promiseChain = null; + $modal.getPromiseChain = function() { + return promiseChain; + }; + + $modal.open = function(modalOptions) { + var modalResultDeferred = $q.defer(); + var modalOpenedDeferred = $q.defer(); + var modalClosedDeferred = $q.defer(); + var modalRenderDeferred = $q.defer(); + + //prepare an instance of a modal to be injected into controllers and returned to a caller + var modalInstance = { + result: modalResultDeferred.promise, + opened: modalOpenedDeferred.promise, + closed: modalClosedDeferred.promise, + rendered: modalRenderDeferred.promise, + close: function (result) { + return $modalStack.close(modalInstance, result); + }, + dismiss: function (reason) { + return $modalStack.dismiss(modalInstance, reason); + } + }; + + //merge and clean up options + modalOptions = angular.extend({}, $modalProvider.options, modalOptions); + modalOptions.resolve = modalOptions.resolve || {}; + modalOptions.appendTo = modalOptions.appendTo || $document.find('body').eq(0); + + //verify options + if (!modalOptions.component && !modalOptions.template && !modalOptions.templateUrl) { + throw new Error('One of component or template or templateUrl options is required.'); + } + + var templateAndResolvePromise; + if (modalOptions.component) { + templateAndResolvePromise = $q.when($uibResolve.resolve(modalOptions.resolve, {}, null, null)); + } else { + templateAndResolvePromise = + $q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]); + } + + function resolveWithTemplate() { + return templateAndResolvePromise; + } + + // Wait for the resolution of the existing promise chain. + // Then switch to our own combined promise dependency (regardless of how the previous modal fared). + // Then add to $modalStack and resolve opened. + // Finally clean up the chain variable if no subsequent modal has overwritten it. + var samePromise; + samePromise = promiseChain = $q.all([promiseChain]) + .then(resolveWithTemplate, resolveWithTemplate) + .then(function resolveSuccess(tplAndVars) { + var providedScope = modalOptions.scope || $rootScope; + + var modalScope = providedScope.$new(); + modalScope.$close = modalInstance.close; + modalScope.$dismiss = modalInstance.dismiss; + + modalScope.$on('$destroy', function() { + if (!modalScope.$$uibDestructionScheduled) { + modalScope.$dismiss('$uibUnscheduledDestruction'); + } + }); + + var modal = { + scope: modalScope, + deferred: modalResultDeferred, + renderDeferred: modalRenderDeferred, + closedDeferred: modalClosedDeferred, + animation: modalOptions.animation, + backdrop: modalOptions.backdrop, + keyboard: modalOptions.keyboard, + backdropClass: modalOptions.backdropClass, + windowTopClass: modalOptions.windowTopClass, + windowClass: modalOptions.windowClass, + windowTemplateUrl: modalOptions.windowTemplateUrl, + ariaLabelledBy: modalOptions.ariaLabelledBy, + ariaDescribedBy: modalOptions.ariaDescribedBy, + size: modalOptions.size, + openedClass: modalOptions.openedClass, + appendTo: modalOptions.appendTo + }; + + var component = {}; + var ctrlInstance, ctrlInstantiate, ctrlLocals = {}; + + if (modalOptions.component) { + constructLocals(component, false, true, false); + component.name = modalOptions.component; + modal.component = component; + } else if (modalOptions.controller) { + constructLocals(ctrlLocals, true, false, true); + + // the third param will make the controller instantiate later,private api + // @see https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L126 + ctrlInstantiate = $controller(modalOptions.controller, ctrlLocals, true, modalOptions.controllerAs); + if (modalOptions.controllerAs && modalOptions.bindToController) { + ctrlInstance = ctrlInstantiate.instance; + ctrlInstance.$close = modalScope.$close; + ctrlInstance.$dismiss = modalScope.$dismiss; + angular.extend(ctrlInstance, { + $resolve: ctrlLocals.$scope.$resolve + }, providedScope); + } + + ctrlInstance = ctrlInstantiate(); + + if (angular.isFunction(ctrlInstance.$onInit)) { + ctrlInstance.$onInit(); + } + } + + if (!modalOptions.component) { + modal.content = tplAndVars[0]; + } + + $modalStack.open(modalInstance, modal); + modalOpenedDeferred.resolve(true); + + function constructLocals(obj, template, instanceOnScope, injectable) { + obj.$scope = modalScope; + obj.$scope.$resolve = {}; + if (instanceOnScope) { + obj.$scope.$uibModalInstance = modalInstance; + } else { + obj.$uibModalInstance = modalInstance; + } + + var resolves = template ? tplAndVars[1] : tplAndVars; + angular.forEach(resolves, function(value, key) { + if (injectable) { + obj[key] = value; + } + + obj.$scope.$resolve[key] = value; + }); + } + }, function resolveError(reason) { + modalOpenedDeferred.reject(reason); + modalResultDeferred.reject(reason); + })['finally'](function() { + if (promiseChain === samePromise) { + promiseChain = null; + } + }); + + return modalInstance; + }; + + return $modal; + } + ] + }; + + return $modalProvider; + }); + +angular.module('ui.bootstrap.paging', []) +/** + * Helper internal service for generating common controller code between the + * pager and pagination components + */ +.factory('uibPaging', ['$parse', function($parse) { + return { + create: function(ctrl, $scope, $attrs) { + ctrl.setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop; + ctrl.ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl + ctrl._watchers = []; + + ctrl.init = function(ngModelCtrl, config) { + ctrl.ngModelCtrl = ngModelCtrl; + ctrl.config = config; + + ngModelCtrl.$render = function() { + ctrl.render(); + }; + + if ($attrs.itemsPerPage) { + ctrl._watchers.push($scope.$parent.$watch($attrs.itemsPerPage, function(value) { + ctrl.itemsPerPage = parseInt(value, 10); + $scope.totalPages = ctrl.calculateTotalPages(); + ctrl.updatePage(); + })); + } else { + ctrl.itemsPerPage = config.itemsPerPage; + } + + $scope.$watch('totalItems', function(newTotal, oldTotal) { + if (angular.isDefined(newTotal) || newTotal !== oldTotal) { + $scope.totalPages = ctrl.calculateTotalPages(); + ctrl.updatePage(); + } + }); + }; + + ctrl.calculateTotalPages = function() { + var totalPages = ctrl.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / ctrl.itemsPerPage); + return Math.max(totalPages || 0, 1); + }; + + ctrl.render = function() { + $scope.page = parseInt(ctrl.ngModelCtrl.$viewValue, 10) || 1; + }; + + $scope.selectPage = function(page, evt) { + if (evt) { + evt.preventDefault(); + } + + var clickAllowed = !$scope.ngDisabled || !evt; + if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) { + if (evt && evt.target) { + evt.target.blur(); + } + ctrl.ngModelCtrl.$setViewValue(page); + ctrl.ngModelCtrl.$render(); + } + }; + + $scope.getText = function(key) { + return $scope[key + 'Text'] || ctrl.config[key + 'Text']; + }; + + $scope.noPrevious = function() { + return $scope.page === 1; + }; + + $scope.noNext = function() { + return $scope.page === $scope.totalPages; + }; + + ctrl.updatePage = function() { + ctrl.setNumPages($scope.$parent, $scope.totalPages); // Readonly variable + + if ($scope.page > $scope.totalPages) { + $scope.selectPage($scope.totalPages); + } else { + ctrl.ngModelCtrl.$render(); + } + }; + + $scope.$on('$destroy', function() { + while (ctrl._watchers.length) { + ctrl._watchers.shift()(); + } + }); + } + }; +}]); + +angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging', 'ui.bootstrap.tabindex']) + +.controller('UibPagerController', ['$scope', '$attrs', 'uibPaging', 'uibPagerConfig', function($scope, $attrs, uibPaging, uibPagerConfig) { + $scope.align = angular.isDefined($attrs.align) ? $scope.$parent.$eval($attrs.align) : uibPagerConfig.align; + + uibPaging.create(this, $scope, $attrs); +}]) + +.constant('uibPagerConfig', { + itemsPerPage: 10, + previousText: '« Previous', + nextText: 'Next »', + align: true +}) + +.directive('uibPager', ['uibPagerConfig', function(uibPagerConfig) { + return { + scope: { + totalItems: '=', + previousText: '@', + nextText: '@', + ngDisabled: '=' + }, + require: ['uibPager', '?ngModel'], + restrict: 'A', + controller: 'UibPagerController', + controllerAs: 'pager', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/pager/pager.html'; + }, + link: function(scope, element, attrs, ctrls) { + element.addClass('pager'); + var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (!ngModelCtrl) { + return; // do nothing if no ng-model + } + + paginationCtrl.init(ngModelCtrl, uibPagerConfig); + } + }; +}]); + +angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging', 'ui.bootstrap.tabindex']) +.controller('UibPaginationController', ['$scope', '$attrs', '$parse', 'uibPaging', 'uibPaginationConfig', function($scope, $attrs, $parse, uibPaging, uibPaginationConfig) { + var ctrl = this; + // Setup configuration parameters + var maxSize = angular.isDefined($attrs.maxSize) ? $scope.$parent.$eval($attrs.maxSize) : uibPaginationConfig.maxSize, + rotate = angular.isDefined($attrs.rotate) ? $scope.$parent.$eval($attrs.rotate) : uibPaginationConfig.rotate, + forceEllipses = angular.isDefined($attrs.forceEllipses) ? $scope.$parent.$eval($attrs.forceEllipses) : uibPaginationConfig.forceEllipses, + boundaryLinkNumbers = angular.isDefined($attrs.boundaryLinkNumbers) ? $scope.$parent.$eval($attrs.boundaryLinkNumbers) : uibPaginationConfig.boundaryLinkNumbers, + pageLabel = angular.isDefined($attrs.pageLabel) ? function(idx) { return $scope.$parent.$eval($attrs.pageLabel, {$page: idx}); } : angular.identity; + $scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : uibPaginationConfig.boundaryLinks; + $scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : uibPaginationConfig.directionLinks; + + uibPaging.create(this, $scope, $attrs); + + if ($attrs.maxSize) { + ctrl._watchers.push($scope.$parent.$watch($parse($attrs.maxSize), function(value) { + maxSize = parseInt(value, 10); + ctrl.render(); + })); + } + + // Create page object used in template + function makePage(number, text, isActive) { + return { + number: number, + text: text, + active: isActive + }; + } + + function getPages(currentPage, totalPages) { + var pages = []; + + // Default page limits + var startPage = 1, endPage = totalPages; + var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages; + + // recompute if maxSize + if (isMaxSized) { + if (rotate) { + // Current page is displayed in the middle of the visible ones + startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1); + endPage = startPage + maxSize - 1; + + // Adjust if limit is exceeded + if (endPage > totalPages) { + endPage = totalPages; + startPage = endPage - maxSize + 1; + } + } else { + // Visible pages are paginated with maxSize + startPage = (Math.ceil(currentPage / maxSize) - 1) * maxSize + 1; + + // Adjust last page if limit is exceeded + endPage = Math.min(startPage + maxSize - 1, totalPages); + } + } + + // Add page number links + for (var number = startPage; number <= endPage; number++) { + var page = makePage(number, pageLabel(number), number === currentPage); + pages.push(page); + } + + // Add links to move between page sets + if (isMaxSized && maxSize > 0 && (!rotate || forceEllipses || boundaryLinkNumbers)) { + if (startPage > 1) { + if (!boundaryLinkNumbers || startPage > 3) { //need ellipsis for all options unless range is too close to beginning + var previousPageSet = makePage(startPage - 1, '...', false); + pages.unshift(previousPageSet); + } + if (boundaryLinkNumbers) { + if (startPage === 3) { //need to replace ellipsis when the buttons would be sequential + var secondPageLink = makePage(2, '2', false); + pages.unshift(secondPageLink); + } + //add the first page + var firstPageLink = makePage(1, '1', false); + pages.unshift(firstPageLink); + } + } + + if (endPage < totalPages) { + if (!boundaryLinkNumbers || endPage < totalPages - 2) { //need ellipsis for all options unless range is too close to end + var nextPageSet = makePage(endPage + 1, '...', false); + pages.push(nextPageSet); + } + if (boundaryLinkNumbers) { + if (endPage === totalPages - 2) { //need to replace ellipsis when the buttons would be sequential + var secondToLastPageLink = makePage(totalPages - 1, totalPages - 1, false); + pages.push(secondToLastPageLink); + } + //add the last page + var lastPageLink = makePage(totalPages, totalPages, false); + pages.push(lastPageLink); + } + } + } + return pages; + } + + var originalRender = this.render; + this.render = function() { + originalRender(); + if ($scope.page > 0 && $scope.page <= $scope.totalPages) { + $scope.pages = getPages($scope.page, $scope.totalPages); + } + }; +}]) + +.constant('uibPaginationConfig', { + itemsPerPage: 10, + boundaryLinks: false, + boundaryLinkNumbers: false, + directionLinks: true, + firstText: 'First', + previousText: 'Previous', + nextText: 'Next', + lastText: 'Last', + rotate: true, + forceEllipses: false +}) + +.directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, uibPaginationConfig) { + return { + scope: { + totalItems: '=', + firstText: '@', + previousText: '@', + nextText: '@', + lastText: '@', + ngDisabled:'=' + }, + require: ['uibPagination', '?ngModel'], + restrict: 'A', + controller: 'UibPaginationController', + controllerAs: 'pagination', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/pagination/pagination.html'; + }, + link: function(scope, element, attrs, ctrls) { + element.addClass('pagination'); + var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (!ngModelCtrl) { + return; // do nothing if no ng-model + } + + paginationCtrl.init(ngModelCtrl, uibPaginationConfig); + } + }; +}]); + +/** + * 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', 'ui.bootstrap.stackedMap']) + +/** + * The $tooltip service creates tooltip- and popover-like directives as well as + * houses global options for them. + */ +.provider('$uibTooltip', function() { + // The default options tooltip and popover. + var defaultOptions = { + placement: 'top', + placementClassPrefix: '', + animation: true, + popupDelay: 0, + popupCloseDelay: 0, + useContentExp: false + }; + + // Default hide triggers for each show trigger + var triggerMap = { + 'mouseenter': 'mouseleave', + 'click': 'click', + 'outsideClick': 'outsideClick', + 'focus': 'blur', + 'none': '' + }; + + // 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', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) { + var openedTooltips = $$stackedMap.createNew(); + $document.on('keyup', keypressListener); + + $rootScope.$on('$destroy', function() { + $document.off('keyup', keypressListener); + }); + + function keypressListener(e) { + if (e.which === 27) { + var last = openedTooltips.top(); + if (last) { + last.value.close(); + last = null; + } + } + } + + return function $tooltip(ttType, prefix, defaultTriggerShow, options) { + options = angular.extend({}, defaultOptions, globalOptions, options); + + /** + * 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 getTriggers(trigger) { + var show = (trigger || options.trigger || defaultTriggerShow).split(' '); + var hide = show.map(function(trigger) { + return triggerMap[trigger] || trigger; + }); + return { + show: show, + hide: hide + }; + } + + var directiveName = snake_case(ttType); + + var startSym = $interpolate.startSymbol(); + var endSym = $interpolate.endSymbol(); + var template = + '
      ' + + '
      '; + + return { + compile: function(tElem, tAttrs) { + var tooltipLinker = $compile(template); + + return function link(scope, element, attrs, tooltipCtrl) { + var tooltip; + var tooltipLinkedScope; + var transitionTimeout; + var showTimeout; + var hideTimeout; + var positionTimeout; + var adjustmentTimeout; + var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false; + var triggers = getTriggers(undefined); + var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']); + var ttScope = scope.$new(true); + var repositionScheduled = false; + var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false; + var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false; + var observers = []; + var lastPlacement; + + var positionTooltip = function() { + // check if tooltip exists and is not empty + if (!tooltip || !tooltip.html()) { return; } + + if (!positionTimeout) { + positionTimeout = $timeout(function() { + var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody); + var initialHeight = angular.isDefined(tooltip.offsetHeight) ? tooltip.offsetHeight : tooltip.prop('offsetHeight'); + var elementPos = appendToBody ? $position.offset(element) : $position.position(element); + tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px' }); + var placementClasses = ttPosition.placement.split('-'); + + if (!tooltip.hasClass(placementClasses[0])) { + tooltip.removeClass(lastPlacement.split('-')[0]); + tooltip.addClass(placementClasses[0]); + } + + if (!tooltip.hasClass(options.placementClassPrefix + ttPosition.placement)) { + tooltip.removeClass(options.placementClassPrefix + lastPlacement); + tooltip.addClass(options.placementClassPrefix + ttPosition.placement); + } + + adjustmentTimeout = $timeout(function() { + var currentHeight = angular.isDefined(tooltip.offsetHeight) ? tooltip.offsetHeight : tooltip.prop('offsetHeight'); + var adjustment = $position.adjustTop(placementClasses, elementPos, initialHeight, currentHeight); + if (adjustment) { + tooltip.css(adjustment); + } + adjustmentTimeout = null; + }, 0, false); + + // first time through tt element will have the + // uib-position-measure class or if the placement + // has changed we need to position the arrow. + if (tooltip.hasClass('uib-position-measure')) { + $position.positionArrow(tooltip, ttPosition.placement); + tooltip.removeClass('uib-position-measure'); + } else if (lastPlacement !== ttPosition.placement) { + $position.positionArrow(tooltip, ttPosition.placement); + } + lastPlacement = ttPosition.placement; + + positionTimeout = null; + }, 0, false); + } + }; + + // Set up the correct scope to allow transclusion later + ttScope.origScope = scope; + + // By default, the tooltip is not open. + // TODO add ability to start tooltip opened + ttScope.isOpen = false; + + function toggleTooltipBind() { + if (!ttScope.isOpen) { + showTooltipBind(); + } else { + hideTooltipBind(); + } + } + + // Show the tooltip with delay if specified, otherwise show it immediately + function showTooltipBind() { + if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) { + return; + } + + cancelHide(); + prepareTooltip(); + + if (ttScope.popupDelay) { + // Do nothing if the tooltip was already scheduled to pop-up. + // This happens if show is triggered multiple times before any hide is triggered. + if (!showTimeout) { + showTimeout = $timeout(show, ttScope.popupDelay, false); + } + } else { + show(); + } + } + + function hideTooltipBind() { + cancelShow(); + + if (ttScope.popupCloseDelay) { + if (!hideTimeout) { + hideTimeout = $timeout(hide, ttScope.popupCloseDelay, false); + } + } else { + hide(); + } + } + + // Show the tooltip popup element. + function show() { + cancelShow(); + cancelHide(); + + // Don't show empty tooltips. + if (!ttScope.content) { + return angular.noop; + } + + createTooltip(); + + // And show the tooltip. + ttScope.$evalAsync(function() { + ttScope.isOpen = true; + assignIsOpen(true); + positionTooltip(); + }); + } + + function cancelShow() { + if (showTimeout) { + $timeout.cancel(showTimeout); + showTimeout = null; + } + + if (positionTimeout) { + $timeout.cancel(positionTimeout); + positionTimeout = null; + } + } + + // Hide the tooltip popup element. + function hide() { + if (!ttScope) { + return; + } + + // First things first: we don't show it anymore. + ttScope.$evalAsync(function() { + if (ttScope) { + ttScope.isOpen = false; + assignIsOpen(false); + // 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. + // The fade transition in TWBS is 150ms. + if (ttScope.animation) { + if (!transitionTimeout) { + transitionTimeout = $timeout(removeTooltip, 150, false); + } + } else { + removeTooltip(); + } + } + }); + } + + function cancelHide() { + if (hideTimeout) { + $timeout.cancel(hideTimeout); + hideTimeout = null; + } + + if (transitionTimeout) { + $timeout.cancel(transitionTimeout); + transitionTimeout = null; + } + } + + function createTooltip() { + // There can only be one tooltip element per directive shown at once. + if (tooltip) { + return; + } + + tooltipLinkedScope = ttScope.$new(); + tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) { + if (appendToBody) { + $document.find('body').append(tooltip); + } else { + element.after(tooltip); + } + }); + + openedTooltips.add(ttScope, { + close: hide + }); + + prepObservers(); + } + + function removeTooltip() { + cancelShow(); + cancelHide(); + unregisterObservers(); + + if (tooltip) { + tooltip.remove(); + + tooltip = null; + if (adjustmentTimeout) { + $timeout.cancel(adjustmentTimeout); + } + } + + openedTooltips.remove(ttScope); + + if (tooltipLinkedScope) { + tooltipLinkedScope.$destroy(); + tooltipLinkedScope = null; + } + } + + /** + * Set the initial scope values. Once + * the tooltip is created, the observers + * will be added to keep things in sync. + */ + function prepareTooltip() { + ttScope.title = attrs[prefix + 'Title']; + if (contentParse) { + ttScope.content = contentParse(scope); + } else { + ttScope.content = attrs[ttType]; + } + + ttScope.popupClass = attrs[prefix + 'Class']; + ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement; + var placement = $position.parsePlacement(ttScope.placement); + lastPlacement = placement[1] ? placement[0] + '-' + placement[1] : placement[0]; + + var delay = parseInt(attrs[prefix + 'PopupDelay'], 10); + var closeDelay = parseInt(attrs[prefix + 'PopupCloseDelay'], 10); + ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay; + ttScope.popupCloseDelay = !isNaN(closeDelay) ? closeDelay : options.popupCloseDelay; + } + + function assignIsOpen(isOpen) { + if (isOpenParse && angular.isFunction(isOpenParse.assign)) { + isOpenParse.assign(scope, isOpen); + } + } + + ttScope.contentExp = function() { + return ttScope.content; + }; + + /** + * Observe the relevant attributes. + */ + attrs.$observe('disabled', function(val) { + if (val) { + cancelShow(); + } + + if (val && ttScope.isOpen) { + hide(); + } + }); + + if (isOpenParse) { + scope.$watch(isOpenParse, function(val) { + if (ttScope && !val === ttScope.isOpen) { + toggleTooltipBind(); + } + }); + } + + function prepObservers() { + observers.length = 0; + + if (contentParse) { + observers.push( + scope.$watch(contentParse, function(val) { + ttScope.content = val; + if (!val && ttScope.isOpen) { + hide(); + } + }) + ); + + observers.push( + tooltipLinkedScope.$watch(function() { + if (!repositionScheduled) { + repositionScheduled = true; + tooltipLinkedScope.$$postDigest(function() { + repositionScheduled = false; + if (ttScope && ttScope.isOpen) { + positionTooltip(); + } + }); + } + }) + ); + } else { + observers.push( + attrs.$observe(ttType, function(val) { + ttScope.content = val; + if (!val && ttScope.isOpen) { + hide(); + } else { + positionTooltip(); + } + }) + ); + } + + observers.push( + attrs.$observe(prefix + 'Title', function(val) { + ttScope.title = val; + if (ttScope.isOpen) { + positionTooltip(); + } + }) + ); + + observers.push( + attrs.$observe(prefix + 'Placement', function(val) { + ttScope.placement = val ? val : options.placement; + if (ttScope.isOpen) { + positionTooltip(); + } + }) + ); + } + + function unregisterObservers() { + if (observers.length) { + angular.forEach(observers, function(observer) { + observer(); + }); + observers.length = 0; + } + } + + // hide tooltips/popovers for outsideClick trigger + function bodyHideTooltipBind(e) { + if (!ttScope || !ttScope.isOpen || !tooltip) { + return; + } + // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked + if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) { + hideTooltipBind(); + } + } + + var unregisterTriggers = function() { + triggers.show.forEach(function(trigger) { + if (trigger === 'outsideClick') { + element.off('click', toggleTooltipBind); + } else { + element.off(trigger, showTooltipBind); + element.off(trigger, toggleTooltipBind); + } + }); + triggers.hide.forEach(function(trigger) { + if (trigger === 'outsideClick') { + $document.off('click', bodyHideTooltipBind); + } else { + element.off(trigger, hideTooltipBind); + } + }); + }; + + function prepTriggers() { + var showTriggers = [], hideTriggers = []; + var val = scope.$eval(attrs[prefix + 'Trigger']); + unregisterTriggers(); + + if (angular.isObject(val)) { + Object.keys(val).forEach(function(key) { + showTriggers.push(key); + hideTriggers.push(val[key]); + }); + triggers = { + show: showTriggers, + hide: hideTriggers + }; + } else { + triggers = getTriggers(val); + } + + if (triggers.show !== 'none') { + triggers.show.forEach(function(trigger, idx) { + if (trigger === 'outsideClick') { + element.on('click', toggleTooltipBind); + $document.on('click', bodyHideTooltipBind); + } else if (trigger === triggers.hide[idx]) { + element.on(trigger, toggleTooltipBind); + } else if (trigger) { + element.on(trigger, showTooltipBind); + element.on(triggers.hide[idx], hideTooltipBind); + } + + element.on('keypress', function(e) { + if (e.which === 27) { + hideTooltipBind(); + } + }); + }); + } + } + + prepTriggers(); + + var animation = scope.$eval(attrs[prefix + 'Animation']); + ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation; + + var appendToBodyVal; + var appendKey = prefix + 'AppendToBody'; + if (appendKey in attrs && attrs[appendKey] === undefined) { + appendToBodyVal = true; + } else { + appendToBodyVal = scope.$eval(attrs[appendKey]); + } + + appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody; + + // Make sure tooltip is destroyed and removed. + scope.$on('$destroy', function onDestroyTooltip() { + unregisterTriggers(); + removeTooltip(); + ttScope = null; + }); + }; + } + }; + }; + }]; +}) + +// This is mostly ngInclude code but with a custom scope +.directive('uibTooltipTemplateTransclude', [ + '$animate', '$sce', '$compile', '$templateRequest', +function ($animate, $sce, $compile, $templateRequest) { + return { + link: function(scope, elem, attrs) { + var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope); + + var changeCounter = 0, + currentScope, + previousElement, + currentElement; + + var cleanupLastIncludeContent = function() { + if (previousElement) { + previousElement.remove(); + previousElement = null; + } + + if (currentScope) { + currentScope.$destroy(); + currentScope = null; + } + + if (currentElement) { + $animate.leave(currentElement).then(function() { + previousElement = null; + }); + previousElement = currentElement; + currentElement = null; + } + }; + + scope.$watch($sce.parseAsResourceUrl(attrs.uibTooltipTemplateTransclude), function(src) { + var thisChangeId = ++changeCounter; + + if (src) { + //set the 2nd param to true to ignore the template request error so that the inner + //contents and scope can be cleaned up. + $templateRequest(src, true).then(function(response) { + if (thisChangeId !== changeCounter) { return; } + var newScope = origScope.$new(); + var template = response; + + var clone = $compile(template)(newScope, function(clone) { + cleanupLastIncludeContent(); + $animate.enter(clone, elem); + }); + + currentScope = newScope; + currentElement = clone; + + currentScope.$emit('$includeContentLoaded', src); + }, function() { + if (thisChangeId === changeCounter) { + cleanupLastIncludeContent(); + scope.$emit('$includeContentError', src); + } + }); + scope.$emit('$includeContentRequested', src); + } else { + cleanupLastIncludeContent(); + } + }); + + scope.$on('$destroy', cleanupLastIncludeContent); + } + }; +}]) + +/** + * Note that it's intentional that these classes are *not* applied through $animate. + * They must not be animated as they're expected to be present on the tooltip on + * initialization. + */ +.directive('uibTooltipClasses', ['$uibPosition', function($uibPosition) { + return { + restrict: 'A', + link: function(scope, element, attrs) { + // need to set the primary position so the + // arrow has space during position measure. + // tooltip.positionTooltip() + if (scope.placement) { + // // There are no top-left etc... classes + // // in TWBS, so we need the primary position. + var position = $uibPosition.parsePlacement(scope.placement); + element.addClass(position[0]); + } + + if (scope.popupClass) { + element.addClass(scope.popupClass); + } + + if (scope.animation) { + element.addClass(attrs.tooltipAnimationClass); + } + } + }; +}]) + +.directive('uibTooltipPopup', function() { + return { + restrict: 'A', + scope: { content: '@' }, + templateUrl: 'uib/template/tooltip/tooltip-popup.html' + }; +}) + +.directive('uibTooltip', [ '$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibTooltip', 'tooltip', 'mouseenter'); +}]) + +.directive('uibTooltipTemplatePopup', function() { + return { + restrict: 'A', + scope: { contentExp: '&', originScope: '&' }, + templateUrl: 'uib/template/tooltip/tooltip-template-popup.html' + }; +}) + +.directive('uibTooltipTemplate', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibTooltipTemplate', 'tooltip', 'mouseenter', { + useContentExp: true + }); +}]) + +.directive('uibTooltipHtmlPopup', function() { + return { + restrict: 'A', + scope: { contentExp: '&' }, + templateUrl: 'uib/template/tooltip/tooltip-html-popup.html' + }; +}) + +.directive('uibTooltipHtml', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibTooltipHtml', 'tooltip', 'mouseenter', { + useContentExp: true + }); +}]); + +/** + * 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, and selector delegatation. + */ +angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip']) + +.directive('uibPopoverTemplatePopup', function() { + return { + restrict: 'A', + scope: { uibTitle: '@', contentExp: '&', originScope: '&' }, + templateUrl: 'uib/template/popover/popover-template.html' + }; +}) + +.directive('uibPopoverTemplate', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibPopoverTemplate', 'popover', 'click', { + useContentExp: true + }); +}]) + +.directive('uibPopoverHtmlPopup', function() { + return { + restrict: 'A', + scope: { contentExp: '&', uibTitle: '@' }, + templateUrl: 'uib/template/popover/popover-html.html' + }; +}) + +.directive('uibPopoverHtml', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibPopoverHtml', 'popover', 'click', { + useContentExp: true + }); +}]) + +.directive('uibPopoverPopup', function() { + return { + restrict: 'A', + scope: { uibTitle: '@', content: '@' }, + templateUrl: 'uib/template/popover/popover.html' + }; +}) + +.directive('uibPopover', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibPopover', 'popover', 'click'); +}]); + +angular.module('ui.bootstrap.progressbar', []) + +.constant('uibProgressConfig', { + animate: true, + max: 100 +}) + +.controller('UibProgressController', ['$scope', '$attrs', 'uibProgressConfig', function($scope, $attrs, progressConfig) { + var self = this, + animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate; + + this.bars = []; + $scope.max = getMaxOrDefault(); + + this.addBar = function(bar, element, attrs) { + if (!animate) { + element.css({'transition': 'none'}); + } + + this.bars.push(bar); + + bar.max = getMaxOrDefault(); + bar.title = attrs && angular.isDefined(attrs.title) ? attrs.title : 'progressbar'; + + bar.$watch('value', function(value) { + bar.recalculatePercentage(); + }); + + bar.recalculatePercentage = function() { + var totalPercentage = self.bars.reduce(function(total, bar) { + bar.percent = +(100 * bar.value / bar.max).toFixed(2); + return total + bar.percent; + }, 0); + + if (totalPercentage > 100) { + bar.percent -= totalPercentage - 100; + } + }; + + bar.$on('$destroy', function() { + element = null; + self.removeBar(bar); + }); + }; + + this.removeBar = function(bar) { + this.bars.splice(this.bars.indexOf(bar), 1); + this.bars.forEach(function (bar) { + bar.recalculatePercentage(); + }); + }; + + //$attrs.$observe('maxParam', function(maxParam) { + $scope.$watch('maxParam', function(maxParam) { + self.bars.forEach(function(bar) { + bar.max = getMaxOrDefault(); + bar.recalculatePercentage(); + }); + }); + + function getMaxOrDefault () { + return angular.isDefined($scope.maxParam) ? $scope.maxParam : progressConfig.max; + } +}]) + +.directive('uibProgress', function() { + return { + replace: true, + transclude: true, + controller: 'UibProgressController', + require: 'uibProgress', + scope: { + maxParam: '=?max' + }, + templateUrl: 'uib/template/progressbar/progress.html' + }; +}) + +.directive('uibBar', function() { + return { + replace: true, + transclude: true, + require: '^uibProgress', + scope: { + value: '=', + type: '@' + }, + templateUrl: 'uib/template/progressbar/bar.html', + link: function(scope, element, attrs, progressCtrl) { + progressCtrl.addBar(scope, element, attrs); + } + }; +}) + +.directive('uibProgressbar', function() { + return { + replace: true, + transclude: true, + controller: 'UibProgressController', + scope: { + value: '=', + maxParam: '=?max', + type: '@' + }, + templateUrl: 'uib/template/progressbar/progressbar.html', + link: function(scope, element, attrs, progressCtrl) { + progressCtrl.addBar(scope, angular.element(element.children()[0]), {title: attrs.title}); + } + }; +}); + +angular.module('ui.bootstrap.rating', []) + +.constant('uibRatingConfig', { + max: 5, + stateOn: null, + stateOff: null, + enableReset: true, + titles: ['one', 'two', 'three', 'four', 'five'] +}) + +.controller('UibRatingController', ['$scope', '$attrs', 'uibRatingConfig', function($scope, $attrs, ratingConfig) { + var ngModelCtrl = { $setViewValue: angular.noop }, + self = this; + + this.init = function(ngModelCtrl_) { + ngModelCtrl = ngModelCtrl_; + ngModelCtrl.$render = this.render; + + ngModelCtrl.$formatters.push(function(value) { + if (angular.isNumber(value) && value << 0 !== value) { + value = Math.round(value); + } + + return value; + }); + + this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn; + this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff; + this.enableReset = angular.isDefined($attrs.enableReset) ? + $scope.$parent.$eval($attrs.enableReset) : ratingConfig.enableReset; + var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles; + this.titles = angular.isArray(tmpTitles) && tmpTitles.length > 0 ? + tmpTitles : ratingConfig.titles; + + var ratingStates = angular.isDefined($attrs.ratingStates) ? + $scope.$parent.$eval($attrs.ratingStates) : + new Array(angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max); + $scope.range = this.buildTemplateObjects(ratingStates); + }; + + this.buildTemplateObjects = function(states) { + for (var i = 0, n = states.length; i < n; i++) { + states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff, title: this.getTitle(i) }, states[i]); + } + return states; + }; + + this.getTitle = function(index) { + if (index >= this.titles.length) { + return index + 1; + } + + return this.titles[index]; + }; + + $scope.rate = function(value) { + if (!$scope.readonly && value >= 0 && value <= $scope.range.length) { + var newViewValue = self.enableReset && ngModelCtrl.$viewValue === value ? 0 : value; + ngModelCtrl.$setViewValue(newViewValue); + ngModelCtrl.$render(); + } + }; + + $scope.enter = function(value) { + if (!$scope.readonly) { + $scope.value = value; + } + $scope.onHover({value: value}); + }; + + $scope.reset = function() { + $scope.value = ngModelCtrl.$viewValue; + $scope.onLeave(); + }; + + $scope.onKeydown = function(evt) { + if (/(37|38|39|40)/.test(evt.which)) { + evt.preventDefault(); + evt.stopPropagation(); + $scope.rate($scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1)); + } + }; + + this.render = function() { + $scope.value = ngModelCtrl.$viewValue; + $scope.title = self.getTitle($scope.value - 1); + }; +}]) + +.directive('uibRating', function() { + return { + require: ['uibRating', 'ngModel'], + restrict: 'A', + scope: { + readonly: '=?readOnly', + onHover: '&', + onLeave: '&' + }, + controller: 'UibRatingController', + templateUrl: 'uib/template/rating/rating.html', + link: function(scope, element, attrs, ctrls) { + var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + ratingCtrl.init(ngModelCtrl); + } + }; +}); + +angular.module('ui.bootstrap.tabs', []) + +.controller('UibTabsetController', ['$scope', function ($scope) { + var ctrl = this, + oldIndex; + ctrl.tabs = []; + + ctrl.select = function(index, evt) { + if (!destroyed) { + var previousIndex = findTabIndex(oldIndex); + var previousSelected = ctrl.tabs[previousIndex]; + if (previousSelected) { + previousSelected.tab.onDeselect({ + $event: evt, + $selectedIndex: index + }); + if (evt && evt.isDefaultPrevented()) { + return; + } + previousSelected.tab.active = false; + } + + var selected = ctrl.tabs[index]; + if (selected) { + selected.tab.onSelect({ + $event: evt + }); + selected.tab.active = true; + ctrl.active = selected.index; + oldIndex = selected.index; + } else if (!selected && angular.isDefined(oldIndex)) { + ctrl.active = null; + oldIndex = null; + } + } + }; + + ctrl.addTab = function addTab(tab) { + ctrl.tabs.push({ + tab: tab, + index: tab.index + }); + ctrl.tabs.sort(function(t1, t2) { + if (t1.index > t2.index) { + return 1; + } + + if (t1.index < t2.index) { + return -1; + } + + return 0; + }); + + if (tab.index === ctrl.active || !angular.isDefined(ctrl.active) && ctrl.tabs.length === 1) { + var newActiveIndex = findTabIndex(tab.index); + ctrl.select(newActiveIndex); + } + }; + + ctrl.removeTab = function removeTab(tab) { + var index; + for (var i = 0; i < ctrl.tabs.length; i++) { + if (ctrl.tabs[i].tab === tab) { + index = i; + break; + } + } + + if (ctrl.tabs[index].index === ctrl.active) { + var newActiveTabIndex = index === ctrl.tabs.length - 1 ? + index - 1 : index + 1 % ctrl.tabs.length; + ctrl.select(newActiveTabIndex); + } + + ctrl.tabs.splice(index, 1); + }; + + $scope.$watch('tabset.active', function(val) { + if (angular.isDefined(val) && val !== oldIndex) { + ctrl.select(findTabIndex(val)); + } + }); + + var destroyed; + $scope.$on('$destroy', function() { + destroyed = true; + }); + + function findTabIndex(index) { + for (var i = 0; i < ctrl.tabs.length; i++) { + if (ctrl.tabs[i].index === index) { + return i; + } + } + } +}]) + +.directive('uibTabset', function() { + return { + transclude: true, + replace: true, + scope: {}, + bindToController: { + active: '=?', + type: '@' + }, + controller: 'UibTabsetController', + controllerAs: 'tabset', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/tabs/tabset.html'; + }, + link: function(scope, element, attrs) { + scope.vertical = angular.isDefined(attrs.vertical) ? + scope.$parent.$eval(attrs.vertical) : false; + scope.justified = angular.isDefined(attrs.justified) ? + scope.$parent.$eval(attrs.justified) : false; + } + }; +}) + +.directive('uibTab', ['$parse', function($parse) { + return { + require: '^uibTabset', + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/tabs/tab.html'; + }, + transclude: true, + scope: { + heading: '@', + index: '=?', + classes: '@?', + onSelect: '&select', //This callback is called in contentHeadingTransclude + //once it inserts the tab's content into the dom + onDeselect: '&deselect' + }, + controller: function() { + //Empty controller so other directives can require being 'under' a tab + }, + controllerAs: 'tab', + link: function(scope, elm, attrs, tabsetCtrl, transclude) { + scope.disabled = false; + if (attrs.disable) { + scope.$parent.$watch($parse(attrs.disable), function(value) { + scope.disabled = !! value; + }); + } + + if (angular.isUndefined(attrs.index)) { + if (tabsetCtrl.tabs && tabsetCtrl.tabs.length) { + scope.index = Math.max.apply(null, tabsetCtrl.tabs.map(function(t) { return t.index; })) + 1; + } else { + scope.index = 0; + } + } + + if (angular.isUndefined(attrs.classes)) { + scope.classes = ''; + } + + scope.select = function(evt) { + if (!scope.disabled) { + var index; + for (var i = 0; i < tabsetCtrl.tabs.length; i++) { + if (tabsetCtrl.tabs[i].tab === scope) { + index = i; + break; + } + } + + tabsetCtrl.select(index, evt); + } + }; + + tabsetCtrl.addTab(scope); + scope.$on('$destroy', function() { + tabsetCtrl.removeTab(scope); + }); + + //We need to transclude later, once the content container is ready. + //when this link happens, we're inside a tab heading. + scope.$transcludeFn = transclude; + } + }; +}]) + +.directive('uibTabHeadingTransclude', function() { + return { + restrict: 'A', + require: '^uibTab', + link: function(scope, elm) { + scope.$watch('headingElement', function updateHeadingElement(heading) { + if (heading) { + elm.html(''); + elm.append(heading); + } + }); + } + }; +}) + +.directive('uibTabContentTransclude', function() { + return { + restrict: 'A', + require: '^uibTabset', + link: function(scope, elm, attrs) { + var tab = scope.$eval(attrs.uibTabContentTransclude).tab; + + //Now our tab is ready to be transcluded: both the tab heading area + //and the tab content area are loaded. Transclude 'em both. + tab.$transcludeFn(tab.$parent, function(contents) { + angular.forEach(contents, function(node) { + if (isTabHeading(node)) { + //Let tabHeadingTransclude know. + tab.headingElement = node; + } else { + elm.append(node); + } + }); + }); + } + }; + + function isTabHeading(node) { + return node.tagName && ( + node.hasAttribute('uib-tab-heading') || + node.hasAttribute('data-uib-tab-heading') || + node.hasAttribute('x-uib-tab-heading') || + node.tagName.toLowerCase() === 'uib-tab-heading' || + node.tagName.toLowerCase() === 'data-uib-tab-heading' || + node.tagName.toLowerCase() === 'x-uib-tab-heading' || + node.tagName.toLowerCase() === 'uib:tab-heading' + ); + } +}); + +angular.module('ui.bootstrap.timepicker', []) + +.constant('uibTimepickerConfig', { + hourStep: 1, + minuteStep: 1, + secondStep: 1, + showMeridian: true, + showSeconds: false, + meridians: null, + readonlyInput: false, + mousewheel: true, + arrowkeys: true, + showSpinners: true, + templateUrl: 'uib/template/timepicker/timepicker.html' +}) + +.controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) { + var hoursModelCtrl, minutesModelCtrl, secondsModelCtrl; + var selected = new Date(), + watchers = [], + ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl + meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS, + padHours = angular.isDefined($attrs.padHours) ? $scope.$parent.$eval($attrs.padHours) : true; + + $scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0; + $element.removeAttr('tabindex'); + + this.init = function(ngModelCtrl_, inputs) { + ngModelCtrl = ngModelCtrl_; + ngModelCtrl.$render = this.render; + + ngModelCtrl.$formatters.unshift(function(modelValue) { + return modelValue ? new Date(modelValue) : null; + }); + + var hoursInputEl = inputs.eq(0), + minutesInputEl = inputs.eq(1), + secondsInputEl = inputs.eq(2); + + hoursModelCtrl = hoursInputEl.controller('ngModel'); + minutesModelCtrl = minutesInputEl.controller('ngModel'); + secondsModelCtrl = secondsInputEl.controller('ngModel'); + + var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel; + + if (mousewheel) { + this.setupMousewheelEvents(hoursInputEl, minutesInputEl, secondsInputEl); + } + + var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys; + if (arrowkeys) { + this.setupArrowkeyEvents(hoursInputEl, minutesInputEl, secondsInputEl); + } + + $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput; + this.setupInputEvents(hoursInputEl, minutesInputEl, secondsInputEl); + }; + + var hourStep = timepickerConfig.hourStep; + if ($attrs.hourStep) { + watchers.push($scope.$parent.$watch($parse($attrs.hourStep), function(value) { + hourStep = +value; + })); + } + + var minuteStep = timepickerConfig.minuteStep; + if ($attrs.minuteStep) { + watchers.push($scope.$parent.$watch($parse($attrs.minuteStep), function(value) { + minuteStep = +value; + })); + } + + var min; + watchers.push($scope.$parent.$watch($parse($attrs.min), function(value) { + var dt = new Date(value); + min = isNaN(dt) ? undefined : dt; + })); + + var max; + watchers.push($scope.$parent.$watch($parse($attrs.max), function(value) { + var dt = new Date(value); + max = isNaN(dt) ? undefined : dt; + })); + + var disabled = false; + if ($attrs.ngDisabled) { + watchers.push($scope.$parent.$watch($parse($attrs.ngDisabled), function(value) { + disabled = value; + })); + } + + $scope.noIncrementHours = function() { + var incrementedSelected = addMinutes(selected, hourStep * 60); + return disabled || incrementedSelected > max || + incrementedSelected < selected && incrementedSelected < min; + }; + + $scope.noDecrementHours = function() { + var decrementedSelected = addMinutes(selected, -hourStep * 60); + return disabled || decrementedSelected < min || + decrementedSelected > selected && decrementedSelected > max; + }; + + $scope.noIncrementMinutes = function() { + var incrementedSelected = addMinutes(selected, minuteStep); + return disabled || incrementedSelected > max || + incrementedSelected < selected && incrementedSelected < min; + }; + + $scope.noDecrementMinutes = function() { + var decrementedSelected = addMinutes(selected, -minuteStep); + return disabled || decrementedSelected < min || + decrementedSelected > selected && decrementedSelected > max; + }; + + $scope.noIncrementSeconds = function() { + var incrementedSelected = addSeconds(selected, secondStep); + return disabled || incrementedSelected > max || + incrementedSelected < selected && incrementedSelected < min; + }; + + $scope.noDecrementSeconds = function() { + var decrementedSelected = addSeconds(selected, -secondStep); + return disabled || decrementedSelected < min || + decrementedSelected > selected && decrementedSelected > max; + }; + + $scope.noToggleMeridian = function() { + if (selected.getHours() < 12) { + return disabled || addMinutes(selected, 12 * 60) > max; + } + + return disabled || addMinutes(selected, -12 * 60) < min; + }; + + var secondStep = timepickerConfig.secondStep; + if ($attrs.secondStep) { + watchers.push($scope.$parent.$watch($parse($attrs.secondStep), function(value) { + secondStep = +value; + })); + } + + $scope.showSeconds = timepickerConfig.showSeconds; + if ($attrs.showSeconds) { + watchers.push($scope.$parent.$watch($parse($attrs.showSeconds), function(value) { + $scope.showSeconds = !!value; + })); + } + + // 12H / 24H mode + $scope.showMeridian = timepickerConfig.showMeridian; + if ($attrs.showMeridian) { + watchers.push($scope.$parent.$watch($parse($attrs.showMeridian), function(value) { + $scope.showMeridian = !!value; + + if (ngModelCtrl.$error.time) { + // Evaluate from template + var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate(); + if (angular.isDefined(hours) && angular.isDefined(minutes)) { + selected.setHours(hours); + refresh(); + } + } else { + updateTemplate(); + } + })); + } + + // Get $scope.hours in 24H mode if valid + function getHoursFromTemplate() { + var hours = +$scope.hours; + var valid = $scope.showMeridian ? hours > 0 && hours < 13 : + hours >= 0 && hours < 24; + if (!valid || $scope.hours === '') { + return undefined; + } + + if ($scope.showMeridian) { + if (hours === 12) { + hours = 0; + } + if ($scope.meridian === meridians[1]) { + hours = hours + 12; + } + } + return hours; + } + + function getMinutesFromTemplate() { + var minutes = +$scope.minutes; + var valid = minutes >= 0 && minutes < 60; + if (!valid || $scope.minutes === '') { + return undefined; + } + return minutes; + } + + function getSecondsFromTemplate() { + var seconds = +$scope.seconds; + return seconds >= 0 && seconds < 60 ? seconds : undefined; + } + + function pad(value, noPad) { + if (value === null) { + return ''; + } + + return angular.isDefined(value) && value.toString().length < 2 && !noPad ? + '0' + value : value.toString(); + } + + // Respond on mousewheel spin + this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { + var isScrollingUp = function(e) { + if (e.originalEvent) { + e = e.originalEvent; + } + //pick correct delta variable depending on event + var delta = e.wheelDelta ? e.wheelDelta : -e.deltaY; + return e.detail || delta > 0; + }; + + hoursInputEl.on('mousewheel wheel', function(e) { + if (!disabled) { + $scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours()); + } + e.preventDefault(); + }); + + minutesInputEl.on('mousewheel wheel', function(e) { + if (!disabled) { + $scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes()); + } + e.preventDefault(); + }); + + secondsInputEl.on('mousewheel wheel', function(e) { + if (!disabled) { + $scope.$apply(isScrollingUp(e) ? $scope.incrementSeconds() : $scope.decrementSeconds()); + } + e.preventDefault(); + }); + }; + + // Respond on up/down arrowkeys + this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { + hoursInputEl.on('keydown', function(e) { + if (!disabled) { + if (e.which === 38) { // up + e.preventDefault(); + $scope.incrementHours(); + $scope.$apply(); + } else if (e.which === 40) { // down + e.preventDefault(); + $scope.decrementHours(); + $scope.$apply(); + } + } + }); + + minutesInputEl.on('keydown', function(e) { + if (!disabled) { + if (e.which === 38) { // up + e.preventDefault(); + $scope.incrementMinutes(); + $scope.$apply(); + } else if (e.which === 40) { // down + e.preventDefault(); + $scope.decrementMinutes(); + $scope.$apply(); + } + } + }); + + secondsInputEl.on('keydown', function(e) { + if (!disabled) { + if (e.which === 38) { // up + e.preventDefault(); + $scope.incrementSeconds(); + $scope.$apply(); + } else if (e.which === 40) { // down + e.preventDefault(); + $scope.decrementSeconds(); + $scope.$apply(); + } + } + }); + }; + + this.setupInputEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { + if ($scope.readonlyInput) { + $scope.updateHours = angular.noop; + $scope.updateMinutes = angular.noop; + $scope.updateSeconds = angular.noop; + return; + } + + var invalidate = function(invalidHours, invalidMinutes, invalidSeconds) { + ngModelCtrl.$setViewValue(null); + ngModelCtrl.$setValidity('time', false); + if (angular.isDefined(invalidHours)) { + $scope.invalidHours = invalidHours; + if (hoursModelCtrl) { + hoursModelCtrl.$setValidity('hours', false); + } + } + + if (angular.isDefined(invalidMinutes)) { + $scope.invalidMinutes = invalidMinutes; + if (minutesModelCtrl) { + minutesModelCtrl.$setValidity('minutes', false); + } + } + + if (angular.isDefined(invalidSeconds)) { + $scope.invalidSeconds = invalidSeconds; + if (secondsModelCtrl) { + secondsModelCtrl.$setValidity('seconds', false); + } + } + }; + + $scope.updateHours = function() { + var hours = getHoursFromTemplate(), + minutes = getMinutesFromTemplate(); + + ngModelCtrl.$setDirty(); + + if (angular.isDefined(hours) && angular.isDefined(minutes)) { + selected.setHours(hours); + selected.setMinutes(minutes); + if (selected < min || selected > max) { + invalidate(true); + } else { + refresh('h'); + } + } else { + invalidate(true); + } + }; + + hoursInputEl.on('blur', function(e) { + ngModelCtrl.$setTouched(); + if (modelIsEmpty()) { + makeValid(); + } else if ($scope.hours === null || $scope.hours === '') { + invalidate(true); + } else if (!$scope.invalidHours && $scope.hours < 10) { + $scope.$apply(function() { + $scope.hours = pad($scope.hours, !padHours); + }); + } + }); + + $scope.updateMinutes = function() { + var minutes = getMinutesFromTemplate(), + hours = getHoursFromTemplate(); + + ngModelCtrl.$setDirty(); + + if (angular.isDefined(minutes) && angular.isDefined(hours)) { + selected.setHours(hours); + selected.setMinutes(minutes); + if (selected < min || selected > max) { + invalidate(undefined, true); + } else { + refresh('m'); + } + } else { + invalidate(undefined, true); + } + }; + + minutesInputEl.on('blur', function(e) { + ngModelCtrl.$setTouched(); + if (modelIsEmpty()) { + makeValid(); + } else if ($scope.minutes === null) { + invalidate(undefined, true); + } else if (!$scope.invalidMinutes && $scope.minutes < 10) { + $scope.$apply(function() { + $scope.minutes = pad($scope.minutes); + }); + } + }); + + $scope.updateSeconds = function() { + var seconds = getSecondsFromTemplate(); + + ngModelCtrl.$setDirty(); + + if (angular.isDefined(seconds)) { + selected.setSeconds(seconds); + refresh('s'); + } else { + invalidate(undefined, undefined, true); + } + }; + + secondsInputEl.on('blur', function(e) { + if (modelIsEmpty()) { + makeValid(); + } else if (!$scope.invalidSeconds && $scope.seconds < 10) { + $scope.$apply( function() { + $scope.seconds = pad($scope.seconds); + }); + } + }); + + }; + + this.render = function() { + var date = ngModelCtrl.$viewValue; + + if (isNaN(date)) { + ngModelCtrl.$setValidity('time', false); + $log.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.'); + } else { + if (date) { + selected = date; + } + + if (selected < min || selected > max) { + ngModelCtrl.$setValidity('time', false); + $scope.invalidHours = true; + $scope.invalidMinutes = true; + } else { + makeValid(); + } + updateTemplate(); + } + }; + + // Call internally when we know that model is valid. + function refresh(keyboardChange) { + makeValid(); + ngModelCtrl.$setViewValue(new Date(selected)); + updateTemplate(keyboardChange); + } + + function makeValid() { + if (hoursModelCtrl) { + hoursModelCtrl.$setValidity('hours', true); + } + + if (minutesModelCtrl) { + minutesModelCtrl.$setValidity('minutes', true); + } + + if (secondsModelCtrl) { + secondsModelCtrl.$setValidity('seconds', true); + } + + ngModelCtrl.$setValidity('time', true); + $scope.invalidHours = false; + $scope.invalidMinutes = false; + $scope.invalidSeconds = false; + } + + function updateTemplate(keyboardChange) { + if (!ngModelCtrl.$modelValue) { + $scope.hours = null; + $scope.minutes = null; + $scope.seconds = null; + $scope.meridian = meridians[0]; + } else { + var hours = selected.getHours(), + minutes = selected.getMinutes(), + seconds = selected.getSeconds(); + + if ($scope.showMeridian) { + hours = hours === 0 || hours === 12 ? 12 : hours % 12; // Convert 24 to 12 hour system + } + + $scope.hours = keyboardChange === 'h' ? hours : pad(hours, !padHours); + if (keyboardChange !== 'm') { + $scope.minutes = pad(minutes); + } + $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1]; + + if (keyboardChange !== 's') { + $scope.seconds = pad(seconds); + } + $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1]; + } + } + + function addSecondsToSelected(seconds) { + selected = addSeconds(selected, seconds); + refresh(); + } + + function addMinutes(selected, minutes) { + return addSeconds(selected, minutes*60); + } + + function addSeconds(date, seconds) { + var dt = new Date(date.getTime() + seconds * 1000); + var newDate = new Date(date); + newDate.setHours(dt.getHours(), dt.getMinutes(), dt.getSeconds()); + return newDate; + } + + function modelIsEmpty() { + return ($scope.hours === null || $scope.hours === '') && + ($scope.minutes === null || $scope.minutes === '') && + (!$scope.showSeconds || $scope.showSeconds && ($scope.seconds === null || $scope.seconds === '')); + } + + $scope.showSpinners = angular.isDefined($attrs.showSpinners) ? + $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners; + + $scope.incrementHours = function() { + if (!$scope.noIncrementHours()) { + addSecondsToSelected(hourStep * 60 * 60); + } + }; + + $scope.decrementHours = function() { + if (!$scope.noDecrementHours()) { + addSecondsToSelected(-hourStep * 60 * 60); + } + }; + + $scope.incrementMinutes = function() { + if (!$scope.noIncrementMinutes()) { + addSecondsToSelected(minuteStep * 60); + } + }; + + $scope.decrementMinutes = function() { + if (!$scope.noDecrementMinutes()) { + addSecondsToSelected(-minuteStep * 60); + } + }; + + $scope.incrementSeconds = function() { + if (!$scope.noIncrementSeconds()) { + addSecondsToSelected(secondStep); + } + }; + + $scope.decrementSeconds = function() { + if (!$scope.noDecrementSeconds()) { + addSecondsToSelected(-secondStep); + } + }; + + $scope.toggleMeridian = function() { + var minutes = getMinutesFromTemplate(), + hours = getHoursFromTemplate(); + + if (!$scope.noToggleMeridian()) { + if (angular.isDefined(minutes) && angular.isDefined(hours)) { + addSecondsToSelected(12 * 60 * (selected.getHours() < 12 ? 60 : -60)); + } else { + $scope.meridian = $scope.meridian === meridians[0] ? meridians[1] : meridians[0]; + } + } + }; + + $scope.blur = function() { + ngModelCtrl.$setTouched(); + }; + + $scope.$on('$destroy', function() { + while (watchers.length) { + watchers.shift()(); + } + }); +}]) + +.directive('uibTimepicker', ['uibTimepickerConfig', function(uibTimepickerConfig) { + return { + require: ['uibTimepicker', '?^ngModel'], + restrict: 'A', + controller: 'UibTimepickerController', + controllerAs: 'timepicker', + scope: {}, + templateUrl: function(element, attrs) { + return attrs.templateUrl || uibTimepickerConfig.templateUrl; + }, + link: function(scope, element, attrs, ctrls) { + var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (ngModelCtrl) { + timepickerCtrl.init(ngModelCtrl, element.find('input')); + } + } + }; +}]); + +angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', '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('uibTypeaheadParser', ['$parse', function($parse) { + // 000001111111100000000000002222222200000000000000003333333333333330000000000044444444000 + var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/; + return { + parse: function(input) { + var match = input.match(TYPEAHEAD_REGEXP); + 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]) + }; + } + }; + }]) + + .controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$$debounce', '$uibPosition', 'uibTypeaheadParser', + function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $$debounce, $position, typeaheadParser) { + var HOT_KEYS = [9, 13, 27, 38, 40]; + var eventDebounceTime = 200; + var modelCtrl, ngModelOptions; + //SUPPORTED ATTRIBUTES (OPTIONS) + + //minimal no of characters that needs to be entered before typeahead kicks-in + var minLength = originalScope.$eval(attrs.typeaheadMinLength); + if (!minLength && minLength !== 0) { + minLength = 1; + } + + originalScope.$watch(attrs.typeaheadMinLength, function (newVal) { + minLength = !newVal && newVal !== 0 ? 1 : newVal; + }); + + //minimal wait time after last character typed before typeahead kicks-in + var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0; + + //should it restrict model values to the ones selected from the popup only? + var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false; + originalScope.$watch(attrs.typeaheadEditable, function (newVal) { + isEditable = newVal !== false; + }); + + //binding to a variable that indicates if matches are being retrieved asynchronously + var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop; + + //a function to determine if an event should cause selection + var isSelectEvent = attrs.typeaheadShouldSelect ? $parse(attrs.typeaheadShouldSelect) : function(scope, vals) { + var evt = vals.$event; + return evt.which === 13 || evt.which === 9; + }; + + //a callback executed when a match is selected + var onSelectCallback = $parse(attrs.typeaheadOnSelect); + + //should it select highlighted popup value when losing focus? + var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false; + + //binding to a variable that indicates if there were no results after the query is completed + var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop; + + var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined; + + var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false; + + var appendTo = attrs.typeaheadAppendTo ? + originalScope.$eval(attrs.typeaheadAppendTo) : null; + + var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false; + + //If input matches an item of the list exactly, select it automatically + var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false; + + //binding to a variable that indicates if dropdown is open + var isOpenSetter = $parse(attrs.typeaheadIsOpen).assign || angular.noop; + + var showHint = originalScope.$eval(attrs.typeaheadShowHint) || false; + + //INTERNAL VARIABLES + + //model setter executed upon match selection + var parsedModel = $parse(attrs.ngModel); + var invokeModelSetter = $parse(attrs.ngModel + '($$$p)'); + var $setModelValue = function(scope, newValue) { + if (angular.isFunction(parsedModel(originalScope)) && + ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) { + return invokeModelSetter(scope, {$$$p: newValue}); + } + + return parsedModel.assign(scope, newValue); + }; + + //expressions used by typeahead + var parserResult = typeaheadParser.parse(attrs.uibTypeahead); + + var hasFocus; + + //Used to avoid bug in iOS webview where iOS keyboard does not fire + //mousedown & mouseup events + //Issue #3699 + var selected; + + //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(); + var offDestroy = originalScope.$on('$destroy', function() { + scope.$destroy(); + }); + scope.$on('$destroy', offDestroy); + + // WAI-ARIA + var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000); + element.attr({ + 'aria-autocomplete': 'list', + 'aria-expanded': false, + 'aria-owns': popupId + }); + + var inputsContainer, hintInputElem; + //add read-only input to show hint + if (showHint) { + inputsContainer = angular.element('
      '); + inputsContainer.css('position', 'relative'); + element.after(inputsContainer); + hintInputElem = element.clone(); + hintInputElem.attr('placeholder', ''); + hintInputElem.attr('tabindex', '-1'); + hintInputElem.val(''); + hintInputElem.css({ + 'position': 'absolute', + 'top': '0px', + 'left': '0px', + 'border-color': 'transparent', + 'box-shadow': 'none', + 'opacity': 1, + 'background': 'none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)', + 'color': '#999' + }); + element.css({ + 'position': 'relative', + 'vertical-align': 'top', + 'background-color': 'transparent' + }); + + if (hintInputElem.attr('id')) { + hintInputElem.removeAttr('id'); // remove duplicate id if present. + } + inputsContainer.append(hintInputElem); + hintInputElem.after(element); + } + + //pop-up element used to display matches + var popUpEl = angular.element('
      '); + popUpEl.attr({ + id: popupId, + matches: 'matches', + active: 'activeIdx', + select: 'select(activeIdx, evt)', + 'move-in-progress': 'moveInProgress', + query: 'query', + position: 'position', + 'assign-is-open': 'assignIsOpen(isOpen)', + debounce: 'debounceUpdate' + }); + //custom item template + if (angular.isDefined(attrs.typeaheadTemplateUrl)) { + popUpEl.attr('template-url', attrs.typeaheadTemplateUrl); + } + + if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) { + popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl); + } + + var resetHint = function() { + if (showHint) { + hintInputElem.val(''); + } + }; + + var resetMatches = function() { + scope.matches = []; + scope.activeIdx = -1; + element.attr('aria-expanded', false); + resetHint(); + }; + + var getMatchId = function(index) { + return popupId + '-option-' + index; + }; + + // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead. + // This attribute is added or removed automatically when the `activeIdx` changes. + scope.$watch('activeIdx', function(index) { + if (index < 0) { + element.removeAttr('aria-activedescendant'); + } else { + element.attr('aria-activedescendant', getMatchId(index)); + } + }); + + var inputIsExactMatch = function(inputValue, index) { + if (scope.matches.length > index && inputValue) { + return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase(); + } + + return false; + }; + + var getMatchesAsync = function(inputValue, evt) { + var locals = {$viewValue: inputValue}; + isLoadingSetter(originalScope, true); + isNoResultsSetter(originalScope, false); + $q.when(parserResult.source(originalScope, 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 + var onCurrentRequest = inputValue === modelCtrl.$viewValue; + if (onCurrentRequest && hasFocus) { + if (matches && matches.length > 0) { + scope.activeIdx = focusFirst ? 0 : -1; + isNoResultsSetter(originalScope, false); + scope.matches.length = 0; + + //transform labels + for (var i = 0; i < matches.length; i++) { + locals[parserResult.itemName] = matches[i]; + scope.matches.push({ + id: getMatchId(i), + label: parserResult.viewMapper(scope, locals), + model: matches[i] + }); + } + + scope.query = inputValue; + //position pop-up with matches - we need to re-calculate its position each time we are opening a window + //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page + //due to other elements being rendered + recalculatePosition(); + + element.attr('aria-expanded', true); + + //Select the single remaining option if user input matches + if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) { + if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) { + $$debounce(function() { + scope.select(0, evt); + }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']); + } else { + scope.select(0, evt); + } + } + + if (showHint) { + var firstLabel = scope.matches[0].label; + if (angular.isString(inputValue) && + inputValue.length > 0 && + firstLabel.slice(0, inputValue.length).toUpperCase() === inputValue.toUpperCase()) { + hintInputElem.val(inputValue + firstLabel.slice(inputValue.length)); + } else { + hintInputElem.val(''); + } + } + } else { + resetMatches(); + isNoResultsSetter(originalScope, true); + } + } + if (onCurrentRequest) { + isLoadingSetter(originalScope, false); + } + }, function() { + resetMatches(); + isLoadingSetter(originalScope, false); + isNoResultsSetter(originalScope, true); + }); + }; + + // bind events only if appendToBody params exist - performance feature + if (appendToBody) { + angular.element($window).on('resize', fireRecalculating); + $document.find('body').on('scroll', fireRecalculating); + } + + // Declare the debounced function outside recalculating for + // proper debouncing + var debouncedRecalculate = $$debounce(function() { + // if popup is visible + if (scope.matches.length) { + recalculatePosition(); + } + + scope.moveInProgress = false; + }, eventDebounceTime); + + // Default progress type + scope.moveInProgress = false; + + function fireRecalculating() { + if (!scope.moveInProgress) { + scope.moveInProgress = true; + scope.$digest(); + } + + debouncedRecalculate(); + } + + // recalculate actual position and set new values to scope + // after digest loop is popup in right position + function recalculatePosition() { + scope.position = appendToBody ? $position.offset(element) : $position.position(element); + scope.position.top += element.prop('offsetHeight'); + } + + //we need to propagate user's query so we can higlight matches + scope.query = undefined; + + //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later + var timeoutPromise; + + var scheduleSearchWithTimeout = function(inputValue) { + timeoutPromise = $timeout(function() { + getMatchesAsync(inputValue); + }, waitTime); + }; + + var cancelPreviousTimeout = function() { + if (timeoutPromise) { + $timeout.cancel(timeoutPromise); + } + }; + + resetMatches(); + + scope.assignIsOpen = function (isOpen) { + isOpenSetter(originalScope, isOpen); + }; + + scope.select = function(activeIdx, evt) { + //called from within the $digest() cycle + var locals = {}; + var model, item; + + selected = true; + locals[parserResult.itemName] = item = scope.matches[activeIdx].model; + model = parserResult.modelMapper(originalScope, locals); + $setModelValue(originalScope, model); + modelCtrl.$setValidity('editable', true); + modelCtrl.$setValidity('parse', true); + + onSelectCallback(originalScope, { + $item: item, + $model: model, + $label: parserResult.viewMapper(originalScope, locals), + $event: evt + }); + + resetMatches(); + + //return focus to the input element if a match was selected via a mouse click event + // use timeout to avoid $rootScope:inprog error + if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) { + $timeout(function() { element[0].focus(); }, 0, false); + } + }; + + //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27) + element.on('keydown', function(evt) { + //typeahead is open and an "interesting" key was pressed + if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) { + return; + } + + var shouldSelect = isSelectEvent(originalScope, {$event: evt}); + + /** + * if there's nothing selected (i.e. focusFirst) and enter or tab is hit + * or + * shift + tab is pressed to bring focus to the previous element + * then clear the results + */ + if (scope.activeIdx === -1 && shouldSelect || evt.which === 9 && !!evt.shiftKey) { + resetMatches(); + scope.$digest(); + return; + } + + evt.preventDefault(); + var target; + switch (evt.which) { + case 27: // escape + evt.stopPropagation(); + + resetMatches(); + originalScope.$digest(); + break; + case 38: // up arrow + scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1; + scope.$digest(); + target = popUpEl[0].querySelectorAll('.uib-typeahead-match')[scope.activeIdx]; + target.parentNode.scrollTop = target.offsetTop; + break; + case 40: // down arrow + scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length; + scope.$digest(); + target = popUpEl[0].querySelectorAll('.uib-typeahead-match')[scope.activeIdx]; + target.parentNode.scrollTop = target.offsetTop; + break; + default: + if (shouldSelect) { + scope.$apply(function() { + if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) { + $$debounce(function() { + scope.select(scope.activeIdx, evt); + }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']); + } else { + scope.select(scope.activeIdx, evt); + } + }); + } + } + }); + + element.on('focus', function (evt) { + hasFocus = true; + if (minLength === 0 && !modelCtrl.$viewValue) { + $timeout(function() { + getMatchesAsync(modelCtrl.$viewValue, evt); + }, 0); + } + }); + + element.on('blur', function(evt) { + if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) { + selected = true; + scope.$apply(function() { + if (angular.isObject(scope.debounceUpdate) && angular.isNumber(scope.debounceUpdate.blur)) { + $$debounce(function() { + scope.select(scope.activeIdx, evt); + }, scope.debounceUpdate.blur); + } else { + scope.select(scope.activeIdx, evt); + } + }); + } + if (!isEditable && modelCtrl.$error.editable) { + modelCtrl.$setViewValue(); + scope.$apply(function() { + // Reset validity as we are clearing + modelCtrl.$setValidity('editable', true); + modelCtrl.$setValidity('parse', true); + }); + element.val(''); + } + hasFocus = false; + selected = false; + }); + + // Keep reference to click handler to unbind it. + var dismissClickHandler = function(evt) { + // Issue #3973 + // Firefox treats right click as a click on document + if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) { + resetMatches(); + if (!$rootScope.$$phase) { + originalScope.$digest(); + } + } + }; + + $document.on('click', dismissClickHandler); + + originalScope.$on('$destroy', function() { + $document.off('click', dismissClickHandler); + if (appendToBody || appendTo) { + $popup.remove(); + } + + if (appendToBody) { + angular.element($window).off('resize', fireRecalculating); + $document.find('body').off('scroll', fireRecalculating); + } + // Prevent jQuery cache memory leak + popUpEl.remove(); + + if (showHint) { + inputsContainer.remove(); + } + }); + + var $popup = $compile(popUpEl)(scope); + + if (appendToBody) { + $document.find('body').append($popup); + } else if (appendTo) { + angular.element(appendTo).eq(0).append($popup); + } else { + element.after($popup); + } + + this.init = function(_modelCtrl, _ngModelOptions) { + modelCtrl = _modelCtrl; + ngModelOptions = _ngModelOptions; + + scope.debounceUpdate = modelCtrl.$options && $parse(modelCtrl.$options.debounce)(originalScope); + + //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM + //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue + modelCtrl.$parsers.unshift(function(inputValue) { + hasFocus = true; + + if (minLength === 0 || inputValue && inputValue.length >= minLength) { + if (waitTime > 0) { + cancelPreviousTimeout(); + scheduleSearchWithTimeout(inputValue); + } else { + getMatchesAsync(inputValue); + } + } else { + isLoadingSetter(originalScope, false); + cancelPreviousTimeout(); + resetMatches(); + } + + if (isEditable) { + return inputValue; + } + + if (!inputValue) { + // Reset in case user had typed something previously. + modelCtrl.$setValidity('editable', true); + return null; + } + + modelCtrl.$setValidity('editable', false); + return undefined; + }); + + modelCtrl.$formatters.push(function(modelValue) { + var candidateViewValue, emptyViewValue; + var locals = {}; + + // The validity may be set to false via $parsers (see above) if + // the model is restricted to selected values. If the model + // is set manually it is considered to be valid. + if (!isEditable) { + modelCtrl.$setValidity('editable', true); + } + + if (inputFormatter) { + locals.$model = modelValue; + return inputFormatter(originalScope, locals); + } + + //it might happen that we don't have enough info to properly render input value + //we need to check for this situation and simply return model value if we can't apply custom formatting + locals[parserResult.itemName] = modelValue; + candidateViewValue = parserResult.viewMapper(originalScope, locals); + locals[parserResult.itemName] = undefined; + emptyViewValue = parserResult.viewMapper(originalScope, locals); + + return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue; + }); + }; + }]) + + .directive('uibTypeahead', function() { + return { + controller: 'UibTypeaheadController', + require: ['ngModel', '^?ngModelOptions', 'uibTypeahead'], + link: function(originalScope, element, attrs, ctrls) { + ctrls[2].init(ctrls[0], ctrls[1]); + } + }; + }) + + .directive('uibTypeaheadPopup', ['$$debounce', function($$debounce) { + return { + scope: { + matches: '=', + query: '=', + active: '=', + position: '&', + moveInProgress: '=', + select: '&', + assignIsOpen: '&', + debounce: '&' + }, + replace: true, + templateUrl: function(element, attrs) { + return attrs.popupTemplateUrl || 'uib/template/typeahead/typeahead-popup.html'; + }, + link: function(scope, element, attrs) { + scope.templateUrl = attrs.templateUrl; + + scope.isOpen = function() { + var isDropdownOpen = scope.matches.length > 0; + scope.assignIsOpen({ isOpen: isDropdownOpen }); + return isDropdownOpen; + }; + + scope.isActive = function(matchIdx) { + return scope.active === matchIdx; + }; + + scope.selectActive = function(matchIdx) { + scope.active = matchIdx; + }; + + scope.selectMatch = function(activeIdx, evt) { + var debounce = scope.debounce(); + if (angular.isNumber(debounce) || angular.isObject(debounce)) { + $$debounce(function() { + scope.select({activeIdx: activeIdx, evt: evt}); + }, angular.isNumber(debounce) ? debounce : debounce['default']); + } else { + scope.select({activeIdx: activeIdx, evt: evt}); + } + }; + } + }; + }]) + + .directive('uibTypeaheadMatch', ['$templateRequest', '$compile', '$parse', function($templateRequest, $compile, $parse) { + return { + scope: { + index: '=', + match: '=', + query: '=' + }, + link: function(scope, element, attrs) { + var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'uib/template/typeahead/typeahead-match.html'; + $templateRequest(tplUrl).then(function(tplContent) { + var tplEl = angular.element(tplContent.trim()); + element.replaceWith(tplEl); + $compile(tplEl)(scope); + }); + } + }; + }]) + + .filter('uibTypeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) { + var isSanitizePresent; + isSanitizePresent = $injector.has('$sanitize'); + + function escapeRegexp(queryToEscape) { + // Regex: capture the whole query string and replace it with the string that will be used to match + // the results, for example if the capture is "a" the result will be \a + return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1'); + } + + function containsHtml(matchItem) { + return /<.*>/g.test(matchItem); + } + + return function(matchItem, query) { + if (!isSanitizePresent && containsHtml(matchItem)) { + $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger + } + matchItem = query ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '$&') : matchItem; // Replaces the capture string with a the same string inside of a "strong" tag + if (!isSanitizePresent) { + matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive + } + return matchItem; + }; + }]); +angular.module('ui.bootstrap.carousel').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibCarouselCss && angular.element(document).find('head').prepend(''); angular.$$uibCarouselCss = true; }); +angular.module('ui.bootstrap.datepicker').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibDatepickerCss && angular.element(document).find('head').prepend(''); angular.$$uibDatepickerCss = true; }); +angular.module('ui.bootstrap.position').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibPositionCss && angular.element(document).find('head').prepend(''); angular.$$uibPositionCss = true; }); +angular.module('ui.bootstrap.datepickerPopup').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibDatepickerpopupCss && angular.element(document).find('head').prepend(''); angular.$$uibDatepickerpopupCss = true; }); +angular.module('ui.bootstrap.tooltip').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTooltipCss && angular.element(document).find('head').prepend(''); angular.$$uibTooltipCss = true; }); +angular.module('ui.bootstrap.timepicker').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTimepickerCss && angular.element(document).find('head').prepend(''); angular.$$uibTimepickerCss = true; }); +angular.module('ui.bootstrap.typeahead').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTypeaheadCss && angular.element(document).find('head').prepend(''); angular.$$uibTypeaheadCss = true; }); \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap.min.js b/ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap.min.js new file mode 100644 index 00000000..e5511f3c --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-bootstrap/ui-bootstrap.min.js @@ -0,0 +1,10 @@ +/* + * angular-ui-bootstrap + * http://angular-ui.github.io/bootstrap/ + + * Version: 2.4.0 - 2016-12-29 + * License: MIT + */angular.module("ui.bootstrap",["ui.bootstrap.collapse","ui.bootstrap.tabindex","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.datepicker","ui.bootstrap.position","ui.bootstrap.datepickerPopup","ui.bootstrap.debounce","ui.bootstrap.multiMap","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]),angular.module("ui.bootstrap.collapse",[]).directive("uibCollapse",["$animate","$q","$parse","$injector",function(a,b,c,d){var e=d.has("$animateCss")?d.get("$animateCss"):null;return{link:function(d,f,g){function h(){r=!!("horizontal"in g),r?(s={width:""},t={width:"0"}):(s={height:""},t={height:"0"}),d.$eval(g.uibCollapse)||f.addClass("in").addClass("collapse").attr("aria-expanded",!0).attr("aria-hidden",!1).css(s)}function i(a){return r?{width:a.scrollWidth+"px"}:{height:a.scrollHeight+"px"}}function j(){f.hasClass("collapse")&&f.hasClass("in")||b.resolve(n(d)).then(function(){f.removeClass("collapse").addClass("collapsing").attr("aria-expanded",!0).attr("aria-hidden",!1),e?e(f,{addClass:"in",easing:"ease",css:{overflow:"hidden"},to:i(f[0])}).start()["finally"](k):a.addClass(f,"in",{css:{overflow:"hidden"},to:i(f[0])}).then(k)})}function k(){f.removeClass("collapsing").addClass("collapse").css(s),o(d)}function l(){return f.hasClass("collapse")||f.hasClass("in")?void b.resolve(p(d)).then(function(){f.css(i(f[0])).removeClass("collapse").addClass("collapsing").attr("aria-expanded",!1).attr("aria-hidden",!0),e?e(f,{removeClass:"in",to:t}).start()["finally"](m):a.removeClass(f,"in",{to:t}).then(m)}):m()}function m(){f.css(t),f.removeClass("collapsing").addClass("collapse"),q(d)}var n=c(g.expanding),o=c(g.expanded),p=c(g.collapsing),q=c(g.collapsed),r=!1,s={},t={};h(),d.$watch(g.uibCollapse,function(a){a?l():j()})}}}]),angular.module("ui.bootstrap.tabindex",[]).directive("uibTabindexToggle",function(){return{restrict:"A",link:function(a,b,c){c.$observe("disabled",function(a){c.$set("tabindex",a?-1:null)})}}}),angular.module("ui.bootstrap.accordion",["ui.bootstrap.collapse","ui.bootstrap.tabindex"]).constant("uibAccordionConfig",{closeOthers:!0}).controller("UibAccordionController",["$scope","$attrs","uibAccordionConfig",function(a,b,c){this.groups=[],this.closeOthers=function(d){var e=angular.isDefined(b.closeOthers)?a.$eval(b.closeOthers):c.closeOthers;e&&angular.forEach(this.groups,function(a){a!==d&&(a.isOpen=!1)})},this.addGroup=function(a){var b=this;this.groups.push(a),a.$on("$destroy",function(c){b.removeGroup(a)})},this.removeGroup=function(a){var b=this.groups.indexOf(a);-1!==b&&this.groups.splice(b,1)}}]).directive("uibAccordion",function(){return{controller:"UibAccordionController",controllerAs:"accordion",transclude:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/accordion/accordion.html"}}}).directive("uibAccordionGroup",function(){return{require:"^uibAccordion",transclude:!0,restrict:"A",templateUrl:function(a,b){return b.templateUrl||"uib/template/accordion/accordion-group.html"},scope:{heading:"@",panelClass:"@?",isOpen:"=?",isDisabled:"=?"},controller:function(){this.setHeading=function(a){this.heading=a}},link:function(a,b,c,d){b.addClass("panel"),d.addGroup(a),a.openClass=c.openClass||"panel-open",a.panelClass=c.panelClass||"panel-default",a.$watch("isOpen",function(c){b.toggleClass(a.openClass,!!c),c&&d.closeOthers(a)}),a.toggleOpen=function(b){a.isDisabled||b&&32!==b.which||(a.isOpen=!a.isOpen)};var e="accordiongroup-"+a.$id+"-"+Math.floor(1e4*Math.random());a.headingId=e+"-tab",a.panelId=e+"-panel"}}}).directive("uibAccordionHeading",function(){return{transclude:!0,template:"",replace:!0,require:"^uibAccordionGroup",link:function(a,b,c,d,e){d.setHeading(e(a,angular.noop))}}}).directive("uibAccordionTransclude",function(){function a(){return"uib-accordion-header,data-uib-accordion-header,x-uib-accordion-header,uib\\:accordion-header,[uib-accordion-header],[data-uib-accordion-header],[x-uib-accordion-header]"}return{require:"^uibAccordionGroup",link:function(b,c,d,e){b.$watch(function(){return e[d.uibAccordionTransclude]},function(b){if(b){var d=angular.element(c[0].querySelector(a()));d.html(""),d.append(b)}})}}}),angular.module("ui.bootstrap.alert",[]).controller("UibAlertController",["$scope","$element","$attrs","$interpolate","$timeout",function(a,b,c,d,e){a.closeable=!!c.close,b.addClass("alert"),c.$set("role","alert"),a.closeable&&b.addClass("alert-dismissible");var f=angular.isDefined(c.dismissOnTimeout)?d(c.dismissOnTimeout)(a.$parent):null;f&&e(function(){a.close()},parseInt(f,10))}]).directive("uibAlert",function(){return{controller:"UibAlertController",controllerAs:"alert",restrict:"A",templateUrl:function(a,b){return b.templateUrl||"uib/template/alert/alert.html"},transclude:!0,scope:{close:"&"}}}),angular.module("ui.bootstrap.buttons",[]).constant("uibButtonConfig",{activeClass:"active",toggleEvent:"click"}).controller("UibButtonsController",["uibButtonConfig",function(a){this.activeClass=a.activeClass||"active",this.toggleEvent=a.toggleEvent||"click"}]).directive("uibBtnRadio",["$parse",function(a){return{require:["uibBtnRadio","ngModel"],controller:"UibButtonsController",controllerAs:"buttons",link:function(b,c,d,e){var f=e[0],g=e[1],h=a(d.uibUncheckable);c.find("input").css({display:"none"}),g.$render=function(){c.toggleClass(f.activeClass,angular.equals(g.$modelValue,b.$eval(d.uibBtnRadio)))},c.on(f.toggleEvent,function(){if(!d.disabled){var a=c.hasClass(f.activeClass);a&&!angular.isDefined(d.uncheckable)||b.$apply(function(){g.$setViewValue(a?null:b.$eval(d.uibBtnRadio)),g.$render()})}}),d.uibUncheckable&&b.$watch(h,function(a){d.$set("uncheckable",a?"":void 0)})}}}]).directive("uibBtnCheckbox",function(){return{require:["uibBtnCheckbox","ngModel"],controller:"UibButtonsController",controllerAs:"button",link:function(a,b,c,d){function e(){return g(c.btnCheckboxTrue,!0)}function f(){return g(c.btnCheckboxFalse,!1)}function g(b,c){return angular.isDefined(b)?a.$eval(b):c}var h=d[0],i=d[1];b.find("input").css({display:"none"}),i.$render=function(){b.toggleClass(h.activeClass,angular.equals(i.$modelValue,e()))},b.on(h.toggleEvent,function(){c.disabled||a.$apply(function(){i.$setViewValue(b.hasClass(h.activeClass)?f():e()),i.$render()})})}}}),angular.module("ui.bootstrap.carousel",[]).controller("UibCarouselController",["$scope","$element","$interval","$timeout","$animate",function(a,b,c,d,e){function f(){for(;t.length;)t.shift()}function g(a){for(var b=0;b1){q[d].element.data(r,c.direction);var j=p.getCurrentIndex();angular.isNumber(j)&&q[j].element&&q[j].element.data(r,c.direction),a.$currentTransition=!0,e.on("addClass",q[d].element,function(b,c){if("close"===c&&(a.$currentTransition=null,e.off("addClass",b),t.length)){var d=t.pop().slide,g=d.index,i=g>p.getCurrentIndex()?"next":"prev";f(),h(d,g,i)}})}a.active=c.index,s=c.index,g(d),l()}}function i(a){for(var b=0;b0&&(n=c(m,b))}function m(){var b=+a.interval;o&&!isNaN(b)&&b>0&&q.length?a.next():a.pause()}var n,o,p=this,q=p.slides=a.slides=[],r="uib-slideDirection",s=a.active,t=[],u=!1;b.addClass("carousel"),p.addSlide=function(b,c){q.push({slide:b,element:c}),q.sort(function(a,b){return+a.slide.index-+b.slide.index}),(b.index===a.active||1===q.length&&!angular.isNumber(a.active))&&(a.$currentTransition&&(a.$currentTransition=null),s=b.index,a.active=b.index,g(s),p.select(q[i(b)]),1===q.length&&a.play())},p.getCurrentIndex=function(){for(var a=0;a0&&s===c?c>=q.length?(s=q.length-1,a.active=s,g(s),p.select(q[q.length-1])):(s=c,a.active=s,g(s),p.select(q[c])):s>c&&(s--,a.active=s),0===q.length&&(s=null,a.active=null,f())},p.select=a.select=function(b,c){var d=i(b.slide);void 0===c&&(c=d>p.getCurrentIndex()?"next":"prev"),b.slide.index===s||a.$currentTransition?b&&b.slide.index!==s&&a.$currentTransition&&t.push(q[d]):h(b.slide,d,c)},a.indexOfSlide=function(a){return+a.slide.index},a.isActive=function(b){return a.active===b.slide.index},a.isPrevDisabled=function(){return 0===a.active&&a.noWrap()},a.isNextDisabled=function(){return a.active===q.length-1&&a.noWrap()},a.pause=function(){a.noPause||(o=!1,j())},a.play=function(){o||(o=!0,l())},b.on("mouseenter",a.pause),b.on("mouseleave",a.play),a.$on("$destroy",function(){u=!0,j()}),a.$watch("noTransition",function(a){e.enabled(b,!a)}),a.$watch("interval",l),a.$watchCollection("slides",k),a.$watch("active",function(a){if(angular.isNumber(a)&&s!==a){for(var b=0;b-1){var f=!1;a=a.split("");for(var g=e;g-1){a=a.split(""),c[e]="("+d.regex+")",a[e]="$";for(var f=e+1,g=e+d.key.length;g>f;f++)c[f]="",a[f]="$";a=a.join(""),b.push({index:e,key:d.key,apply:d.apply,matcher:d.regex})}}),{regex:new RegExp("^"+c.join("")+"$"),map:d(b,"index")}}function h(a){for(var b,c,d=[],e=0;e=a.length||"'"!==a.charAt(e+1))&&(d.push(i(a,c,e)),c=null);else if(e===a.length)for(;cc?!1:1===b&&c>28?29===c&&(a%4===0&&a%100!==0||a%400===0):3===b||5===b||8===b||10===b?31>c:!0}function l(a){return parseInt(a,10)}function m(a,b){return a&&b?q(a,b):a}function n(a,b){return a&&b?q(a,b,!0):a}function o(a,b){a=a.replace(/:/g,"");var c=Date.parse("Jan 01, 1970 00:00:00 "+a)/6e4;return isNaN(c)?b:c}function p(a,b){return a=new Date(a.getTime()),a.setMinutes(a.getMinutes()+b),a}function q(a,b,c){c=c?-1:1;var d=a.getTimezoneOffset(),e=o(b,d);return p(a,c*(e-d))}var r,s,t=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;this.init=function(){r=b.id,this.parsers={},this.formatters={},s=[{key:"yyyy",regex:"\\d{4}",apply:function(a){this.year=+a},formatter:function(a){var b=new Date;return b.setFullYear(Math.abs(a.getFullYear())),c(b,"yyyy")}},{key:"yy",regex:"\\d{2}",apply:function(a){a=+a,this.year=69>a?a+2e3:a+1900},formatter:function(a){var b=new Date;return b.setFullYear(Math.abs(a.getFullYear())),c(b,"yy")}},{key:"y",regex:"\\d{1,4}",apply:function(a){this.year=+a},formatter:function(a){var b=new Date;return b.setFullYear(Math.abs(a.getFullYear())),c(b,"y")}},{key:"M!",regex:"0?[1-9]|1[0-2]",apply:function(a){this.month=a-1},formatter:function(a){var b=a.getMonth();return/^[0-9]$/.test(b)?c(a,"MM"):c(a,"M")}},{key:"MMMM",regex:b.DATETIME_FORMATS.MONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.MONTH.indexOf(a)},formatter:function(a){return c(a,"MMMM")}},{key:"MMM",regex:b.DATETIME_FORMATS.SHORTMONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.SHORTMONTH.indexOf(a)},formatter:function(a){return c(a,"MMM")}},{key:"MM",regex:"0[1-9]|1[0-2]",apply:function(a){this.month=a-1},formatter:function(a){return c(a,"MM")}},{key:"M",regex:"[1-9]|1[0-2]",apply:function(a){this.month=a-1},formatter:function(a){return c(a,"M")}},{key:"d!",regex:"[0-2]?[0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a},formatter:function(a){var b=a.getDate();return/^[1-9]$/.test(b)?c(a,"dd"):c(a,"d")}},{key:"dd",regex:"[0-2][0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a},formatter:function(a){return c(a,"dd")}},{key:"d",regex:"[1-2]?[0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a},formatter:function(a){return c(a,"d")}},{key:"EEEE",regex:b.DATETIME_FORMATS.DAY.join("|"),formatter:function(a){return c(a,"EEEE")}},{key:"EEE",regex:b.DATETIME_FORMATS.SHORTDAY.join("|"),formatter:function(a){return c(a,"EEE")}},{key:"HH",regex:"(?:0|1)[0-9]|2[0-3]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"HH")}},{key:"hh",regex:"0[0-9]|1[0-2]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"hh")}},{key:"H",regex:"1?[0-9]|2[0-3]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"H")}},{key:"h",regex:"[0-9]|1[0-2]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"h")}},{key:"mm",regex:"[0-5][0-9]",apply:function(a){this.minutes=+a},formatter:function(a){return c(a,"mm")}},{key:"m",regex:"[0-9]|[1-5][0-9]",apply:function(a){this.minutes=+a},formatter:function(a){return c(a,"m")}},{key:"sss",regex:"[0-9][0-9][0-9]",apply:function(a){this.milliseconds=+a},formatter:function(a){return c(a,"sss")}},{key:"ss",regex:"[0-5][0-9]",apply:function(a){this.seconds=+a},formatter:function(a){return c(a,"ss")}},{key:"s",regex:"[0-9]|[1-5][0-9]",apply:function(a){this.seconds=+a},formatter:function(a){return c(a,"s")}},{key:"a",regex:b.DATETIME_FORMATS.AMPMS.join("|"),apply:function(a){12===this.hours&&(this.hours=0),"PM"===a&&(this.hours+=12)},formatter:function(a){return c(a,"a")}},{key:"Z",regex:"[+-]\\d{4}",apply:function(a){var b=a.match(/([+-])(\d{2})(\d{2})/),c=b[1],d=b[2],e=b[3];this.hours+=l(c+d),this.minutes+=l(c+e)},formatter:function(a){return c(a,"Z")}},{key:"ww",regex:"[0-4][0-9]|5[0-3]",formatter:function(a){return c(a,"ww")}},{key:"w",regex:"[0-9]|[1-4][0-9]|5[0-3]",formatter:function(a){return c(a,"w")}},{key:"GGGG",regex:b.DATETIME_FORMATS.ERANAMES.join("|").replace(/\s/g,"\\s"),formatter:function(a){return c(a,"GGGG")}},{key:"GGG",regex:b.DATETIME_FORMATS.ERAS.join("|"),formatter:function(a){return c(a,"GGG")}},{key:"GG",regex:b.DATETIME_FORMATS.ERAS.join("|"),formatter:function(a){return c(a,"GG")}},{key:"G",regex:b.DATETIME_FORMATS.ERAS.join("|"),formatter:function(a){return c(a,"G")}}],angular.version.major>=1&&angular.version.minor>4&&s.push({key:"LLLL",regex:b.DATETIME_FORMATS.STANDALONEMONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.STANDALONEMONTH.indexOf(a)},formatter:function(a){return c(a,"LLLL")}})},this.init(),this.getParser=function(a){var b=f(a);return b&&b.apply||null},this.overrideParser=function(a,b){var c=f(a);c&&angular.isFunction(b)&&(this.parsers={},c.apply=b)}.bind(this),this.filter=function(a,c){if(!angular.isDate(a)||isNaN(a)||!c)return"";c=b.DATETIME_FORMATS[c]||c,b.id!==r&&this.init(),this.formatters[c]||(this.formatters[c]=h(c));var d=this.formatters[c];return d.reduce(function(b,c){return b+c(a)},"")},this.parse=function(c,d,e){if(!angular.isString(c)||!d)return c;d=b.DATETIME_FORMATS[d]||d,d=d.replace(t,"\\$&"),b.id!==r&&this.init(),this.parsers[d]||(this.parsers[d]=g(d,"apply"));var f=this.parsers[d],h=f.regex,i=f.map,j=c.match(h),l=!1;if(j&&j.length){var m,n;angular.isDate(e)&&!isNaN(e.getTime())?m={year:e.getFullYear(),month:e.getMonth(),date:e.getDate(),hours:e.getHours(),minutes:e.getMinutes(),seconds:e.getSeconds(),milliseconds:e.getMilliseconds()}:(e&&a.warn("dateparser:","baseDate is not a valid date"),m={year:1900,month:0,date:1,hours:0,minutes:0,seconds:0,milliseconds:0});for(var o=1,p=j.length;p>o;o++){var q=i[o-1];"Z"===q.matcher&&(l=!0),q.apply&&q.apply.call(m,j[o])}var s=l?Date.prototype.setUTCFullYear:Date.prototype.setFullYear,u=l?Date.prototype.setUTCHours:Date.prototype.setHours;return k(m.year,m.month,m.date)&&(!angular.isDate(e)||isNaN(e.getTime())||l?(n=new Date(0),s.call(n,m.year,m.month,m.date),u.call(n,m.hours||0,m.minutes||0,m.seconds||0,m.milliseconds||0)):(n=new Date(e),s.call(n,m.year,m.month,m.date),u.call(n,m.hours,m.minutes,m.seconds,m.milliseconds))),n}},this.toTimezone=m,this.fromTimezone=n,this.timezoneToOffset=o,this.addDateMinutes=p,this.convertTimezoneToLocal=q}]),angular.module("ui.bootstrap.isClass",[]).directive("uibIsClass",["$animate",function(a){var b=/^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/,c=/^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/;return{restrict:"A",compile:function(d,e){function f(a,b,c){i.push(a),j.push({scope:a,element:b}),o.forEach(function(b,c){g(b,a)}),a.$on("$destroy",h)}function g(b,d){var e=b.match(c),f=d.$eval(e[1]),g=e[2],h=k[b];if(!h){var i=function(b){var c=null;j.some(function(a){var d=a.scope.$eval(m);return d===b?(c=a,!0):void 0}),h.lastActivated!==c&&(h.lastActivated&&a.removeClass(h.lastActivated.element,f),c&&a.addClass(c.element,f),h.lastActivated=c)};k[b]=h={lastActivated:null,scope:d,watchFn:i,compareWithExp:g,watcher:d.$watch(g,i)}}h.watchFn(d.$eval(g))}function h(a){var b=a.targetScope,c=i.indexOf(b);if(i.splice(c,1),j.splice(c,1),i.length){var d=i[0];angular.forEach(k,function(a){a.scope===b&&(a.watcher=d.$watch(a.compareWithExp,a.watchFn),a.scope=d)})}else k={}}var i=[],j=[],k={},l=e.uibIsClass.match(b),m=l[2],n=l[1],o=n.split(",");return f}}}]),angular.module("ui.bootstrap.datepicker",["ui.bootstrap.dateparser","ui.bootstrap.isClass"]).value("$datepickerSuppressError",!1).value("$datepickerLiteralWarning",!0).constant("uibDatepickerConfig",{datepickerMode:"day",formatDay:"dd",formatMonth:"MMMM",formatYear:"yyyy",formatDayHeader:"EEE",formatDayTitle:"MMMM yyyy",formatMonthTitle:"yyyy",maxDate:null,maxMode:"year",minDate:null,minMode:"day",monthColumns:3,ngModelOptions:{},shortcutPropagation:!1,showWeeks:!0,yearColumns:5,yearRows:4}).controller("UibDatepickerController",["$scope","$element","$attrs","$parse","$interpolate","$locale","$log","dateFilter","uibDatepickerConfig","$datepickerLiteralWarning","$datepickerSuppressError","uibDateParser",function(a,b,c,d,e,f,g,h,i,j,k,l){function m(b){a.datepickerMode=b,a.datepickerOptions.datepickerMode=b}var n=this,o={$setViewValue:angular.noop},p={},q=[];b.addClass("uib-datepicker"),c.$set("role","application"),a.datepickerOptions||(a.datepickerOptions={}),this.modes=["day","month","year"],["customClass","dateDisabled","datepickerMode","formatDay","formatDayHeader","formatDayTitle","formatMonth","formatMonthTitle","formatYear","maxDate","maxMode","minDate","minMode","monthColumns","showWeeks","shortcutPropagation","startingDay","yearColumns","yearRows"].forEach(function(b){switch(b){case"customClass":case"dateDisabled":a[b]=a.datepickerOptions[b]||angular.noop;break;case"datepickerMode":a.datepickerMode=angular.isDefined(a.datepickerOptions.datepickerMode)?a.datepickerOptions.datepickerMode:i.datepickerMode;break;case"formatDay":case"formatDayHeader":case"formatDayTitle":case"formatMonth":case"formatMonthTitle":case"formatYear":n[b]=angular.isDefined(a.datepickerOptions[b])?e(a.datepickerOptions[b])(a.$parent):i[b];break;case"monthColumns":case"showWeeks":case"shortcutPropagation":case"yearColumns":case"yearRows":n[b]=angular.isDefined(a.datepickerOptions[b])?a.datepickerOptions[b]:i[b];break;case"startingDay":angular.isDefined(a.datepickerOptions.startingDay)?n.startingDay=a.datepickerOptions.startingDay:angular.isNumber(i.startingDay)?n.startingDay=i.startingDay:n.startingDay=(f.DATETIME_FORMATS.FIRSTDAYOFWEEK+8)%7;break;case"maxDate":case"minDate":a.$watch("datepickerOptions."+b,function(a){a?angular.isDate(a)?n[b]=l.fromTimezone(new Date(a),p.timezone):(j&&g.warn("Literal date support has been deprecated, please switch to date object usage"),n[b]=new Date(h(a,"medium"))):n[b]=i[b]?l.fromTimezone(new Date(i[b]),p.timezone):null,n.refreshView()});break;case"maxMode":case"minMode":a.datepickerOptions[b]?a.$watch(function(){return a.datepickerOptions[b]},function(c){n[b]=a[b]=angular.isDefined(c)?c:a.datepickerOptions[b],("minMode"===b&&n.modes.indexOf(a.datepickerOptions.datepickerMode)n.modes.indexOf(n[b]))&&(a.datepickerMode=n[b],a.datepickerOptions.datepickerMode=n[b])}):n[b]=a[b]=i[b]||null}}),a.uniqueId="datepicker-"+a.$id+"-"+Math.floor(1e4*Math.random()),a.disabled=angular.isDefined(c.disabled)||!1,angular.isDefined(c.ngDisabled)&&q.push(a.$parent.$watch(c.ngDisabled,function(b){a.disabled=b,n.refreshView()})),a.isActive=function(b){return 0===n.compare(b.date,n.activeDate)?(a.activeDateId=b.uid,!0):!1},this.init=function(b){o=b,p=b.$options||a.datepickerOptions.ngModelOptions||i.ngModelOptions,a.datepickerOptions.initDate?(n.activeDate=l.fromTimezone(a.datepickerOptions.initDate,p.timezone)||new Date,a.$watch("datepickerOptions.initDate",function(a){a&&(o.$isEmpty(o.$modelValue)||o.$invalid)&&(n.activeDate=l.fromTimezone(a,p.timezone),n.refreshView())})):n.activeDate=new Date;var c=o.$modelValue?new Date(o.$modelValue):new Date;this.activeDate=isNaN(c)?l.fromTimezone(new Date,p.timezone):l.fromTimezone(c,p.timezone),o.$render=function(){n.render()}},this.render=function(){if(o.$viewValue){var a=new Date(o.$viewValue),b=!isNaN(a);b?this.activeDate=l.fromTimezone(a,p.timezone):k||g.error('Datepicker directive: "ng-model" value must be a Date object')}this.refreshView()},this.refreshView=function(){if(this.element){a.selectedDt=null,this._refreshView(),a.activeDt&&(a.activeDateId=a.activeDt.uid);var b=o.$viewValue?new Date(o.$viewValue):null;b=l.fromTimezone(b,p.timezone),o.$setValidity("dateDisabled",!b||this.element&&!this.isDisabled(b))}},this.createDateObject=function(b,c){var d=o.$viewValue?new Date(o.$viewValue):null;d=l.fromTimezone(d,p.timezone);var e=new Date;e=l.fromTimezone(e,p.timezone);var f=this.compare(b,e),g={date:b,label:l.filter(b,c),selected:d&&0===this.compare(b,d),disabled:this.isDisabled(b),past:0>f,current:0===f,future:f>0,customClass:this.customClass(b)||null};return d&&0===this.compare(b,d)&&(a.selectedDt=g),n.activeDate&&0===this.compare(g.date,n.activeDate)&&(a.activeDt=g),g},this.isDisabled=function(b){return a.disabled||this.minDate&&this.compare(b,this.minDate)<0||this.maxDate&&this.compare(b,this.maxDate)>0||a.dateDisabled&&a.dateDisabled({date:b,mode:a.datepickerMode})},this.customClass=function(b){return a.customClass({date:b,mode:a.datepickerMode})},this.split=function(a,b){for(var c=[];a.length>0;)c.push(a.splice(0,b));return c},a.select=function(b){if(a.datepickerMode===n.minMode){var c=o.$viewValue?l.fromTimezone(new Date(o.$viewValue),p.timezone):new Date(0,0,0,0,0,0,0);c.setFullYear(b.getFullYear(),b.getMonth(),b.getDate()),c=l.toTimezone(c,p.timezone),o.$setViewValue(c),o.$render()}else n.activeDate=b,m(n.modes[n.modes.indexOf(a.datepickerMode)-1]),a.$emit("uib:datepicker.mode");a.$broadcast("uib:datepicker.focus")},a.move=function(a){var b=n.activeDate.getFullYear()+a*(n.step.years||0),c=n.activeDate.getMonth()+a*(n.step.months||0);n.activeDate.setFullYear(b,c,1),n.refreshView()},a.toggleMode=function(b){b=b||1,a.datepickerMode===n.maxMode&&1===b||a.datepickerMode===n.minMode&&-1===b||(m(n.modes[n.modes.indexOf(a.datepickerMode)+b]),a.$emit("uib:datepicker.mode"))},a.keys={13:"enter",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down"};var r=function(){n.element[0].focus()};a.$on("uib:datepicker.focus",r),a.keydown=function(b){var c=a.keys[b.which];if(c&&!b.shiftKey&&!b.altKey&&!a.disabled)if(b.preventDefault(),n.shortcutPropagation||b.stopPropagation(),"enter"===c||"space"===c){if(n.isDisabled(n.activeDate))return;a.select(n.activeDate)}else!b.ctrlKey||"up"!==c&&"down"!==c?(n.handleKeyDown(c,b),n.refreshView()):a.toggleMode("up"===c?1:-1)},b.on("keydown",function(b){a.$apply(function(){a.keydown(b)})}),a.$on("$destroy",function(){for(;q.length;)q.shift()()})}]).controller("UibDaypickerController",["$scope","$element","dateFilter",function(a,b,c){function d(a,b){return 1!==b||a%4!==0||a%100===0&&a%400!==0?f[b]:29}function e(a){var b=new Date(a);b.setDate(b.getDate()+4-(b.getDay()||7));var c=b.getTime();return b.setMonth(0),b.setDate(1),Math.floor(Math.round((c-b)/864e5)/7)+1}var f=[31,28,31,30,31,30,31,31,30,31,30,31];this.step={months:1},this.element=b,this.init=function(b){angular.extend(b,this),a.showWeeks=b.showWeeks,b.refreshView()},this.getDates=function(a,b){for(var c,d=new Array(b),e=new Date(a),f=0;b>f;)c=new Date(e),d[f++]=c,e.setDate(e.getDate()+1);return d},this._refreshView=function(){var b=this.activeDate.getFullYear(),d=this.activeDate.getMonth(),f=new Date(this.activeDate);f.setFullYear(b,d,1);var g=this.startingDay-f.getDay(),h=g>0?7-g:-g,i=new Date(f);h>0&&i.setDate(-h+1);for(var j=this.getDates(i,42),k=0;42>k;k++)j[k]=angular.extend(this.createDateObject(j[k],this.formatDay),{secondary:j[k].getMonth()!==d,uid:a.uniqueId+"-"+k});a.labels=new Array(7);for(var l=0;7>l;l++)a.labels[l]={abbr:c(j[l].date,this.formatDayHeader),full:c(j[l].date,"EEEE")};if(a.title=c(this.activeDate,this.formatDayTitle),a.rows=this.split(j,7),a.showWeeks){a.weekNumbers=[];for(var m=(11-this.startingDay)%7,n=a.rows.length,o=0;n>o;o++)a.weekNumbers.push(e(a.rows[o][m].date))}},this.compare=function(a,b){var c=new Date(a.getFullYear(),a.getMonth(),a.getDate()),d=new Date(b.getFullYear(),b.getMonth(),b.getDate());return c.setFullYear(a.getFullYear()),d.setFullYear(b.getFullYear()),c-d},this.handleKeyDown=function(a,b){var c=this.activeDate.getDate();if("left"===a)c-=1;else if("up"===a)c-=7;else if("right"===a)c+=1;else if("down"===a)c+=7;else if("pageup"===a||"pagedown"===a){var e=this.activeDate.getMonth()+("pageup"===a?-1:1);this.activeDate.setMonth(e,1),c=Math.min(d(this.activeDate.getFullYear(),this.activeDate.getMonth()),c)}else"home"===a?c=1:"end"===a&&(c=d(this.activeDate.getFullYear(),this.activeDate.getMonth()));this.activeDate.setDate(c)}}]).controller("UibMonthpickerController",["$scope","$element","dateFilter",function(a,b,c){this.step={years:1},this.element=b,this.init=function(a){angular.extend(a,this),a.refreshView()},this._refreshView=function(){for(var b,d=new Array(12),e=this.activeDate.getFullYear(),f=0;12>f;f++)b=new Date(this.activeDate),b.setFullYear(e,f,1),d[f]=angular.extend(this.createDateObject(b,this.formatMonth),{uid:a.uniqueId+"-"+f});a.title=c(this.activeDate,this.formatMonthTitle),a.rows=this.split(d,this.monthColumns),a.yearHeaderColspan=this.monthColumns>3?this.monthColumns-2:1},this.compare=function(a,b){var c=new Date(a.getFullYear(),a.getMonth()),d=new Date(b.getFullYear(),b.getMonth());return c.setFullYear(a.getFullYear()),d.setFullYear(b.getFullYear()),c-d},this.handleKeyDown=function(a,b){var c=this.activeDate.getMonth();if("left"===a)c-=1;else if("up"===a)c-=this.monthColumns;else if("right"===a)c+=1;else if("down"===a)c+=this.monthColumns;else if("pageup"===a||"pagedown"===a){var d=this.activeDate.getFullYear()+("pageup"===a?-1:1);this.activeDate.setFullYear(d)}else"home"===a?c=0:"end"===a&&(c=11);this.activeDate.setMonth(c)}}]).controller("UibYearpickerController",["$scope","$element","dateFilter",function(a,b,c){function d(a){return parseInt((a-1)/f,10)*f+1}var e,f;this.element=b,this.yearpickerInit=function(){e=this.yearColumns,f=this.yearRows*e,this.step={years:f}},this._refreshView=function(){for(var b,c=new Array(f),g=0,h=d(this.activeDate.getFullYear());f>g;g++)b=new Date(this.activeDate),b.setFullYear(h+g,0,1),c[g]=angular.extend(this.createDateObject(b,this.formatYear),{uid:a.uniqueId+"-"+g});a.title=[c[0].label,c[f-1].label].join(" - "),a.rows=this.split(c,e),a.columns=e},this.compare=function(a,b){return a.getFullYear()-b.getFullYear()},this.handleKeyDown=function(a,b){var c=this.activeDate.getFullYear();"left"===a?c-=1:"up"===a?c-=e:"right"===a?c+=1:"down"===a?c+=e:"pageup"===a||"pagedown"===a?c+=("pageup"===a?-1:1)*f:"home"===a?c=d(this.activeDate.getFullYear()):"end"===a&&(c=d(this.activeDate.getFullYear())+f-1),this.activeDate.setFullYear(c)}}]).directive("uibDatepicker",function(){return{templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/datepicker.html"},scope:{datepickerOptions:"=?"},require:["uibDatepicker","^ngModel"],restrict:"A",controller:"UibDatepickerController",controllerAs:"datepicker",link:function(a,b,c,d){var e=d[0],f=d[1];e.init(f)}}}).directive("uibDaypicker",function(){return{templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/day.html"},require:["^uibDatepicker","uibDaypicker"],restrict:"A",controller:"UibDaypickerController",link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibMonthpicker",function(){return{templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/month.html"},require:["^uibDatepicker","uibMonthpicker"],restrict:"A",controller:"UibMonthpickerController",link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibYearpicker",function(){return{templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/year.html"},require:["^uibDatepicker","uibYearpicker"],restrict:"A",controller:"UibYearpickerController",link:function(a,b,c,d){var e=d[0];angular.extend(e,d[1]),e.yearpickerInit(),e.refreshView()}}}),angular.module("ui.bootstrap.position",[]).factory("$uibPosition",["$document","$window",function(a,b){var c,d,e={normal:/(auto|scroll)/,hidden:/(auto|scroll|hidden)/},f={auto:/\s?auto?\s?/i,primary:/^(top|bottom|left|right)$/,secondary:/^(top|bottom|left|right|center)$/,vertical:/^(top|bottom)$/},g=/(HTML|BODY)/;return{getRawNode:function(a){return a.nodeName?a:a[0]||a},parseStyle:function(a){return a=parseFloat(a),isFinite(a)?a:0},offsetParent:function(c){function d(a){return"static"===(b.getComputedStyle(a).position||"static")}c=this.getRawNode(c);for(var e=c.offsetParent||a[0].documentElement;e&&e!==a[0].documentElement&&d(e);)e=e.offsetParent;return e||a[0].documentElement},scrollbarWidth:function(e){if(e){if(angular.isUndefined(d)){var f=a.find("body");f.addClass("uib-position-body-scrollbar-measure"), +d=b.innerWidth-f[0].clientWidth,d=isFinite(d)?d:0,f.removeClass("uib-position-body-scrollbar-measure")}return d}if(angular.isUndefined(c)){var g=angular.element('
      ');a.find("body").append(g),c=g[0].offsetWidth-g[0].clientWidth,c=isFinite(c)?c:0,g.remove()}return c},scrollbarPadding:function(a){a=this.getRawNode(a);var c=b.getComputedStyle(a),d=this.parseStyle(c.paddingRight),e=this.parseStyle(c.paddingBottom),f=this.scrollParent(a,!1,!0),h=this.scrollbarWidth(g.test(f.tagName));return{scrollbarWidth:h,widthOverflow:f.scrollWidth>f.clientWidth,right:d+h,originalRight:d,heightOverflow:f.scrollHeight>f.clientHeight,bottom:e+h,originalBottom:e}},isScrollable:function(a,c){a=this.getRawNode(a);var d=c?e.hidden:e.normal,f=b.getComputedStyle(a);return d.test(f.overflow+f.overflowY+f.overflowX)},scrollParent:function(c,d,f){c=this.getRawNode(c);var g=d?e.hidden:e.normal,h=a[0].documentElement,i=b.getComputedStyle(c);if(f&&g.test(i.overflow+i.overflowY+i.overflowX))return c;var j="absolute"===i.position,k=c.parentElement||h;if(k===h||"fixed"===i.position)return h;for(;k.parentElement&&k!==h;){var l=b.getComputedStyle(k);if(j&&"static"!==l.position&&(j=!1),!j&&g.test(l.overflow+l.overflowY+l.overflowX))break;k=k.parentElement}return k},position:function(c,d){c=this.getRawNode(c);var e=this.offset(c);if(d){var f=b.getComputedStyle(c);e.top-=this.parseStyle(f.marginTop),e.left-=this.parseStyle(f.marginLeft)}var g=this.offsetParent(c),h={top:0,left:0};return g!==a[0].documentElement&&(h=this.offset(g),h.top+=g.clientTop-g.scrollTop,h.left+=g.clientLeft-g.scrollLeft),{width:Math.round(angular.isNumber(e.width)?e.width:c.offsetWidth),height:Math.round(angular.isNumber(e.height)?e.height:c.offsetHeight),top:Math.round(e.top-h.top),left:Math.round(e.left-h.left)}},offset:function(c){c=this.getRawNode(c);var d=c.getBoundingClientRect();return{width:Math.round(angular.isNumber(d.width)?d.width:c.offsetWidth),height:Math.round(angular.isNumber(d.height)?d.height:c.offsetHeight),top:Math.round(d.top+(b.pageYOffset||a[0].documentElement.scrollTop)),left:Math.round(d.left+(b.pageXOffset||a[0].documentElement.scrollLeft))}},viewportOffset:function(c,d,e){c=this.getRawNode(c),e=e!==!1;var f=c.getBoundingClientRect(),g={top:0,left:0,bottom:0,right:0},h=d?a[0].documentElement:this.scrollParent(c),i=h.getBoundingClientRect();if(g.top=i.top+h.clientTop,g.left=i.left+h.clientLeft,h===a[0].documentElement&&(g.top+=b.pageYOffset,g.left+=b.pageXOffset),g.bottom=g.top+h.clientHeight,g.right=g.left+h.clientWidth,e){var j=b.getComputedStyle(h);g.top+=this.parseStyle(j.paddingTop),g.bottom-=this.parseStyle(j.paddingBottom),g.left+=this.parseStyle(j.paddingLeft),g.right-=this.parseStyle(j.paddingRight)}return{top:Math.round(f.top-g.top),bottom:Math.round(g.bottom-f.bottom),left:Math.round(f.left-g.left),right:Math.round(g.right-f.right)}},parsePlacement:function(a){var b=f.auto.test(a);return b&&(a=a.replace(f.auto,"")),a=a.split("-"),a[0]=a[0]||"top",f.primary.test(a[0])||(a[0]="top"),a[1]=a[1]||"center",f.secondary.test(a[1])||(a[1]="center"),b?a[2]=!0:a[2]=!1,a},positionElements:function(a,c,d,e){a=this.getRawNode(a),c=this.getRawNode(c);var g=angular.isDefined(c.offsetWidth)?c.offsetWidth:c.prop("offsetWidth"),h=angular.isDefined(c.offsetHeight)?c.offsetHeight:c.prop("offsetHeight");d=this.parsePlacement(d);var i=e?this.offset(a):this.position(a),j={top:0,left:0,placement:""};if(d[2]){var k=this.viewportOffset(a,e),l=b.getComputedStyle(c),m={width:g+Math.round(Math.abs(this.parseStyle(l.marginLeft)+this.parseStyle(l.marginRight))),height:h+Math.round(Math.abs(this.parseStyle(l.marginTop)+this.parseStyle(l.marginBottom)))};if(d[0]="top"===d[0]&&m.height>k.top&&m.height<=k.bottom?"bottom":"bottom"===d[0]&&m.height>k.bottom&&m.height<=k.top?"top":"left"===d[0]&&m.width>k.left&&m.width<=k.right?"right":"right"===d[0]&&m.width>k.right&&m.width<=k.left?"left":d[0],d[1]="top"===d[1]&&m.height-i.height>k.bottom&&m.height-i.height<=k.top?"bottom":"bottom"===d[1]&&m.height-i.height>k.top&&m.height-i.height<=k.bottom?"top":"left"===d[1]&&m.width-i.width>k.right&&m.width-i.width<=k.left?"right":"right"===d[1]&&m.width-i.width>k.left&&m.width-i.width<=k.right?"left":d[1],"center"===d[1])if(f.vertical.test(d[0])){var n=i.width/2-g/2;k.left+n<0&&m.width-i.width<=k.right?d[1]="left":k.right+n<0&&m.width-i.width<=k.left&&(d[1]="right")}else{var o=i.height/2-m.height/2;k.top+o<0&&m.height-i.height<=k.bottom?d[1]="top":k.bottom+o<0&&m.height-i.height<=k.top&&(d[1]="bottom")}}switch(d[0]){case"top":j.top=i.top-h;break;case"bottom":j.top=i.top+i.height;break;case"left":j.left=i.left-g;break;case"right":j.left=i.left+i.width}switch(d[1]){case"top":j.top=i.top;break;case"bottom":j.top=i.top+i.height-h;break;case"left":j.left=i.left;break;case"right":j.left=i.left+i.width-g;break;case"center":f.vertical.test(d[0])?j.left=i.left+i.width/2-g/2:j.top=i.top+i.height/2-h/2}return j.top=Math.round(j.top),j.left=Math.round(j.left),j.placement="center"===d[1]?d[0]:d[0]+"-"+d[1],j},adjustTop:function(a,b,c,d){return-1!==a.indexOf("top")&&c!==d?{top:b.top-d+"px"}:void 0},positionArrow:function(a,c){a=this.getRawNode(a);var d=a.querySelector(".tooltip-inner, .popover-inner");if(d){var e=angular.element(d).hasClass("tooltip-inner"),g=e?a.querySelector(".tooltip-arrow"):a.querySelector(".arrow");if(g){var h={top:"",bottom:"",left:"",right:""};if(c=this.parsePlacement(c),"center"===c[1])return void angular.element(g).css(h);var i="border-"+c[0]+"-width",j=b.getComputedStyle(g)[i],k="border-";k+=f.vertical.test(c[0])?c[0]+"-"+c[1]:c[1]+"-"+c[0],k+="-radius";var l=b.getComputedStyle(e?d:a)[k];switch(c[0]){case"top":h.bottom=e?"0":"-"+j;break;case"bottom":h.top=e?"0":"-"+j;break;case"left":h.right=e?"0":"-"+j;break;case"right":h.left=e?"0":"-"+j}h[c[1]]=l,angular.element(g).css(h)}}}}}]),angular.module("ui.bootstrap.datepickerPopup",["ui.bootstrap.datepicker","ui.bootstrap.position"]).value("$datepickerPopupLiteralWarning",!0).constant("uibDatepickerPopupConfig",{altInputFormats:[],appendToBody:!1,clearText:"Clear",closeOnDateSelection:!0,closeText:"Done",currentText:"Today",datepickerPopup:"yyyy-MM-dd",datepickerPopupTemplateUrl:"uib/template/datepickerPopup/popup.html",datepickerTemplateUrl:"uib/template/datepicker/datepicker.html",html5Types:{date:"yyyy-MM-dd","datetime-local":"yyyy-MM-ddTHH:mm:ss.sss",month:"yyyy-MM"},onOpenFocus:!0,showButtonBar:!0,placement:"auto bottom-left"}).controller("UibDatepickerPopupController",["$scope","$element","$attrs","$compile","$log","$parse","$window","$document","$rootScope","$uibPosition","dateFilter","uibDateParser","uibDatepickerPopupConfig","$timeout","uibDatepickerConfig","$datepickerPopupLiteralWarning",function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p){function q(b){var c=l.parse(b,w,a.date);if(isNaN(c))for(var d=0;d
      "),C.attr({"ng-model":"date","ng-change":"dateSelection(date)","template-url":A}),D=angular.element(C.children()[0]),D.attr("template-url",B),a.datepickerOptions||(a.datepickerOptions={}),J&&"month"===c.type&&(a.datepickerOptions.datepickerMode="month",a.datepickerOptions.minMode="month"),D.attr("datepicker-options","datepickerOptions"),J?F.$formatters.push(function(b){return a.date=l.fromTimezone(b,G.timezone),b}):(F.$$parserName="date",F.$validators.date=s,F.$parsers.unshift(r),F.$formatters.push(function(b){return F.$isEmpty(b)?(a.date=b,b):(angular.isNumber(b)&&(b=new Date(b)),a.date=l.fromTimezone(b,G.timezone),l.filter(a.date,w))})),F.$viewChangeListeners.push(function(){a.date=q(F.$viewValue)}),b.on("keydown",u),H=d(C)(a),C.remove(),y?h.find("body").append(H):b.after(H),a.$on("$destroy",function(){for(a.isOpen===!0&&(i.$$phase||a.$apply(function(){a.isOpen=!1})),H.remove(),b.off("keydown",u),h.off("click",t),E&&E.off("scroll",v),angular.element(g).off("resize",v);K.length;)K.shift()()})},a.getText=function(b){return a[b+"Text"]||m[b+"Text"]},a.isDisabled=function(b){"today"===b&&(b=l.fromTimezone(new Date,G.timezone));var c={};return angular.forEach(["minDate","maxDate"],function(b){a.datepickerOptions[b]?angular.isDate(a.datepickerOptions[b])?c[b]=new Date(a.datepickerOptions[b]):(p&&e.warn("Literal date support has been deprecated, please switch to date object usage"),c[b]=new Date(k(a.datepickerOptions[b],"medium"))):c[b]=null}),a.datepickerOptions&&c.minDate&&a.compare(b,c.minDate)<0||c.maxDate&&a.compare(b,c.maxDate)>0},a.compare=function(a,b){return new Date(a.getFullYear(),a.getMonth(),a.getDate())-new Date(b.getFullYear(),b.getMonth(),b.getDate())},a.dateSelection=function(c){a.date=c;var d=a.date?l.filter(a.date,w):null;b.val(d),F.$setViewValue(d),x&&(a.isOpen=!1,b[0].focus())},a.keydown=function(c){27===c.which&&(c.stopPropagation(),a.isOpen=!1,b[0].focus())},a.select=function(b,c){if(c.stopPropagation(),"today"===b){var d=new Date;angular.isDate(a.date)?(b=new Date(a.date),b.setFullYear(d.getFullYear(),d.getMonth(),d.getDate())):(b=l.fromTimezone(d,G.timezone),b.setHours(0,0,0,0))}a.dateSelection(b)},a.close=function(c){c.stopPropagation(),a.isOpen=!1,b[0].focus()},a.disabled=angular.isDefined(c.disabled)||!1,c.ngDisabled&&K.push(a.$parent.$watch(f(c.ngDisabled),function(b){a.disabled=b})),a.$watch("isOpen",function(d){d?a.disabled?a.isOpen=!1:n(function(){v(),z&&a.$broadcast("uib:datepicker.focus"),h.on("click",t);var d=c.popupPlacement?c.popupPlacement:m.placement;y||j.parsePlacement(d)[2]?(E=E||angular.element(j.scrollParent(b)),E&&E.on("scroll",v)):E=null,angular.element(g).on("resize",v)},0,!1):(h.off("click",t),E&&E.off("scroll",v),angular.element(g).off("resize",v))}),a.$on("uib:datepicker.mode",function(){n(v,0,!1)})}]).directive("uibDatepickerPopup",function(){return{require:["ngModel","uibDatepickerPopup"],controller:"UibDatepickerPopupController",scope:{datepickerOptions:"=?",isOpen:"=?",currentText:"@",clearText:"@",closeText:"@"},link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibDatepickerPopupWrap",function(){return{restrict:"A",transclude:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/datepickerPopup/popup.html"}}}),angular.module("ui.bootstrap.debounce",[]).factory("$$debounce",["$timeout",function(a){return function(b,c){var d;return function(){var e=this,f=Array.prototype.slice.call(arguments);d&&a.cancel(d),d=a(function(){b.apply(e,f)},c)}}}]),angular.module("ui.bootstrap.multiMap",[]).factory("$$multiMap",function(){return{createNew:function(){var a={};return{entries:function(){return Object.keys(a).map(function(b){return{key:b,value:a[b]}})},get:function(b){return a[b]},hasKey:function(b){return!!a[b]},keys:function(){return Object.keys(a)},put:function(b,c){a[b]||(a[b]=[]),a[b].push(c)},remove:function(b,c){var d=a[b];if(d){var e=d.indexOf(c);-1!==e&&d.splice(e,1),d.length||delete a[b]}}}}}}),angular.module("ui.bootstrap.dropdown",["ui.bootstrap.multiMap","ui.bootstrap.position"]).constant("uibDropdownConfig",{appendToOpenClass:"uib-dropdown-open",openClass:"open"}).service("uibDropdownService",["$document","$rootScope","$$multiMap",function(a,b,c){var d=null,e=c.createNew();this.isOnlyOpen=function(a,b){var c=e.get(b);if(c){var d=c.reduce(function(b,c){return c.scope===a?c:b},{});if(d)return 1===c.length}return!1},this.open=function(b,c,g){if(d||a.on("click",f),d&&d!==b&&(d.isOpen=!1),d=b,g){var h=e.get(g);if(h){var i=h.map(function(a){return a.scope});-1===i.indexOf(b)&&e.put(g,{scope:b})}else e.put(g,{scope:b})}},this.close=function(b,c,g){if(d===b&&(a.off("click",f),a.off("keydown",this.keybindFilter),d=null),g){var h=e.get(g);if(h){var i=h.reduce(function(a,c){return c.scope===b?c:a},{});i&&e.remove(g,i)}}};var f=function(a){if(d&&!(a&&"disabled"===d.getAutoClose()||a&&3===a.which)){var c=d.getToggleElement();if(!(a&&c&&c[0].contains(a.target))){var e=d.getDropdownElement();a&&"outsideClick"===d.getAutoClose()&&e&&e[0].contains(a.target)||(d.focusToggleElement(),d.isOpen=!1,b.$$phase||d.$apply())}}};this.keybindFilter=function(a){if(d){var b=d.getDropdownElement(),c=d.getToggleElement(),e=b&&b[0].contains(a.target),g=c&&c[0].contains(a.target);27===a.which?(a.stopPropagation(),d.focusToggleElement(),f()):d.isKeynavEnabled()&&-1!==[38,40].indexOf(a.which)&&d.isOpen&&(e||g)&&(a.preventDefault(),a.stopPropagation(),d.focusDropdownEntry(a.which))}}}]).controller("UibDropdownController",["$scope","$element","$attrs","$parse","uibDropdownConfig","uibDropdownService","$animate","$uibPosition","$document","$compile","$templateRequest",function(a,b,c,d,e,f,g,h,i,j,k){var l,m,n=this,o=a.$new(),p=e.appendToOpenClass,q=e.openClass,r=angular.noop,s=c.onToggle?d(c.onToggle):angular.noop,t=!1,u=null,v=!1,w=i.find("body");b.addClass("dropdown"),this.init=function(){if(c.isOpen&&(m=d(c.isOpen),r=m.assign,a.$watch(m,function(a){o.isOpen=!!a})),angular.isDefined(c.dropdownAppendTo)){var e=d(c.dropdownAppendTo)(o);e&&(u=angular.element(e))}t=angular.isDefined(c.dropdownAppendToBody),v=angular.isDefined(c.keyboardNav),t&&!u&&(u=w),u&&n.dropdownMenu&&(u.append(n.dropdownMenu),b.on("$destroy",function(){n.dropdownMenu.remove()}))},this.toggle=function(a){return o.isOpen=arguments.length?!!a:!o.isOpen,angular.isFunction(r)&&r(o,o.isOpen),o.isOpen},this.isOpen=function(){return o.isOpen},o.getToggleElement=function(){return n.toggleElement},o.getAutoClose=function(){return c.autoClose||"always"},o.getElement=function(){return b},o.isKeynavEnabled=function(){return v},o.focusDropdownEntry=function(a){var c=n.dropdownMenu?angular.element(n.dropdownMenu).find("a"):b.find("ul").eq(0).find("a");switch(a){case 40:angular.isNumber(n.selectedOption)?n.selectedOption=n.selectedOption===c.length-1?n.selectedOption:n.selectedOption+1:n.selectedOption=0;break;case 38:angular.isNumber(n.selectedOption)?n.selectedOption=0===n.selectedOption?0:n.selectedOption-1:n.selectedOption=c.length-1}c[n.selectedOption].focus()},o.getDropdownElement=function(){return n.dropdownMenu},o.focusToggleElement=function(){n.toggleElement&&n.toggleElement[0].focus()},o.$watch("isOpen",function(c,d){if(u&&n.dropdownMenu){var e,m,v,w=h.positionElements(b,n.dropdownMenu,"bottom-left",!0),x=0;if(e={top:w.top+"px",display:c?"block":"none"},m=n.dropdownMenu.hasClass("dropdown-menu-right"),m?(e.left="auto",v=h.scrollbarPadding(u),v.heightOverflow&&v.scrollbarWidth&&(x=v.scrollbarWidth),e.right=window.innerWidth-x-(w.left+b.prop("offsetWidth"))+"px"):(e.left=w.left+"px",e.right="auto"),!t){var y=h.offset(u);e.top=w.top-y.top+"px",m?e.right=window.innerWidth-(w.left-y.left+b.prop("offsetWidth"))+"px":e.left=w.left-y.left+"px"}n.dropdownMenu.css(e)}var z=u?u:b,A=u?p:q,B=z.hasClass(A),C=f.isOnlyOpen(a,u);if(B===!c){var D;D=u?C?"removeClass":"addClass":c?"addClass":"removeClass",g[D](z,A).then(function(){angular.isDefined(c)&&c!==d&&s(a,{open:!!c})})}if(c)n.dropdownMenuTemplateUrl?k(n.dropdownMenuTemplateUrl).then(function(a){l=o.$new(),j(a.trim())(l,function(a){var b=a;n.dropdownMenu.replaceWith(b),n.dropdownMenu=b,i.on("keydown",f.keybindFilter)})}):i.on("keydown",f.keybindFilter),o.focusToggleElement(),f.open(o,b,u);else{if(f.close(o,b,u),n.dropdownMenuTemplateUrl){l&&l.$destroy();var E=angular.element('');n.dropdownMenu.replaceWith(E),n.dropdownMenu=E}n.selectedOption=null}angular.isFunction(r)&&r(a,c)})}]).directive("uibDropdown",function(){return{controller:"UibDropdownController",link:function(a,b,c,d){d.init()}}}).directive("uibDropdownMenu",function(){return{restrict:"A",require:"?^uibDropdown",link:function(a,b,c,d){if(d&&!angular.isDefined(c.dropdownNested)){b.addClass("dropdown-menu");var e=c.templateUrl;e&&(d.dropdownMenuTemplateUrl=e),d.dropdownMenu||(d.dropdownMenu=b)}}}}).directive("uibDropdownToggle",function(){return{require:"?^uibDropdown",link:function(a,b,c,d){if(d){b.addClass("dropdown-toggle"),d.toggleElement=b;var e=function(e){e.preventDefault(),b.hasClass("disabled")||c.disabled||a.$apply(function(){d.toggle()})};b.on("click",e),b.attr({"aria-haspopup":!0,"aria-expanded":!1}),a.$watch(d.isOpen,function(a){b.attr("aria-expanded",!!a)}),a.$on("$destroy",function(){b.off("click",e)})}}}}),angular.module("ui.bootstrap.stackedMap",[]).factory("$$stackedMap",function(){return{createNew:function(){var a=[];return{add:function(b,c){a.push({key:b,value:c})},get:function(b){for(var c=0;c-1&&A>a&&(a=A),a}function m(a,b){var c=x.get(a).value,d=c.appendTo;x.remove(a),B=x.top(),B&&(A=parseInt(B.value.modalDomEl.attr("index"),10)),p(c.modalDomEl,c.modalScope,function(){var b=c.openedClass||w;y.remove(b,a);var e=y.hasKey(b);d.toggleClass(b,e),!e&&v&&v.heightOverflow&&v.scrollbarWidth&&(v.originalRight?d.css({paddingRight:v.originalRight+"px"}):d.css({paddingRight:""}),v=null),n(!0)},c.closedDeferred),o(),b&&b.focus?b.focus():d.focus&&d.focus()}function n(a){var b;x.length()>0&&(b=x.top().value,b.modalDomEl.toggleClass(b.windowTopClass||"",a))}function o(){if(t&&-1===l()){var a=u;p(t,u,function(){a=null}),t=void 0,u=void 0}}function p(b,c,d,e){function g(){g.done||(g.done=!0,a.leave(b).then(function(){d&&d(),b.remove(),e&&e.resolve()}),c.$destroy())}var h,i=null,j=function(){return h||(h=f.defer(),i=h.promise),function(){h.resolve()}};return c.$broadcast(z.NOW_CLOSING_EVENT,j),f.when(i).then(g)}function q(a){if(a.isDefaultPrevented())return a;var b=x.top();if(b)switch(a.which){case 27:b.value.keyboard&&(a.preventDefault(),e.$apply(function(){z.dismiss(b.key,"escape key press")}));break;case 9:var c=z.loadFocusElementList(b),d=!1;a.shiftKey?(z.isFocusInFirstItem(a,c)||z.isModalFocused(a,b))&&(d=z.focusLastFocusableElement(c)):z.isFocusInLastItem(a,c)&&(d=z.focusFirstFocusableElement(c)),d&&(a.preventDefault(),a.stopPropagation())}}function r(a,b,c){return!a.value.modalScope.$broadcast("modal.closing",b,c).defaultPrevented}function s(){Array.prototype.forEach.call(document.querySelectorAll("["+C+"]"),function(a){var b=parseInt(a.getAttribute(C),10),c=b-1;a.setAttribute(C,c),c||(a.removeAttribute(C),a.removeAttribute("aria-hidden"))})}var t,u,v,w="modal-open",x=h.createNew(),y=g.createNew(),z={NOW_CLOSING_EVENT:"modal.stack.now-closing"},A=0,B=null,C="data-bootstrap-modal-aria-hidden-count",D="a[href], area[href], input:not([disabled]):not([tabindex='-1']), button:not([disabled]):not([tabindex='-1']),select:not([disabled]):not([tabindex='-1']), textarea:not([disabled]):not([tabindex='-1']), iframe, object, embed, *[tabindex]:not([tabindex='-1']), *[contenteditable=true]",E=/[A-Z]/g;return e.$watch(l,function(a){u&&(u.index=a)}),c.on("keydown",q),e.$on("$destroy",function(){c.off("keydown",q)}),z.open=function(b,f){function g(a){function b(a){var b=a.parent()?a.parent().children():[];return Array.prototype.filter.call(b,function(b){return b!==a[0]})}if(a&&"BODY"!==a[0].tagName)return b(a).forEach(function(a){var b="true"===a.getAttribute("aria-hidden"),c=parseInt(a.getAttribute(C),10);c||(c=b?1:0),a.setAttribute(C,c+1),a.setAttribute("aria-hidden","true")}),g(a.parent())}var h=c[0].activeElement,k=f.openedClass||w;n(!1),B=x.top(),x.add(b,{deferred:f.deferred,renderDeferred:f.renderDeferred,closedDeferred:f.closedDeferred,modalScope:f.scope,backdrop:f.backdrop,keyboard:f.keyboard,openedClass:f.openedClass,windowTopClass:f.windowTopClass,animation:f.animation,appendTo:f.appendTo}),y.put(k,b);var m=f.appendTo,o=l();if(!m.length)throw new Error("appendTo element not found. Make sure that the element passed is in DOM.");o>=0&&!t&&(u=e.$new(!0),u.modalOptions=f,u.index=o,t=angular.element('
      '),t.attr({"class":"modal-backdrop","ng-style":"{'z-index': 1040 + (index && 1 || 0) + index*10}","uib-modal-animation-class":"fade","modal-in-class":"in"}),f.backdropClass&&t.addClass(f.backdropClass),f.animation&&t.attr("modal-animation","true"),d(t)(u),a.enter(t,m),i.isScrollable(m)&&(v=i.scrollbarPadding(m),v.heightOverflow&&v.scrollbarWidth&&m.css({paddingRight:v.right+"px"})));var p;f.component?(p=document.createElement(j(f.component.name)),p=angular.element(p),p.attr({resolve:"$resolve","modal-instance":"$uibModalInstance",close:"$close($value)",dismiss:"$dismiss($value)"})):p=f.content,A=B?parseInt(B.value.modalDomEl.attr("index"),10)+1:0;var q=angular.element('
      ');q.attr({"class":"modal","template-url":f.windowTemplateUrl,"window-top-class":f.windowTopClass,role:"dialog","aria-labelledby":f.ariaLabelledBy,"aria-describedby":f.ariaDescribedBy,size:f.size,index:A,animate:"animate","ng-style":"{'z-index': 1050 + $$topModalIndex*10, display: 'block'}",tabindex:-1,"uib-modal-animation-class":"fade","modal-in-class":"in"}).append(p),f.windowClass&&q.addClass(f.windowClass),f.animation&&q.attr("modal-animation","true"),m.addClass(k),f.scope&&(f.scope.$$topModalIndex=A),a.enter(d(q)(f.scope),m),x.top().value.modalDomEl=q,x.top().value.modalOpener=h,g(q)},z.close=function(a,b){var c=x.get(a);return s(),c&&r(c,b,!0)?(c.value.modalScope.$$uibDestructionScheduled=!0,c.value.deferred.resolve(b),m(a,c.value.modalOpener),!0):!c},z.dismiss=function(a,b){var c=x.get(a);return s(),c&&r(c,b,!1)?(c.value.modalScope.$$uibDestructionScheduled=!0,c.value.deferred.reject(b),m(a,c.value.modalOpener),!0):!c},z.dismissAll=function(a){for(var b=this.getTop();b&&this.dismiss(b.key,a);)b=this.getTop()},z.getTop=function(){return x.top()},z.modalRendered=function(a){var b=x.get(a);b&&b.value.renderDeferred.resolve()},z.focusFirstFocusableElement=function(a){return a.length>0?(a[0].focus(),!0):!1},z.focusLastFocusableElement=function(a){return a.length>0?(a[a.length-1].focus(),!0):!1},z.isModalFocused=function(a,b){if(a&&b){var c=b.value.modalDomEl;if(c&&c.length)return(a.target||a.srcElement)===c[0]}return!1},z.isFocusInFirstItem=function(a,b){return b.length>0?(a.target||a.srcElement)===b[0]:!1},z.isFocusInLastItem=function(a,b){return b.length>0?(a.target||a.srcElement)===b[b.length-1]:!1},z.loadFocusElementList=function(a){if(a){var b=a.value.modalDomEl;if(b&&b.length){var c=b[0].querySelectorAll(D);return c?Array.prototype.filter.call(c,function(a){return k(a)}):c}}},z}]).provider("$uibModal",function(){var a={options:{animation:!0,backdrop:!0,keyboard:!0},$get:["$rootScope","$q","$document","$templateRequest","$controller","$uibResolve","$uibModalStack",function(b,c,d,e,f,g,h){function i(a){return a.template?c.when(a.template):e(angular.isFunction(a.templateUrl)?a.templateUrl():a.templateUrl)}var j={},k=null;return j.getPromiseChain=function(){return k},j.open=function(e){function j(){return q}var l=c.defer(),m=c.defer(),n=c.defer(),o=c.defer(),p={result:l.promise,opened:m.promise,closed:n.promise,rendered:o.promise,close:function(a){return h.close(p,a)},dismiss:function(a){return h.dismiss(p,a)}};if(e=angular.extend({},a.options,e),e.resolve=e.resolve||{},e.appendTo=e.appendTo||d.find("body").eq(0),!e.component&&!e.template&&!e.templateUrl)throw new Error("One of component or template or templateUrl options is required.");var q;q=e.component?c.when(g.resolve(e.resolve,{},null,null)):c.all([i(e),g.resolve(e.resolve,{},null,null)]);var r;return r=k=c.all([k]).then(j,j).then(function(a){function c(b,c,d,e){b.$scope=g,b.$scope.$resolve={},d?b.$scope.$uibModalInstance=p:b.$uibModalInstance=p;var f=c?a[1]:a;angular.forEach(f,function(a,c){e&&(b[c]=a),b.$scope.$resolve[c]=a})}var d=e.scope||b,g=d.$new();g.$close=p.close,g.$dismiss=p.dismiss,g.$on("$destroy",function(){g.$$uibDestructionScheduled||g.$dismiss("$uibUnscheduledDestruction")});var i,j,k={scope:g,deferred:l,renderDeferred:o,closedDeferred:n,animation:e.animation,backdrop:e.backdrop,keyboard:e.keyboard,backdropClass:e.backdropClass,windowTopClass:e.windowTopClass,windowClass:e.windowClass,windowTemplateUrl:e.windowTemplateUrl,ariaLabelledBy:e.ariaLabelledBy,ariaDescribedBy:e.ariaDescribedBy,size:e.size,openedClass:e.openedClass,appendTo:e.appendTo},q={},r={};e.component?(c(q,!1,!0,!1),q.name=e.component,k.component=q):e.controller&&(c(r,!0,!1,!0),j=f(e.controller,r,!0,e.controllerAs),e.controllerAs&&e.bindToController&&(i=j.instance,i.$close=g.$close,i.$dismiss=g.$dismiss,angular.extend(i,{$resolve:r.$scope.$resolve},d)),i=j(),angular.isFunction(i.$onInit)&&i.$onInit()),e.component||(k.content=a[0]),h.open(p,k),m.resolve(!0)},function(a){m.reject(a),l.reject(a)})["finally"](function(){k===r&&(k=null)}),p},j}]};return a}),angular.module("ui.bootstrap.paging",[]).factory("uibPaging",["$parse",function(a){return{create:function(b,c,d){b.setNumPages=d.numPages?a(d.numPages).assign:angular.noop,b.ngModelCtrl={$setViewValue:angular.noop},b._watchers=[],b.init=function(a,e){b.ngModelCtrl=a,b.config=e,a.$render=function(){b.render()},d.itemsPerPage?b._watchers.push(c.$parent.$watch(d.itemsPerPage,function(a){b.itemsPerPage=parseInt(a,10),c.totalPages=b.calculateTotalPages(),b.updatePage()})):b.itemsPerPage=e.itemsPerPage,c.$watch("totalItems",function(a,d){(angular.isDefined(a)||a!==d)&&(c.totalPages=b.calculateTotalPages(),b.updatePage())})},b.calculateTotalPages=function(){var a=b.itemsPerPage<1?1:Math.ceil(c.totalItems/b.itemsPerPage);return Math.max(a||0,1)},b.render=function(){c.page=parseInt(b.ngModelCtrl.$viewValue,10)||1},c.selectPage=function(a,d){d&&d.preventDefault();var e=!c.ngDisabled||!d;e&&c.page!==a&&a>0&&a<=c.totalPages&&(d&&d.target&&d.target.blur(),b.ngModelCtrl.$setViewValue(a),b.ngModelCtrl.$render())},c.getText=function(a){return c[a+"Text"]||b.config[a+"Text"]},c.noPrevious=function(){return 1===c.page},c.noNext=function(){return c.page===c.totalPages},b.updatePage=function(){b.setNumPages(c.$parent,c.totalPages),c.page>c.totalPages?c.selectPage(c.totalPages):b.ngModelCtrl.$render()},c.$on("$destroy",function(){for(;b._watchers.length;)b._watchers.shift()()})}}}]),angular.module("ui.bootstrap.pager",["ui.bootstrap.paging","ui.bootstrap.tabindex"]).controller("UibPagerController",["$scope","$attrs","uibPaging","uibPagerConfig",function(a,b,c,d){a.align=angular.isDefined(b.align)?a.$parent.$eval(b.align):d.align,c.create(this,a,b)}]).constant("uibPagerConfig",{itemsPerPage:10,previousText:"« Previous",nextText:"Next »",align:!0}).directive("uibPager",["uibPagerConfig",function(a){return{scope:{totalItems:"=",previousText:"@",nextText:"@",ngDisabled:"="},require:["uibPager","?ngModel"],restrict:"A",controller:"UibPagerController",controllerAs:"pager",templateUrl:function(a,b){return b.templateUrl||"uib/template/pager/pager.html"},link:function(b,c,d,e){c.addClass("pager");var f=e[0],g=e[1];g&&f.init(g,a)}}}]),angular.module("ui.bootstrap.pagination",["ui.bootstrap.paging","ui.bootstrap.tabindex"]).controller("UibPaginationController",["$scope","$attrs","$parse","uibPaging","uibPaginationConfig",function(a,b,c,d,e){ +function f(a,b,c){return{number:a,text:b,active:c}}function g(a,b){var c=[],d=1,e=b,g=angular.isDefined(i)&&b>i;g&&(j?(d=Math.max(a-Math.floor(i/2),1),e=d+i-1,e>b&&(e=b,d=e-i+1)):(d=(Math.ceil(a/i)-1)*i+1,e=Math.min(d+i-1,b)));for(var h=d;e>=h;h++){var n=f(h,m(h),h===a);c.push(n)}if(g&&i>0&&(!j||k||l)){if(d>1){if(!l||d>3){var o=f(d-1,"...",!1);c.unshift(o)}if(l){if(3===d){var p=f(2,"2",!1);c.unshift(p)}var q=f(1,"1",!1);c.unshift(q)}}if(b>e){if(!l||b-2>e){var r=f(e+1,"...",!1);c.push(r)}if(l){if(e===b-2){var s=f(b-1,b-1,!1);c.push(s)}var t=f(b,b,!1);c.push(t)}}}return c}var h=this,i=angular.isDefined(b.maxSize)?a.$parent.$eval(b.maxSize):e.maxSize,j=angular.isDefined(b.rotate)?a.$parent.$eval(b.rotate):e.rotate,k=angular.isDefined(b.forceEllipses)?a.$parent.$eval(b.forceEllipses):e.forceEllipses,l=angular.isDefined(b.boundaryLinkNumbers)?a.$parent.$eval(b.boundaryLinkNumbers):e.boundaryLinkNumbers,m=angular.isDefined(b.pageLabel)?function(c){return a.$parent.$eval(b.pageLabel,{$page:c})}:angular.identity;a.boundaryLinks=angular.isDefined(b.boundaryLinks)?a.$parent.$eval(b.boundaryLinks):e.boundaryLinks,a.directionLinks=angular.isDefined(b.directionLinks)?a.$parent.$eval(b.directionLinks):e.directionLinks,d.create(this,a,b),b.maxSize&&h._watchers.push(a.$parent.$watch(c(b.maxSize),function(a){i=parseInt(a,10),h.render()}));var n=this.render;this.render=function(){n(),a.page>0&&a.page<=a.totalPages&&(a.pages=g(a.page,a.totalPages))}}]).constant("uibPaginationConfig",{itemsPerPage:10,boundaryLinks:!1,boundaryLinkNumbers:!1,directionLinks:!0,firstText:"First",previousText:"Previous",nextText:"Next",lastText:"Last",rotate:!0,forceEllipses:!1}).directive("uibPagination",["$parse","uibPaginationConfig",function(a,b){return{scope:{totalItems:"=",firstText:"@",previousText:"@",nextText:"@",lastText:"@",ngDisabled:"="},require:["uibPagination","?ngModel"],restrict:"A",controller:"UibPaginationController",controllerAs:"pagination",templateUrl:function(a,b){return b.templateUrl||"uib/template/pagination/pagination.html"},link:function(a,c,d,e){c.addClass("pagination");var f=e[0],g=e[1];g&&f.init(g,b)}}}]),angular.module("ui.bootstrap.tooltip",["ui.bootstrap.position","ui.bootstrap.stackedMap"]).provider("$uibTooltip",function(){function a(a){var b=/[A-Z]/g,c="-";return a.replace(b,function(a,b){return(b?c:"")+a.toLowerCase()})}var b={placement:"top",placementClassPrefix:"",animation:!0,popupDelay:0,popupCloseDelay:0,useContentExp:!1},c={mouseenter:"mouseleave",click:"click",outsideClick:"outsideClick",focus:"blur",none:""},d={};this.options=function(a){angular.extend(d,a)},this.setTriggers=function(a){angular.extend(c,a)},this.$get=["$window","$compile","$timeout","$document","$uibPosition","$interpolate","$rootScope","$parse","$$stackedMap",function(e,f,g,h,i,j,k,l,m){function n(a){if(27===a.which){var b=o.top();b&&(b.value.close(),b=null)}}var o=m.createNew();return h.on("keyup",n),k.$on("$destroy",function(){h.off("keyup",n)}),function(e,k,m,n){function p(a){var b=(a||n.trigger||m).split(" "),d=b.map(function(a){return c[a]||a});return{show:b,hide:d}}n=angular.extend({},b,d,n);var q=a(e),r=j.startSymbol(),s=j.endSymbol(),t="
      ';return{compile:function(a,b){var c=f(t);return function(a,b,d,f){function j(){O.isOpen?q():m()}function m(){N&&!a.$eval(d[k+"Enable"])||(u(),x(),O.popupDelay?G||(G=g(r,O.popupDelay,!1)):r())}function q(){s(),O.popupCloseDelay?H||(H=g(t,O.popupCloseDelay,!1)):t()}function r(){return s(),u(),O.content?(v(),void O.$evalAsync(function(){O.isOpen=!0,y(!0),T()})):angular.noop}function s(){G&&(g.cancel(G),G=null),I&&(g.cancel(I),I=null)}function t(){O&&O.$evalAsync(function(){O&&(O.isOpen=!1,y(!1),O.animation?F||(F=g(w,150,!1)):w())})}function u(){H&&(g.cancel(H),H=null),F&&(g.cancel(F),F=null)}function v(){D||(E=O.$new(),D=c(E,function(a){L?h.find("body").append(a):b.after(a)}),o.add(O,{close:t}),z())}function w(){s(),u(),A(),D&&(D.remove(),D=null,J&&g.cancel(J)),o.remove(O),E&&(E.$destroy(),E=null)}function x(){O.title=d[k+"Title"],R?O.content=R(a):O.content=d[e],O.popupClass=d[k+"Class"],O.placement=angular.isDefined(d[k+"Placement"])?d[k+"Placement"]:n.placement;var b=i.parsePlacement(O.placement);K=b[1]?b[0]+"-"+b[1]:b[0];var c=parseInt(d[k+"PopupDelay"],10),f=parseInt(d[k+"PopupCloseDelay"],10);O.popupDelay=isNaN(c)?n.popupDelay:c,O.popupCloseDelay=isNaN(f)?n.popupCloseDelay:f}function y(b){Q&&angular.isFunction(Q.assign)&&Q.assign(a,b)}function z(){S.length=0,R?(S.push(a.$watch(R,function(a){O.content=a,!a&&O.isOpen&&t()})),S.push(E.$watch(function(){P||(P=!0,E.$$postDigest(function(){P=!1,O&&O.isOpen&&T()}))}))):S.push(d.$observe(e,function(a){O.content=a,!a&&O.isOpen?t():T()})),S.push(d.$observe(k+"Title",function(a){O.title=a,O.isOpen&&T()})),S.push(d.$observe(k+"Placement",function(a){O.placement=a?a:n.placement,O.isOpen&&T()}))}function A(){S.length&&(angular.forEach(S,function(a){a()}),S.length=0)}function B(a){O&&O.isOpen&&D&&(b[0].contains(a.target)||D[0].contains(a.target)||q())}function C(){var c=[],e=[],f=a.$eval(d[k+"Trigger"]);U(),angular.isObject(f)?(Object.keys(f).forEach(function(a){c.push(a),e.push(f[a])}),M={show:c,hide:e}):M=p(f),"none"!==M.show&&M.show.forEach(function(a,c){"outsideClick"===a?(b.on("click",j),h.on("click",B)):a===M.hide[c]?b.on(a,j):a&&(b.on(a,m),b.on(M.hide[c],q)),b.on("keypress",function(a){27===a.which&&q()})})}var D,E,F,G,H,I,J,K,L=angular.isDefined(n.appendToBody)?n.appendToBody:!1,M=p(void 0),N=angular.isDefined(d[k+"Enable"]),O=a.$new(!0),P=!1,Q=angular.isDefined(d[k+"IsOpen"])?l(d[k+"IsOpen"]):!1,R=n.useContentExp?l(d[e]):!1,S=[],T=function(){D&&D.html()&&(I||(I=g(function(){var a=i.positionElements(b,D,O.placement,L),c=angular.isDefined(D.offsetHeight)?D.offsetHeight:D.prop("offsetHeight"),d=L?i.offset(b):i.position(b);D.css({top:a.top+"px",left:a.left+"px"});var e=a.placement.split("-");D.hasClass(e[0])||(D.removeClass(K.split("-")[0]),D.addClass(e[0])),D.hasClass(n.placementClassPrefix+a.placement)||(D.removeClass(n.placementClassPrefix+K),D.addClass(n.placementClassPrefix+a.placement)),J=g(function(){var a=angular.isDefined(D.offsetHeight)?D.offsetHeight:D.prop("offsetHeight"),b=i.adjustTop(e,d,c,a);b&&D.css(b),J=null},0,!1),D.hasClass("uib-position-measure")?(i.positionArrow(D,a.placement),D.removeClass("uib-position-measure")):K!==a.placement&&i.positionArrow(D,a.placement),K=a.placement,I=null},0,!1)))};O.origScope=a,O.isOpen=!1,O.contentExp=function(){return O.content},d.$observe("disabled",function(a){a&&s(),a&&O.isOpen&&t()}),Q&&a.$watch(Q,function(a){O&&!a===O.isOpen&&j()});var U=function(){M.show.forEach(function(a){"outsideClick"===a?b.off("click",j):(b.off(a,m),b.off(a,j))}),M.hide.forEach(function(a){"outsideClick"===a?h.off("click",B):b.off(a,q)})};C();var V=a.$eval(d[k+"Animation"]);O.animation=angular.isDefined(V)?!!V:n.animation;var W,X=k+"AppendToBody";W=X in d&&void 0===d[X]?!0:a.$eval(d[X]),L=angular.isDefined(W)?W:L,a.$on("$destroy",function(){U(),w(),O=null})}}}}}]}).directive("uibTooltipTemplateTransclude",["$animate","$sce","$compile","$templateRequest",function(a,b,c,d){return{link:function(e,f,g){var h,i,j,k=e.$eval(g.tooltipTemplateTranscludeScope),l=0,m=function(){i&&(i.remove(),i=null),h&&(h.$destroy(),h=null),j&&(a.leave(j).then(function(){i=null}),i=j,j=null)};e.$watch(b.parseAsResourceUrl(g.uibTooltipTemplateTransclude),function(b){var g=++l;b?(d(b,!0).then(function(d){if(g===l){var e=k.$new(),i=d,n=c(i)(e,function(b){m(),a.enter(b,f)});h=e,j=n,h.$emit("$includeContentLoaded",b)}},function(){g===l&&(m(),e.$emit("$includeContentError",b))}),e.$emit("$includeContentRequested",b)):m()}),e.$on("$destroy",m)}}}]).directive("uibTooltipClasses",["$uibPosition",function(a){return{restrict:"A",link:function(b,c,d){if(b.placement){var e=a.parsePlacement(b.placement);c.addClass(e[0])}b.popupClass&&c.addClass(b.popupClass),b.animation&&c.addClass(d.tooltipAnimationClass)}}}]).directive("uibTooltipPopup",function(){return{restrict:"A",scope:{content:"@"},templateUrl:"uib/template/tooltip/tooltip-popup.html"}}).directive("uibTooltip",["$uibTooltip",function(a){return a("uibTooltip","tooltip","mouseenter")}]).directive("uibTooltipTemplatePopup",function(){return{restrict:"A",scope:{contentExp:"&",originScope:"&"},templateUrl:"uib/template/tooltip/tooltip-template-popup.html"}}).directive("uibTooltipTemplate",["$uibTooltip",function(a){return a("uibTooltipTemplate","tooltip","mouseenter",{useContentExp:!0})}]).directive("uibTooltipHtmlPopup",function(){return{restrict:"A",scope:{contentExp:"&"},templateUrl:"uib/template/tooltip/tooltip-html-popup.html"}}).directive("uibTooltipHtml",["$uibTooltip",function(a){return a("uibTooltipHtml","tooltip","mouseenter",{useContentExp:!0})}]),angular.module("ui.bootstrap.popover",["ui.bootstrap.tooltip"]).directive("uibPopoverTemplatePopup",function(){return{restrict:"A",scope:{uibTitle:"@",contentExp:"&",originScope:"&"},templateUrl:"uib/template/popover/popover-template.html"}}).directive("uibPopoverTemplate",["$uibTooltip",function(a){return a("uibPopoverTemplate","popover","click",{useContentExp:!0})}]).directive("uibPopoverHtmlPopup",function(){return{restrict:"A",scope:{contentExp:"&",uibTitle:"@"},templateUrl:"uib/template/popover/popover-html.html"}}).directive("uibPopoverHtml",["$uibTooltip",function(a){return a("uibPopoverHtml","popover","click",{useContentExp:!0})}]).directive("uibPopoverPopup",function(){return{restrict:"A",scope:{uibTitle:"@",content:"@"},templateUrl:"uib/template/popover/popover.html"}}).directive("uibPopover",["$uibTooltip",function(a){return a("uibPopover","popover","click")}]),angular.module("ui.bootstrap.progressbar",[]).constant("uibProgressConfig",{animate:!0,max:100}).controller("UibProgressController",["$scope","$attrs","uibProgressConfig",function(a,b,c){function d(){return angular.isDefined(a.maxParam)?a.maxParam:c.max}var e=this,f=angular.isDefined(b.animate)?a.$parent.$eval(b.animate):c.animate;this.bars=[],a.max=d(),this.addBar=function(a,b,c){f||b.css({transition:"none"}),this.bars.push(a),a.max=d(),a.title=c&&angular.isDefined(c.title)?c.title:"progressbar",a.$watch("value",function(b){a.recalculatePercentage()}),a.recalculatePercentage=function(){var b=e.bars.reduce(function(a,b){return b.percent=+(100*b.value/b.max).toFixed(2),a+b.percent},0);b>100&&(a.percent-=b-100)},a.$on("$destroy",function(){b=null,e.removeBar(a)})},this.removeBar=function(a){this.bars.splice(this.bars.indexOf(a),1),this.bars.forEach(function(a){a.recalculatePercentage()})},a.$watch("maxParam",function(a){e.bars.forEach(function(a){a.max=d(),a.recalculatePercentage()})})}]).directive("uibProgress",function(){return{replace:!0,transclude:!0,controller:"UibProgressController",require:"uibProgress",scope:{maxParam:"=?max"},templateUrl:"uib/template/progressbar/progress.html"}}).directive("uibBar",function(){return{replace:!0,transclude:!0,require:"^uibProgress",scope:{value:"=",type:"@"},templateUrl:"uib/template/progressbar/bar.html",link:function(a,b,c,d){d.addBar(a,b,c)}}}).directive("uibProgressbar",function(){return{replace:!0,transclude:!0,controller:"UibProgressController",scope:{value:"=",maxParam:"=?max",type:"@"},templateUrl:"uib/template/progressbar/progressbar.html",link:function(a,b,c,d){d.addBar(a,angular.element(b.children()[0]),{title:c.title})}}}),angular.module("ui.bootstrap.rating",[]).constant("uibRatingConfig",{max:5,stateOn:null,stateOff:null,enableReset:!0,titles:["one","two","three","four","five"]}).controller("UibRatingController",["$scope","$attrs","uibRatingConfig",function(a,b,c){var d={$setViewValue:angular.noop},e=this;this.init=function(e){d=e,d.$render=this.render,d.$formatters.push(function(a){return angular.isNumber(a)&&a<<0!==a&&(a=Math.round(a)),a}),this.stateOn=angular.isDefined(b.stateOn)?a.$parent.$eval(b.stateOn):c.stateOn,this.stateOff=angular.isDefined(b.stateOff)?a.$parent.$eval(b.stateOff):c.stateOff,this.enableReset=angular.isDefined(b.enableReset)?a.$parent.$eval(b.enableReset):c.enableReset;var f=angular.isDefined(b.titles)?a.$parent.$eval(b.titles):c.titles;this.titles=angular.isArray(f)&&f.length>0?f:c.titles;var g=angular.isDefined(b.ratingStates)?a.$parent.$eval(b.ratingStates):new Array(angular.isDefined(b.max)?a.$parent.$eval(b.max):c.max);a.range=this.buildTemplateObjects(g)},this.buildTemplateObjects=function(a){for(var b=0,c=a.length;c>b;b++)a[b]=angular.extend({index:b},{stateOn:this.stateOn,stateOff:this.stateOff,title:this.getTitle(b)},a[b]);return a},this.getTitle=function(a){return a>=this.titles.length?a+1:this.titles[a]},a.rate=function(b){if(!a.readonly&&b>=0&&b<=a.range.length){var c=e.enableReset&&d.$viewValue===b?0:b;d.$setViewValue(c),d.$render()}},a.enter=function(b){a.readonly||(a.value=b),a.onHover({value:b})},a.reset=function(){a.value=d.$viewValue,a.onLeave()},a.onKeydown=function(b){/(37|38|39|40)/.test(b.which)&&(b.preventDefault(),b.stopPropagation(),a.rate(a.value+(38===b.which||39===b.which?1:-1)))},this.render=function(){a.value=d.$viewValue,a.title=e.getTitle(a.value-1)}}]).directive("uibRating",function(){return{require:["uibRating","ngModel"],restrict:"A",scope:{readonly:"=?readOnly",onHover:"&",onLeave:"&"},controller:"UibRatingController",templateUrl:"uib/template/rating/rating.html",link:function(a,b,c,d){var e=d[0],f=d[1];e.init(f)}}}),angular.module("ui.bootstrap.tabs",[]).controller("UibTabsetController",["$scope",function(a){function b(a){for(var b=0;bb.index?1:a.index0&&13>b:b>=0&&24>b;return c&&""!==a.hours?(a.showMeridian&&(12===b&&(b=0),a.meridian===y[1]&&(b+=12)),b):void 0}function i(){var b=+a.minutes,c=b>=0&&60>b;return c&&""!==a.minutes?b:void 0}function j(){var b=+a.seconds;return b>=0&&60>b?b:void 0}function k(a,b){return null===a?"":angular.isDefined(a)&&a.toString().length<2&&!b?"0"+a:a.toString()}function l(a){m(),x.$setViewValue(new Date(v)),n(a)}function m(){s&&s.$setValidity("hours",!0),t&&t.$setValidity("minutes",!0),u&&u.$setValidity("seconds",!0),x.$setValidity("time",!0),a.invalidHours=!1,a.invalidMinutes=!1,a.invalidSeconds=!1}function n(b){if(x.$modelValue){var c=v.getHours(),d=v.getMinutes(),e=v.getSeconds();a.showMeridian&&(c=0===c||12===c?12:c%12),a.hours="h"===b?c:k(c,!z),"m"!==b&&(a.minutes=k(d)),a.meridian=v.getHours()<12?y[0]:y[1],"s"!==b&&(a.seconds=k(e)),a.meridian=v.getHours()<12?y[0]:y[1]}else a.hours=null,a.minutes=null,a.seconds=null,a.meridian=y[0]}function o(a){v=q(v,a),l()}function p(a,b){return q(a,60*b)}function q(a,b){var c=new Date(a.getTime()+1e3*b),d=new Date(a);return d.setHours(c.getHours(),c.getMinutes(),c.getSeconds()),d}function r(){return(null===a.hours||""===a.hours)&&(null===a.minutes||""===a.minutes)&&(!a.showSeconds||a.showSeconds&&(null===a.seconds||""===a.seconds))}var s,t,u,v=new Date,w=[],x={$setViewValue:angular.noop},y=angular.isDefined(c.meridians)?a.$parent.$eval(c.meridians):g.meridians||f.DATETIME_FORMATS.AMPMS,z=angular.isDefined(c.padHours)?a.$parent.$eval(c.padHours):!0;a.tabindex=angular.isDefined(c.tabindex)?c.tabindex:0,b.removeAttr("tabindex"),this.init=function(b,d){x=b,x.$render=this.render,x.$formatters.unshift(function(a){return a?new Date(a):null});var e=d.eq(0),f=d.eq(1),h=d.eq(2);s=e.controller("ngModel"),t=f.controller("ngModel"),u=h.controller("ngModel");var i=angular.isDefined(c.mousewheel)?a.$parent.$eval(c.mousewheel):g.mousewheel;i&&this.setupMousewheelEvents(e,f,h);var j=angular.isDefined(c.arrowkeys)?a.$parent.$eval(c.arrowkeys):g.arrowkeys;j&&this.setupArrowkeyEvents(e,f,h),a.readonlyInput=angular.isDefined(c.readonlyInput)?a.$parent.$eval(c.readonlyInput):g.readonlyInput,this.setupInputEvents(e,f,h)};var A=g.hourStep;c.hourStep&&w.push(a.$parent.$watch(d(c.hourStep),function(a){A=+a}));var B=g.minuteStep;c.minuteStep&&w.push(a.$parent.$watch(d(c.minuteStep),function(a){B=+a}));var C;w.push(a.$parent.$watch(d(c.min),function(a){var b=new Date(a);C=isNaN(b)?void 0:b}));var D;w.push(a.$parent.$watch(d(c.max),function(a){var b=new Date(a);D=isNaN(b)?void 0:b}));var E=!1;c.ngDisabled&&w.push(a.$parent.$watch(d(c.ngDisabled),function(a){E=a})),a.noIncrementHours=function(){var a=p(v,60*A);return E||a>D||v>a&&C>a},a.noDecrementHours=function(){var a=p(v,60*-A);return E||C>a||a>v&&a>D},a.noIncrementMinutes=function(){var a=p(v,B);return E||a>D||v>a&&C>a},a.noDecrementMinutes=function(){var a=p(v,-B);return E||C>a||a>v&&a>D},a.noIncrementSeconds=function(){var a=q(v,F);return E||a>D||v>a&&C>a},a.noDecrementSeconds=function(){var a=q(v,-F);return E||C>a||a>v&&a>D},a.noToggleMeridian=function(){return v.getHours()<12?E||p(v,720)>D:E||p(v,-720)0};b.on("mousewheel wheel",function(b){E||a.$apply(e(b)?a.incrementHours():a.decrementHours()),b.preventDefault()}),c.on("mousewheel wheel",function(b){E||a.$apply(e(b)?a.incrementMinutes():a.decrementMinutes()),b.preventDefault()}),d.on("mousewheel wheel",function(b){E||a.$apply(e(b)?a.incrementSeconds():a.decrementSeconds()),b.preventDefault()})},this.setupArrowkeyEvents=function(b,c,d){b.on("keydown",function(b){E||(38===b.which?(b.preventDefault(),a.incrementHours(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementHours(),a.$apply()))}),c.on("keydown",function(b){E||(38===b.which?(b.preventDefault(),a.incrementMinutes(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementMinutes(),a.$apply()))}),d.on("keydown",function(b){E||(38===b.which?(b.preventDefault(),a.incrementSeconds(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementSeconds(),a.$apply()))})},this.setupInputEvents=function(b,c,d){if(a.readonlyInput)return a.updateHours=angular.noop,a.updateMinutes=angular.noop,void(a.updateSeconds=angular.noop);var e=function(b,c,d){x.$setViewValue(null),x.$setValidity("time",!1),angular.isDefined(b)&&(a.invalidHours=b,s&&s.$setValidity("hours",!1)),angular.isDefined(c)&&(a.invalidMinutes=c,t&&t.$setValidity("minutes",!1)),angular.isDefined(d)&&(a.invalidSeconds=d,u&&u.$setValidity("seconds",!1))};a.updateHours=function(){var a=h(),b=i();x.$setDirty(),angular.isDefined(a)&&angular.isDefined(b)?(v.setHours(a),v.setMinutes(b),C>v||v>D?e(!0):l("h")):e(!0)},b.on("blur",function(b){x.$setTouched(),r()?m():null===a.hours||""===a.hours?e(!0):!a.invalidHours&&a.hours<10&&a.$apply(function(){a.hours=k(a.hours,!z)})}),a.updateMinutes=function(){var a=i(),b=h();x.$setDirty(),angular.isDefined(a)&&angular.isDefined(b)?(v.setHours(b),v.setMinutes(a),C>v||v>D?e(void 0,!0):l("m")):e(void 0,!0)},c.on("blur",function(b){x.$setTouched(),r()?m():null===a.minutes?e(void 0,!0):!a.invalidMinutes&&a.minutes<10&&a.$apply(function(){a.minutes=k(a.minutes)})}),a.updateSeconds=function(){var a=j();x.$setDirty(),angular.isDefined(a)?(v.setSeconds(a),l("s")):e(void 0,void 0,!0)},d.on("blur",function(b){r()?m():!a.invalidSeconds&&a.seconds<10&&a.$apply(function(){a.seconds=k(a.seconds)})})},this.render=function(){var b=x.$viewValue;isNaN(b)?(x.$setValidity("time",!1),e.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')):(b&&(v=b),C>v||v>D?(x.$setValidity("time",!1),a.invalidHours=!0,a.invalidMinutes=!0):m(),n())},a.showSpinners=angular.isDefined(c.showSpinners)?a.$parent.$eval(c.showSpinners):g.showSpinners,a.incrementHours=function(){a.noIncrementHours()||o(60*A*60)},a.decrementHours=function(){a.noDecrementHours()||o(60*-A*60)},a.incrementMinutes=function(){a.noIncrementMinutes()||o(60*B)},a.decrementMinutes=function(){a.noDecrementMinutes()||o(60*-B)},a.incrementSeconds=function(){a.noIncrementSeconds()||o(F)},a.decrementSeconds=function(){a.noDecrementSeconds()||o(-F)},a.toggleMeridian=function(){var b=i(),c=h();a.noToggleMeridian()||(angular.isDefined(b)&&angular.isDefined(c)?o(720*(v.getHours()<12?60:-60)):a.meridian=a.meridian===y[0]?y[1]:y[0])},a.blur=function(){x.$setTouched()},a.$on("$destroy",function(){for(;w.length;)w.shift()()})}]).directive("uibTimepicker",["uibTimepickerConfig",function(a){return{require:["uibTimepicker","?^ngModel"],restrict:"A",controller:"UibTimepickerController",controllerAs:"timepicker",scope:{},templateUrl:function(b,c){return c.templateUrl||a.templateUrl},link:function(a,b,c,d){var e=d[0],f=d[1];f&&e.init(f,b.find("input"))}}}]),angular.module("ui.bootstrap.typeahead",["ui.bootstrap.debounce","ui.bootstrap.position"]).factory("uibTypeaheadParser",["$parse",function(a){var b=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;return{parse:function(c){var d=c.match(b);if(!d)throw new Error('Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_" but got "'+c+'".');return{itemName:d[3],source:a(d[4]),viewMapper:a(d[2]||d[1]),modelMapper:a(d[1])}}}}]).controller("UibTypeaheadController",["$scope","$element","$attrs","$compile","$parse","$q","$timeout","$document","$window","$rootScope","$$debounce","$uibPosition","uibTypeaheadParser",function(a,b,c,d,e,f,g,h,i,j,k,l,m){function n(){O.moveInProgress||(O.moveInProgress=!0,O.$digest()),Z()}function o(){O.position=E?l.offset(b):l.position(b),O.position.top+=b.prop("offsetHeight")}var p,q,r=[9,13,27,38,40],s=200,t=a.$eval(c.typeaheadMinLength);t||0===t||(t=1),a.$watch(c.typeaheadMinLength,function(a){t=a||0===a?a:1});var u=a.$eval(c.typeaheadWaitMs)||0,v=a.$eval(c.typeaheadEditable)!==!1;a.$watch(c.typeaheadEditable,function(a){v=a!==!1});var w,x,y=e(c.typeaheadLoading).assign||angular.noop,z=c.typeaheadShouldSelect?e(c.typeaheadShouldSelect):function(a,b){var c=b.$event;return 13===c.which||9===c.which},A=e(c.typeaheadOnSelect),B=angular.isDefined(c.typeaheadSelectOnBlur)?a.$eval(c.typeaheadSelectOnBlur):!1,C=e(c.typeaheadNoResults).assign||angular.noop,D=c.typeaheadInputFormatter?e(c.typeaheadInputFormatter):void 0,E=c.typeaheadAppendToBody?a.$eval(c.typeaheadAppendToBody):!1,F=c.typeaheadAppendTo?a.$eval(c.typeaheadAppendTo):null,G=a.$eval(c.typeaheadFocusFirst)!==!1,H=c.typeaheadSelectOnExact?a.$eval(c.typeaheadSelectOnExact):!1,I=e(c.typeaheadIsOpen).assign||angular.noop,J=a.$eval(c.typeaheadShowHint)||!1,K=e(c.ngModel),L=e(c.ngModel+"($$$p)"),M=function(b,c){return angular.isFunction(K(a))&&q&&q.$options&&q.$options.getterSetter?L(b,{$$$p:c}):K.assign(b,c)},N=m.parse(c.uibTypeahead),O=a.$new(),P=a.$on("$destroy",function(){O.$destroy()});O.$on("$destroy",P);var Q="typeahead-"+O.$id+"-"+Math.floor(1e4*Math.random());b.attr({"aria-autocomplete":"list","aria-expanded":!1,"aria-owns":Q});var R,S;J&&(R=angular.element("
      "),R.css("position","relative"),b.after(R),S=b.clone(),S.attr("placeholder",""),S.attr("tabindex","-1"),S.val(""),S.css({position:"absolute",top:"0px",left:"0px","border-color":"transparent","box-shadow":"none",opacity:1,background:"none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)",color:"#999"}),b.css({position:"relative","vertical-align":"top","background-color":"transparent"}),S.attr("id")&&S.removeAttr("id"),R.append(S),S.after(b));var T=angular.element("
      ");T.attr({id:Q,matches:"matches",active:"activeIdx",select:"select(activeIdx, evt)","move-in-progress":"moveInProgress",query:"query",position:"position","assign-is-open":"assignIsOpen(isOpen)",debounce:"debounceUpdate"}),angular.isDefined(c.typeaheadTemplateUrl)&&T.attr("template-url",c.typeaheadTemplateUrl),angular.isDefined(c.typeaheadPopupTemplateUrl)&&T.attr("popup-template-url",c.typeaheadPopupTemplateUrl);var U=function(){J&&S.val("")},V=function(){O.matches=[],O.activeIdx=-1,b.attr("aria-expanded",!1),U()},W=function(a){return Q+"-option-"+a};O.$watch("activeIdx",function(a){0>a?b.removeAttr("aria-activedescendant"):b.attr("aria-activedescendant",W(a))});var X=function(a,b){return O.matches.length>b&&a?a.toUpperCase()===O.matches[b].label.toUpperCase():!1},Y=function(c,d){var e={$viewValue:c};y(a,!0),C(a,!1),f.when(N.source(a,e)).then(function(f){var g=c===p.$viewValue;if(g&&w)if(f&&f.length>0){O.activeIdx=G?0:-1,C(a,!1),O.matches.length=0;for(var h=0;h0&&i.slice(0,c.length).toUpperCase()===c.toUpperCase()?S.val(c+i.slice(c.length)):S.val("")}}else V(),C(a,!0);g&&y(a,!1)},function(){V(),y(a,!1),C(a,!0)})};E&&(angular.element(i).on("resize",n),h.find("body").on("scroll",n));var Z=k(function(){O.matches.length&&o(),O.moveInProgress=!1},s);O.moveInProgress=!1,O.query=void 0;var $,_=function(a){$=g(function(){Y(a)},u)},aa=function(){$&&g.cancel($)};V(),O.assignIsOpen=function(b){I(a,b)},O.select=function(d,e){var f,h,i={};x=!0,i[N.itemName]=h=O.matches[d].model,f=N.modelMapper(a,i),M(a,f),p.$setValidity("editable",!0),p.$setValidity("parse",!0),A(a,{$item:h,$model:f,$label:N.viewMapper(a,i),$event:e}),V(),O.$eval(c.typeaheadFocusOnSelect)!==!1&&g(function(){b[0].focus()},0,!1)},b.on("keydown",function(b){if(0!==O.matches.length&&-1!==r.indexOf(b.which)){var c=z(a,{$event:b});if(-1===O.activeIdx&&c||9===b.which&&b.shiftKey)return V(),void O.$digest();b.preventDefault();var d;switch(b.which){case 27:b.stopPropagation(),V(),a.$digest();break;case 38:O.activeIdx=(O.activeIdx>0?O.activeIdx:O.matches.length)-1,O.$digest(),d=T[0].querySelectorAll(".uib-typeahead-match")[O.activeIdx],d.parentNode.scrollTop=d.offsetTop;break;case 40:O.activeIdx=(O.activeIdx+1)%O.matches.length,O.$digest(),d=T[0].querySelectorAll(".uib-typeahead-match")[O.activeIdx],d.parentNode.scrollTop=d.offsetTop;break;default:c&&O.$apply(function(){angular.isNumber(O.debounceUpdate)||angular.isObject(O.debounceUpdate)?k(function(){O.select(O.activeIdx,b)},angular.isNumber(O.debounceUpdate)?O.debounceUpdate:O.debounceUpdate["default"]):O.select(O.activeIdx,b)})}}}),b.on("focus",function(a){w=!0,0!==t||p.$viewValue||g(function(){Y(p.$viewValue,a)},0)}),b.on("blur",function(a){B&&O.matches.length&&-1!==O.activeIdx&&!x&&(x=!0,O.$apply(function(){angular.isObject(O.debounceUpdate)&&angular.isNumber(O.debounceUpdate.blur)?k(function(){O.select(O.activeIdx,a)},O.debounceUpdate.blur):O.select(O.activeIdx,a)})),!v&&p.$error.editable&&(p.$setViewValue(),O.$apply(function(){p.$setValidity("editable",!0),p.$setValidity("parse",!0)}),b.val("")),w=!1,x=!1});var ba=function(c){b[0]!==c.target&&3!==c.which&&0!==O.matches.length&&(V(),j.$$phase||a.$digest())};h.on("click",ba),a.$on("$destroy",function(){h.off("click",ba),(E||F)&&ca.remove(),E&&(angular.element(i).off("resize",n),h.find("body").off("scroll",n)),T.remove(),J&&R.remove()});var ca=d(T)(O);E?h.find("body").append(ca):F?angular.element(F).eq(0).append(ca):b.after(ca),this.init=function(b,c){p=b,q=c,O.debounceUpdate=p.$options&&e(p.$options.debounce)(a),p.$parsers.unshift(function(b){return w=!0,0===t||b&&b.length>=t?u>0?(aa(),_(b)):Y(b):(y(a,!1),aa(),V()),v?b:b?void p.$setValidity("editable",!1):(p.$setValidity("editable",!0),null)}),p.$formatters.push(function(b){var c,d,e={};return v||p.$setValidity("editable",!0),D?(e.$model=b,D(a,e)):(e[N.itemName]=b,c=N.viewMapper(a,e),e[N.itemName]=void 0,d=N.viewMapper(a,e),c!==d?c:b)})}}]).directive("uibTypeahead",function(){return{controller:"UibTypeaheadController",require:["ngModel","^?ngModelOptions","uibTypeahead"],link:function(a,b,c,d){d[2].init(d[0],d[1])}}}).directive("uibTypeaheadPopup",["$$debounce",function(a){return{scope:{matches:"=",query:"=",active:"=",position:"&",moveInProgress:"=",select:"&",assignIsOpen:"&",debounce:"&"},replace:!0,templateUrl:function(a,b){return b.popupTemplateUrl||"uib/template/typeahead/typeahead-popup.html"; +},link:function(b,c,d){b.templateUrl=d.templateUrl,b.isOpen=function(){var a=b.matches.length>0;return b.assignIsOpen({isOpen:a}),a},b.isActive=function(a){return b.active===a},b.selectActive=function(a){b.active=a},b.selectMatch=function(c,d){var e=b.debounce();angular.isNumber(e)||angular.isObject(e)?a(function(){b.select({activeIdx:c,evt:d})},angular.isNumber(e)?e:e["default"]):b.select({activeIdx:c,evt:d})}}}}]).directive("uibTypeaheadMatch",["$templateRequest","$compile","$parse",function(a,b,c){return{scope:{index:"=",match:"=",query:"="},link:function(d,e,f){var g=c(f.templateUrl)(d.$parent)||"uib/template/typeahead/typeahead-match.html";a(g).then(function(a){var c=angular.element(a.trim());e.replaceWith(c),b(c)(d)})}}}]).filter("uibTypeaheadHighlight",["$sce","$injector","$log",function(a,b,c){function d(a){return a.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")}function e(a){return/<.*>/g.test(a)}var f;return f=b.has("$sanitize"),function(b,g){return!f&&e(b)&&c.warn("Unsafe use of typeahead please use ngSanitize"),b=g?(""+b).replace(new RegExp(d(g),"gi"),"$&"):b,f||(b=a.trustAsHtml(b)),b}}]),angular.module("ui.bootstrap.carousel").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibCarouselCss&&angular.element(document).find("head").prepend(''),angular.$$uibCarouselCss=!0}),angular.module("ui.bootstrap.datepicker").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibDatepickerCss&&angular.element(document).find("head").prepend(''),angular.$$uibDatepickerCss=!0}),angular.module("ui.bootstrap.position").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibPositionCss&&angular.element(document).find("head").prepend(''),angular.$$uibPositionCss=!0}),angular.module("ui.bootstrap.datepickerPopup").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibDatepickerpopupCss&&angular.element(document).find("head").prepend(''),angular.$$uibDatepickerpopupCss=!0}),angular.module("ui.bootstrap.tooltip").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTooltipCss&&angular.element(document).find("head").prepend(''),angular.$$uibTooltipCss=!0}),angular.module("ui.bootstrap.timepicker").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTimepickerCss&&angular.element(document).find("head").prepend(''),angular.$$uibTimepickerCss=!0}),angular.module("ui.bootstrap.typeahead").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTypeaheadCss&&angular.element(document).find("head").prepend(''),angular.$$uibTypeaheadCss=!0}); \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/.bower.json b/ecomp-portal-FE/client/bower_components/angular-cache/.bower.json new file mode 100644 index 00000000..69505556 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/.bower.json @@ -0,0 +1,45 @@ +{ + "author": "Jason Dobry", + "name": "angular-cache", + "description": "angular-cache is a very useful replacement for Angular's $cacheFactory.", + "homepage": "https://github.com/jmdobry/angular-cache", + "repository": { + "type": "git", + "url": "https://github.com/jmdobry/angular-cache.git" + }, + "main": "./dist/angular-cache.js", + "ignore": [ + ".idea/", + ".*", + "*.iml", + "src/", + "bower_components/", + "coverage/", + "Gruntfile.js", + "package.json", + "CONTRIBUTING.md", + "node_modules/", + "test/", + "karma.conf.js", + "karma.start.js", + "libpeerconnection.log" + ], + "devDependencies": { + "angular-1.2.25": "angular#1.2.25", + "angular-resource": "angular-resource#1.2.25", + "angular-mocks-1.2.25": "angular-mocks#1.2.25" + }, + "dependencies": { + "angular": ">=1.x" + }, + "version": "4.6.0", + "_release": "4.6.0", + "_resolution": { + "type": "version", + "tag": "4.6.0", + "commit": "b491ed8361cd092dd82e59eab0031a2e202900a6" + }, + "_source": "https://github.com/jmdobry/angular-cache.git", + "_target": "~4.6.0", + "_originalSource": "angular-cache" +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/CHANGELOG.md b/ecomp-portal-FE/client/bower_components/angular-cache/CHANGELOG.md new file mode 100644 index 00000000..b5f917d9 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/CHANGELOG.md @@ -0,0 +1,353 @@ +##### 4.6.0 29 April 2016 + +###### Bug fixes +- #215 - angular-cache.min.js tries to load the .map from dist/ +- #204 - Does not work with Angular 1.3 and browserify + +##### 4.5.0 12 January 2016 + +###### Backwards compatible API changes +- #205 - Feature request: Cache.getAllValues + +###### Backwards compatible bug fixes + +- #211 - Expires value gets sometimes automatically set to null +- #212 - Internal Cache not reloaded on Reload + +###### Other + +- Upgraded to CacheFactory v1.4.0 + +##### 4.4.3 30 December 2015 + +- #179 - capacity does not work on often reload +- #210 - Why does angular-cache ignore capacity configuration? + +##### 4.4.1 12 October 2015 + +- #203 - trouble: bundle with r.js not works + +##### 4.4.0 12 October 2015 + +- #200 - 4.3 storagePrefix is not backwards compatible +- #201 - webpack minification error + +##### 4.3.2 10 July 2015 + +- #191 - yabh issue with ie8 + +##### 4.3.1 07 July 2015 + +- #190 - 4.3 breaks phantomJS tests + +##### 4.3.0 06 July 2015 + +- #189 - Extracted non-angular code. + +##### 4.2.2 01 July 2015 + +###### Backwards compatible bug fixes +- #165 - storageMode does not inherits from defaults + +##### 4.2.1 01 July 2015 + +Upgraded dependencies +Better CommonJS interop +Added a number of build examples + +##### 4.2.0 27 April 2015 + +###### Backwards compatible bug fixes +- #174 - Cache not being fully emptied if using localStorage and multiple web pages + +##### 4.1.0 30 March 2015 + +###### Backwards compatible API changes +- #169 - Official support for ngResource + +##### 4.0.2 22 March 2015 + +###### Backwards compatible bug fixes +- #164 - onExpire is still called when cache is empty + +##### 4.0.1 20 March 2015 + +###### Backwards compatible bug fixes +- #163 - Configuring CacheOption storagePrefix results in "true.{key}" + +##### 4.0.0 15 March 2015 + +###### Breaking API changes +- Completely disassociated angular-cache from the deprecated angular-data (angular-data has been replaced by js-data + js-data-angular) +- Angular module renamed to _angular-cache_ +- _DSCacheFactory_ renamed to _CacheFactory_ +- _DSBinaryHeap_ renamed to _BinaryHeap_ +- Removed `DSCacheFactoryProvider.setCacheDefaults`. You now do `angular.extend(CacheFactoryProvider.defaults, { ... });` +- No longer exposing a `DSCache` constructor function (as it no longer exists) +- `storageMode` can now be set dynamically, which will remove all items from current storage and insert them into the new storage + +###### Other +- Fixes #161 +- Converted to ES6 and a webpack build with better umd support +- Now exporting the module name _angular-cache_ (when you do `require('angular-cache')` you get `"angular-cache"`) +- Deprecating angular-cache < 4.0.0 + +##### 3.2.5 02 February 2015 + +###### Backwards compatible bug fixes +- #152 - Expired items sometimes only expire after double time. +- #153 - Missing angular dependency in bower.json + +##### 3.2.4 17 December 2014 + +###### Backwards compatible bug fixes +- #149 - when removing an object from localStorage the key didn't get removed if the passed parameter is of number type. + +##### 3.2.3 13 December 2014 + +###### Backwards compatible bug fixes +- #112 - $resource cache and 3.0.0-beta-x +- #122 - Error using DSCacheFactory with $http/ $resource and localStorage +- #148 - Illegal operation when using local-/sessionStorage + +##### 3.2.2 24 November 2014 + +###### Backwards compatible bug fixes +- #147 - `storeOnResolve` and `storeOnReject` should default to `false` + +##### 3.2.1 10 November 2014 + +###### Backwards compatible bug fixes +- #142 - Use JSON.stringify instead of angular.toJson + +##### 3.2.0 07 November 2014 + +###### Backwards compatible API changes +- #135 - Closes #135. (Improved handling of promises.) + +##### 3.1.1 28 August 2014 + +###### Backwards compatible bug fixes +- #124 - DSCache.info does not work if the storageMode is localStorage. +- #127 - requirejs conflict, require object overwritten + +##### 3.1.0 15 July 2014 + +###### Backwards compatible API changes +- #117 - call to DSCacheFactory(...) produces JSHint warning (Added DSCacheFactory.createCache method) + +###### Backwards compatible bug fixes +- #118 - dist/angular-cache.js doesn't end with a semicolon (Upgraded dependencies) +- #120 - How come the non minified version has minified code? (Upgraded dependencies) + +##### 3.0.3 16 June 2014 + +###### Backwards compatible bug fixes +- Angular 1.2.18 with $http/localStorage #116 + +##### 3.0.2 15 June 2014 + +###### Backwards compatible bug fixes +- $http w/ cache is trying to store a promise, which dies on JSON.stringify #115 + +##### 3.0.1 14 June 2014 + +###### Backwards compatible bug fixes +- Added polyfill for `$$minErr`. + +##### 3.0.0 14 June 2014 + +3.0.0 Release + +##### 3.0.0-beta.4 22 April 2014 + +###### Backwards compatible API changes +- Add feature to 'touch' elements in the cache #103 + +###### Backwards compatible bug fixes +- `localstorage` and Safari Private Browsing #107 + +##### 3.0.0-beta.3 03 March 2014 + +###### Backwards compatible bug fixes +- Fixed duplicate keys when using localStorage #106 + +##### 3.0.0-beta.2 25 February 2014 + +###### Backwards compatible bug fixes +- Fixed missing reference to DSBinaryHeap #105 + +##### 3.0.0-beta.1 24 February 2014 + +###### Breaking API changes +- `maxAge` and `deleteOnExpire` are no longer overridable for individual items +- Renamed angular module to `angular-data.DSCacheFactory`. Angular-cache is now part of the `angular-data` namespace +- The `verifyIntegrity` option has been completely removed due to a cache being exclusively in-memory OR in web storage #96 +- Supported values for the `storageMode` option are now: `"memory"`, `"localStorage"` or `"sessionStorage"` with the default being `"memory"` +- `DSCache#put(key, value)` no longer accepts a third `options` argument +- `DSCache#removeExpired()` no longer accepts an `options` argument and thus no longer supports returning removed expired items as an array +- `DSCache#remove(key)` no longer accepts an `options` argument +- `DSCache#setOptions(options[, strict])` no longer accepts `storageMode` and `storageImpl` as part of the `options` argument +- `storageMode` is no longer dynamically configurable +- `storageImpl` is no longer dynamically configurable + +###### Backwards compatible API changes +- Added `DSCache#enable()` +- Added `DSCache#disable()` +- Added `DSCache#setCapacity(capacity)` +- Added `DSCache#setMaxAge(maxAge)` +- Added `DSCache#setCacheFlushInterval(cacheFlushInterval)` +- Added `DSCache#setRecycleFreq(recycleFreq)` +- Added `DSCache#setDeleteOnExpire(deleteOnExpire)` +- Added `DSCache#setOnExpire(onExpire)` +- Added option `storagePrefix` for customizing the prefix used in `localStorage`, etc. #98 +- Refactored to be in-memory OR webStorage, never both #96 + +###### Other +- I might have missed something... + +##### 2.3.3 - 24 February 2014 + +###### Backwards compatible bug fixes +- *sigh Fixed #102 (regression from #100) + +##### 2.3.2 - 23 February 2014 + +###### Backwards compatible bug fixes +- Fixed #100 (regression from #89) + +##### 2.3.1 - 19 February 2014 + +###### Backwards compatible bug fixes +- Fixed #89 + +##### 2.3.0 - 09 January 2014 +- Caches can now be disabled #82 +- The `options` object (`$angularCacheFactory()`, `AngularCache#setOptions()`, and `$angularCacheFactoryProvider.setCacheDefaults()`) now accepts a `disabled` field, which can be set to `true` and defaults to `false`. +- `$angularCacheFactory.enableAll()` will enable any disabled caches. +- `$angularCacheFactory.disableAll()` will disable all caches. +- A disabled cache will operate as normal, except `AngularCache#get()` and `AngularCache#put()` will both immediately return `undefined` instead of performing their normal functions. + +###### Backwards compatible API changes +- `removeExpired()` now returns an object (or array) of the removed items. + +###### Backwards compatible bug fixes +- `removeExpired()` now removes _all_ expired items. + +##### 2.2.0 - 15 December 2013 + +###### Backwards compatible API changes +- `removeExpired()` now returns an object (or array) of the removed items. + +###### Backwards compatible bug fixes +- `removeExpired()` now removes _all_ expired items. + +##### 2.1.1 - 20 November 2013 + +###### Backwards compatible bug fixes +- Allow number keys, but stringify them #76 +- Fix "Uncaught TypeError: Cannot read property 'maxAge' of null" #77 (thanks @evngeny-o) + +##### 2.1.0 - 03 November 2013 + +###### Backwards compatible API changes +- Modify .get(key, options) to accept multiple keys #71 (thanks @roryf) + +###### Other +- Run tests against multiple versions of Angular.js #72 +- Add banner to dist/angular-cache.min.js #68 + +##### 2.0.0 - 30 October 2013 +- Not all methods of AngularCache and $angularCacheFactory are in README #61 +- Fix demo to work with 2.0.0-rc.1 #62 +- Using Bower to install this package, the dist filenames change per version? #63 + +##### 2.0.0-rc.1 - 14 October 2013 + +###### Breaking API changes +- Swapped `aggressiveDelete` option for `deleteOnExpire` option. #30, #47 +- Changed `$angularCacheFactory.info()` to return an object similar to `AngularCache.info()` #45 +- Namespaced angular-cache module under `jmdobry` so it is now "jmdobry.angular-cache". #42 +- Substituted `storageImpl` and `sessionStorageImpl` options for just `storageImpl` option. + +###### Backwards compatible API changes +- Added `recycleFreq` to specify how frequently to check for expired items (no more $timeout). #28, #57 +- Added ability to set global cache defaults in $angularCacheFactoryProvider. #55 + +###### Backwards compatible bug fixes +- cacheFlushInterval doesn't clear web storage when storageMode is used. #52 +- AngularCache#info(key) should return 'undefined' if the key isn't in the cache #53 +- Fixed timespan issues in README.md. #59 + +###### Other +- Refactored angular-cache `setOptions()` internals to be less convoluted and to have better validation. #46 +- Re-wrote documentation to be clearer and more organized. #56 +- Fixed documentation where time spans were incorrectly labeled. #59 + +##### 1.2.0 - 20 September 2013 + +###### Backwards compatible API changes +- Added AngularCache#info(key) #43 + +###### Backwards compatible bug fixes +- Fixed #39, #44, #49, #50 + +##### 1.1.0 - 03 September 2013 + +###### Backwards compatible API changes +- Added `onExpire` callback hook #27 +- Added `$angularCacheFactory.removeAll()` and `$angularCacheFactory.clearAll()` convenience methods #37, #38 + +###### Backwards compatible bug fixes +- Fixed #36 + +##### 1.0.0 - 25 August 2013 +- Closed #31 (Improved documentation) +- Closed #32 + +##### 1.0.0-rc.1 - 21 August 2013 +- Added localStorage feature #26, #29 + +##### 0.9.1 - 03 August 2013 +- Fixed #25 + +##### 0.9.0 - 03 August 2013 +- Added a changelog #13 +- Added documentation for installing with bower +- Added ability to set option `aggressiveDelete` when creating cache and when adding items +- Cleaned up README.md +- Switched the demo to use Bootstrap 3 + +##### 0.8.2 - 09 July 2013 +- Added CONTRIBUTING.md #22 +- Cleaned up meta data in bower.json and package.json + +##### 0.8.1 - 09 July 2013 +- Added .jshintrc +- Cleaned up the docs a bit +- `bower.json` now uses `src/angular-cache.js` instead of the versioned output files #21 +- From now on the tags for the project will be named using [semver](http://semver.org/) + +##### 0.8.0 - 08 July 2013 +- Added `AngularCache.setOptions()`, the ability to dynamically change the configuration of a cache #20 +- Added `AngularCache.keys()`, which returns an array of the keys in a cache #19 +- Added `AngularCache.keySet()`, which returns a hash of the keys in a cache #19 + +##### 0.7.2 - June 2013 +- Added `angular-cache` to bower registry #7 +- Created a working demo #9 #17 +- Fixed the size not being reset to 0 when the cache clears itself #14 #16 +- Added `$angularCacheFactory.keys()`, which returns an array of the keys (the names of the caches) in $angularCacheFactory #18 +- Added `$angularCacheFactory.keySet()`, which returns a hash of the keys (the names of the caches) in $angularCacheFactory #18 + +##### 0.6.1 - June 2013 +- Got the project building on TravisCI +- Renamed the project to `angular-cache` #5 + +##### 0.5.0 - June 2013 +- Added a roadmap to README.md #4 +- Clarify usage documentation #3 +- Wrote unit tests #2 + +##### 0.4.0 - May 2013 +- Added Grunt build tasks #1 diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/LICENSE b/ecomp-portal-FE/client/bower_components/angular-cache/LICENSE new file mode 100644 index 00000000..ab9faee3 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/LICENSE @@ -0,0 +1,20 @@ +The MIT License + +Copyright (C) 2013-2016 angular-cache project authors + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/README.md b/ecomp-portal-FE/client/bower_components/angular-cache/README.md new file mode 100644 index 00000000..a24a57b7 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/README.md @@ -0,0 +1,671 @@ +# angular-cache + +[![bower version](https://img.shields.io/bower/v/angular-cache.svg?style=flat)](https://www.npmjs.org/package/angular-cache) +[![npm version](https://img.shields.io/npm/v/angular-cache.svg?style=flat)](https://www.npmjs.org/package/angular-cache) +[![Circle CI](https://img.shields.io/circleci/project/jmdobry/angular-cache/master.svg?style=flat)](https://circleci.com/gh/jmdobry/angular-cache/tree/master) +[![npm downloads](https://img.shields.io/npm/dm/angular-cache.svg?style=flat)](https://www.npmjs.org/package/angular-cache) +[![codecov](https://img.shields.io/codecov/c/github/jmdobry/angular-cache.svg)](https://codecov.io/gh/jmdobry/angular-cache) + +A very useful replacement for Angular's $cacheFactory. + +__versions of angular-cache below 4.0.0 have been deprecated, see the [breaking changes](https://github.com/jmdobry/angular-cache/blob/master/CHANGELOG.md) in 4.0.0__ + +### Table of Contents +- [Quick Start](#quick-start) +- [The Basics](#the-basics) +- [Working with a cache](#working-with-a-cache) +- [Configuration Options](#configuration-options) +- [Using angular-cache with $http](#using-angular-cache-with-http) +- [Using angular-cache with localStorage](#using-angular-cache-with-localstorage) +- [API Reference](#api-reference) + +### Quick Start +`bower install --save angular-cache` or `npm install --save angular-cache`. + +```js +angular.module('myApp', ['angular-cache']) + .config(function (CacheFactoryProvider) { + angular.extend(CacheFactoryProvider.defaults, { maxAge: 15 * 60 * 1000 }); + }) + .service('BookService', function (CacheFactory, $http) { + if (!CacheFactory.get('bookCache')) { + // or CacheFactory('bookCache', { ... }); + CacheFactory.createCache('bookCache', { + deleteOnExpire: 'aggressive', + recycleFreq: 60000 + }); + } + + var bookCache = CacheFactory.get('bookCache'); + + return { + findBookById: function (id) { + return $http.get('/api/books/' + id, { cache: bookCache }); + } + }; + }); +``` + +#### Why would you want to replace $cacheFactory? + +| Feature | $cacheFactory | angular-cache | +| ----------- | ------------------- | --------------- | +| core implementation usable outside of Angular | no | [__yes__](https://github.com/jmdobry/CacheFactory) | +| key/value store | __yes__ | __yes__ | +| in-memory support | __yes__ | __yes__ | +| localStorage support | no | __yes__ | +| sessionStorage support | no | __yes__ | +| custom storage support | no | __yes__ | +| can set maximum capacity | __yes__ | __yes__ | +| LRU support | __yes__ | __yes__ | +| support for time-based expiration of items | no | __yes__ | +| remove all expired items | n/a | __yes__ | +| "touch" an item | n/a | __yes__ | +| execute callback when an item expires | n/a | __yes__ | +| get info about cache | __yes__ | __yes__ | +| get info about single item | no | __yes__ | +| get item by key | __yes__ | __yes__ | +| remove item by key | __yes__ | __yes__ | +| remove all items | __yes__ | __yes__ | +| get keys of all caches | no | __yes__ | +| clear all caches | no | __yes__ | +| disable/enable a cache | no | __yes__ | +| "touch" all items in all caches | n/a | __yes__ | +| remove expired items from all caches | n/a | __yes__ | +| get all keys in a cache | no | __yes__ | +| clear a cache on a given interval | no | __yes__ | + +### The Basics + +First, inject `CacheFactory` then create a cache. Let's go: + +```js +app.service('myService', function (CacheFactory) { + var profileCache; + + // Check to make sure the cache doesn't already exist + if (!CacheFactory.get('profileCache')) { + profileCache = CacheFactory('profileCache'); + } +}); +``` + +Let's add some items to the cache: + +```js +profileCache.put('/profiles/34', { + name: 'John', + skills: ['programming', 'piano'] +}); + +profileCache.put('/profiles/22', { + name: 'Sally', + skills: ['marketing', 'climbing', 'painting'] +}); +``` + +Right now, these items will stay in the cache until a page refresh. + +Let's have items which are added to `profileCache` expire after an hour: + +```js +profileCache = CacheFactory('profileCache', { + maxAge: 60 * 60 * 1000 // 1 hour +}); +``` + +Perfect. Say we also want the items removed from the cache when they expire: + +```js +profileCache = CacheFactory('profileCache', { + maxAge: 60 * 60 * 1000 // 1 hour, + deleteOnExpire: 'aggressive' +}); +``` + +Let's say that when the items do expire, we want to refresh them with new values: + +```js +profileCache = CacheFactory('profileCache', { + maxAge: 60 * 60 * 1000 // 1 hour, + deleteOnExpire: 'aggressive', + onExpire: function (key, value) { + $http.get(key).success(function (data) { + profileCache.put(key, data); + }); + } +}); +``` + +Or say we want all of our caches to use that configuration as their default: + +```js +angular.module('app', ['angular-cache']).config(function (CacheFactoryProvider) { + angular.extend(CacheFactoryProvider.defaults, { + maxAge: 3600000, + deleteOnExpire: 'aggressive', + onExpire: function (key, value) { + var _this = this; // "this" is the cache in which the item expired + angular.injector(['ng']).get('$http').get(key).success(function (data) { + _this.put(key, data); + }); + } + }); +}); +``` + +### Working with a cache + +We can retrieve items from a cache like so: + +```js +var profile = profileCache.get('/profiles/34'); + +profile.name; // 'John' +``` + +And get information about items in the cache: + +```js +var info = profileCache.info('/profiles/34'); + +info.isExpired; // false +// etc. +``` + +and information about the cache itself: + +``` +var info = profileCache.info(); + +info.size; // 2 +info.maxAge; // 3600000 +info.deleteOnExpire; // 'aggressive' +// etc. +``` + +Items are easily removed, and we can destroy our cache when we're done with it: + +```js +profileCache.remove('/profiles/34'); + +profileCache.get('/profiles/34'); // undefined + +profileCache.destroy(); + +CacheFactory.get('profileCache'); // undefined +``` + +### Configuration Options + +These options apply to: + +- `CacheFactory(cacheId[, options)` +- `CacheFactory.createCache(cacheId[, options])` +- `Cache#setOptions(options[, strict])` +- `Cache#setMaxAge(maxAge)`, `Cache#setOnExpire(onExpire)`, etc. + +##### `cacheFlushInterval` + +If set, remove all items from a cache on an interval after the given number of milliseconds. Default: `null`. + +##### `capacity` + +Maximum number of items a cache can hold. Adding more items than the capacity will cause the cache to operate like an LRU cache, removing the least recently used items to stay under capacity. Default: `Number.MAX_VALUE`. + +##### `deleteOnExpire` + +Determines the behavior of a cache when an item expires. Default: `none`. + +Possible values: + +- `none` - Cache will do nothing when an item expires. +- `passive` - Cache will do nothing when an item expires. Expired items will remain in the cache until requested, at which point they are removed, and `undefined` is returned. +- `aggressive` - Cache will remove expired items as soon as they are discovered. + +##### `disabled` + +Determines whether a cache is disabled. Default: `false`. + +##### `onExpire` + +A callback function to be executed whenever an expired item is removed from a cache when the cache is in `passive` or `aggressive` mode. Will be passed the `key` and `value` of the expired item. + +Will be passed a third `done` argument if the cache is in `passive` mode. This allows you to synchronously access the `key` and `value` of the expired item when you make the `Cache#get(key[, options])` call that is the reason the expired item is being removed in the first place. Default: `null`. + +##### `maxAge` + +The number of milliseconds until a newly inserted item expires. Default: `Number.MAX_VALUE`. + +##### `recycleFreq` + +Determines how often a cache will scan for expired items when in `aggressive` mode. Default: `1000` (milliseconds). + +##### `storageImpl` + +Provide a custom storage medium, e.g. a polyfill for `localStorage`. Default: `null`. + +Must implement: + +- `setItem` - Same API as `localStorage.setItem(key, value)` +- `getItem` - Same API as `localStorage.getItem(key)` +- `removeItem` - Same API as `localStorage.removeItem(key)` + +##### `storageMode` + +Determines the storage medium used by a cache. Default: `memory`. + +Possible values: + +- `memory` - Cache will hold data in memory. Data is cleared when the page is refreshed. +- `localStorage` - Cache will hold data in `localStorage` if available. Data is _not_ cleared when the page is refreshed. +- `sessionStorage` - Cache will hold data in `sessionStorage` if available. Data is _not_ cleared when the page is refreshed. + +##### `storagePrefix` + +Determines the namespace of a cache when `storageMode` is set to `localStorage` or `sessionStorage`. Make it a shorter string to save space. Default: `angular-cache.caches.`. + +##### `storeOnReject` + +If inserting a promise into a cache, also insert the rejection value if the promise rejects. Default: `false`. + +##### storeOnResolve + +If inserting a promise into a cache, also insert the resolved value if the promise resolves. Default: `false`. + +### Using angular-cache with $http + +__Note:__ The downside of letting `$http` handle caching for you is that it caches the responses (in string form) to your requests–not the JavaScript Object parsed from the response body. This means you can't interact with the data in the cache used by `$http`. See below for how to handle the caching yourself, which gives you more control and the ability to interact with the cache (use it as a data store). + +Configure `$http` to use a cache created by `CacheFactory` by default: + +```js +app.run(function ($http, CacheFactory) { + $http.defaults.cache = CacheFactory('defaultCache', { + maxAge: 15 * 60 * 1000, // Items added to this cache expire after 15 minutes + cacheFlushInterval: 60 * 60 * 1000, // This cache will clear itself every hour + deleteOnExpire: 'aggressive' // Items will be deleted from this cache when they expire + }); +}); +``` + +```js +app.service('MyService', function ($http, $q) { + return { + getDataById: function (id) { + var deferred = $q.defer(); + var start = new Date().getTime(); + + $http.get('api/data/' + id, { + cache: true + }).success(function (data) { + console.log('time taken for request: ' + (new Date().getTime() - start) + 'ms'); + deferred.resolve(data); + }); + return deferred.promise; + } + }; +}); +``` + +```js +app.controller('myCtrl', function (MyService) { + MyService.getDataById(1).then(function (data) { + // e.g. "time taken for request: 2375ms" + // Data returned by this next call is already cached. + return MyService.getDataById(1).then(function (data) { + // e.g. "time taken for request: 1ms" + }); + }); +}); +``` + +Tell $http to use a cache created by CacheFactory for a specific request: + +```js +app.service('MyService', function ($q, $http, CacheFactory) { + + CacheFactory('dataCache', { + maxAge: 15 * 60 * 1000, // Items added to this cache expire after 15 minutes + cacheFlushInterval: 60 * 60 * 1000, // This cache will clear itself every hour + deleteOnExpire: 'aggressive' // Items will be deleted from this cache when they expire + }); + + return { + getDataById: function (id) { + var deferred = $q.defer(); + var start = new Date().getTime(); + + $http.get('api/data/' + id, { + cache: CacheFactory.get('dataCache') + }).success(function (data) { + console.log('time taken for request: ' + (new Date().getTime() - start) + 'ms'); + deferred.resolve(data); + }); + return deferred.promise; + } + }; +}); +``` + +```js +app.controller('myCtrl', function (MyService) { + MyService.getDataById(1).then(function (data) { + // e.g. "time taken for request: 2375ms" + // Data returned by this next call is already cached. + return MyService.getDataById(1).then(function (data) { + // e.g. "time taken for request: 1ms" + }); + }); +}); +``` + +Do your own caching while using the $http service: + +```js +app.service('MyService', function ($q, $http, CacheFactory) { + + CacheFactory('dataCache', { + maxAge: 15 * 60 * 1000, // Items added to this cache expire after 15 minutes + cacheFlushInterval: 60 * 60 * 1000, // This cache will clear itself every hour + deleteOnExpire: 'aggressive' // Items will be deleted from this cache when they expire + }); + + return { + getDataById: function (id) { + var deferred = $q.defer(); + var start = new Date().getTime(); + var dataCache = CacheFactory.get('dataCache'); + + // Now that control of inserting/removing from the cache is in our hands, + // we can interact with the data in "dataCache" outside of this context, + // e.g. Modify the data after it has been returned from the server and + // save those modifications to the cache. + if (dataCache.get(id)) { + deferred.resolve(dataCache.get(id)); + } else { + $http.get('api/data/' + id).success(function (data) { + console.log('time taken for request: ' + (new Date().getTime() - start) + 'ms'); + dataCache.put(id, data); + deferred.resolve(data); + }); + } + return deferred.promise; + } + }; +}); +``` + +```js +app.controller('myCtrl', function (MyService) { + MyService.getDataById(1).then(function (data) { + // e.g. "time taken for request: 2375ms" + // Data returned by this next call is already cached. + return MyService.getDataById(1).then(function (data) { + // e.g. "time taken for request: 1ms" + }); + }); +}); +``` + +### Using angular-cache with localStorage + +```js +app.service('myService', function (CacheFactory) { + + // This cache will sync itself with localStorage if it exists, otherwise it won't. Every time the + // browser loads this app, this cache will attempt to initialize itself with any data it had + // already saved to localStorage (or sessionStorage if you used that). + var myAwesomeCache = CacheFactory('myAwesomeCache', { + maxAge: 15 * 60 * 1000, // Items added to this cache expire after 15 minutes. + cacheFlushInterval: 60 * 60 * 1000, // This cache will clear itself every hour. + deleteOnExpire: 'aggressive', // Items will be deleted from this cache right when they expire. + storageMode: 'localStorage' // This cache will use `localStorage`. + }); +}); +``` + +Using angular-cache in browsers that DON'T support localStorage: + +Option 1 - Do nothing (the cache will just store data in memory) + +Option 2 - Create/use a polyfill that provides the global `localStorage` and `sessionStorage` objects. angular-cache will attempt to use these if it finds them. + +Option 3 - Tell angular-cache exactly which polyfill to use (also useful if you just want to use your own implementation/wrapper for localStorage): + +```js +app.service('myService', function (CacheFactory) { + + var localStoragePolyfill = { + getItem: function (key) { ... }, + setItem: function (key, value) { ... }, + removeItem: function (key) { ... } + }; + + // Always use the polyfill + var myAwesomeCache = CacheFactory('myAwesomeCache', { + maxAge: 15 * 60 * 1000, // Items added to this cache expire after 15 minutes. + cacheFlushInterval: 60 * 60 * 1000, // This cache will clear itself every hour. + deleteOnExpire: 'aggressive', // Items will be deleted from this cache right when they expire. + storageMode: 'localStorage', // This cache will use `localStorage`. + storageImpl: localStoragePolyfill // angular-cache will use this polyfill instead of looking for localStorage + }); + + // Conditionally use the polyfill + var options = { + maxAge: 15 * 60 * 1000, // Items added to this cache expire after 15 minutes. + cacheFlushInterval: 60 * 60 * 1000, // This cache will clear itself every hour. + deleteOnExpire: 'aggressive', // Items will be deleted from this cache right when they expire. + storageMode: 'localStorage' // This cache will use `localStorage`. + }; + if (!window.localStorage) { + options.storageImpl = localStoragePolyfill; + } + var myAwesomeCache = CacheFactory('myAwesomeCache', options); +}); +``` + +Documentation on the interface that must be implemented by any storageImpl polyfill used by angular-cache can be found on the W3C Recommendation page for webstorage. The interface itself looks like: + +``` +interface Storage { + readonly attribute unsigned long length; + DOMString? key(unsigned long index); + getter DOMString getItem(DOMString key); + setter creator void setItem(DOMString key, DOMString value); + deleter void removeItem(DOMString key); + void clear(); +}; +``` + +angular-cache cares only about these three methods: + +- `setItem` +- `getItem` +- `removeItem` + +One developer suggested using store.js–a wrapper and polyfill for localStorage. However, store.js has its own API that doesn't match that of the webstorage spec, so if you want to use store.js or any other 3rd-party polyfill then you'll need to create a wrapper for it if it doesn't have the same API as localStorage . For example: + +```js +var storeJsToStandard { + getItem: store.get, + setItem: store.set, + removeItem: store.remove +}; + +CacheFactory('myNewCache', { + storageMode: 'localStorage', + storageImpl: storeJsToStandard +}); +``` + +### API Reference + +##### `CacheFactory(cacheId[, options])` & `CacheFactory.createCache(cacheId[, options])` + +Create a cache. Cache must not already exist. `cacheId` must be a string. `options` is an optional argument and must be an object. Any options you pass here will override any default options. + +```js +var cache = CacheFactory('cache'); +var cache2 = CacheFactory.createCache('cache2'); +var cache3 = CacheFactory('cache', { maxAge: 900000 }); +var cache4 = CacheFactory('cache'); // Error "cache already exists!" +``` + +##### `CacheFactory.get(cacheId)` + +Return the cache with the given `cacheId`. + +##### `CacheFactory.info()` + +Return an object of key-value pairs, the keys being cache ids and the values being the result of `.info()` being called on each cache. + +##### `CacheFactory.keySet()` + +Return the ids of all registered caches as an object. + +##### `CacheFactory.keys()` + +Return the ids of all registered caches as an array. + +##### `CacheFactory.destroy(cacheId)` + +Destroy the cache with the given `cacheId`. + +##### `CacheFactory.destroyAll()` + +Destroy all registered caches. + +##### `CacheFactory.clearAll()` + +Remove all data from all registered caches. + +##### `CacheFactory.enableAll()` + +Enable all registered caches. + +##### `CacheFactory.disableAll()` + +Disable all registered caches. + +##### `CacheFactory.touchAll()` + +Call `.touch()` on all registered caches. + +##### `CacheFactory.removeExpiredFromAll()` + +Call `.removeExpired()` on all registered caches. Returns a hash of any expired items, keyed by cache id. + +##### `Cache#get(key[, options])` + +Return the item with the given `key`. `options`, if provided, must be an object. + +If the cache is in `passive` mode, then `options.onExpire` can be a function that will be called with the `key` and `value` of the requested item if the requested item is expired, with the `get` call itself returning undefined. + +##### `Cache#put(key, value[, options])` + +Insert the item with the given `key` and `value` into the cache. `options`, if provided, must be an object. + +If inserting a promise, `options.storeOnReject` determines whether to insert the rejection value if the promise rejects (overriding the default `storeOnReject` setting for the cache). +If inserting a promise, `options.storeOnResolve` determines whether to insert the resolved value if the promise resolves (overriding the default `storeOnResolve` setting for the cache). + +##### `Cache.remove(key)` + +Remove and return the item with the given `key`, if it is in the cache. + +##### `Cache.removeAll()` + +Remove all items in the cache. + +##### `Cache.removeExpired()` + +Remove and return all expired items in the cache. + +##### `Cache.destroy()` + +Completely destroy this cache and its data. + +##### `Cache#info([key])` + +`Cache#info()` returns an object containing information about the cache. + +`Cache#info(key)` returns an object containing information about the item with the given `key`, if the item is in the cache. + +##### `Cache#keySet()` + +Return the keys of all items in the cache as an object. + +##### `Cache#keys()` + +Return the keys of all items in the cache as an array. + +##### `Cache#enable()` + +Enable the cache. + +##### `Cache#disable()` + +Disable the cache. + +##### `Cache#values()` + +Return all values in the cache. + +##### `Cache#touch([key])` + +`Cache#touch()` will "touch" all items in the cache. +`Cache#touch(key)` will "touch" the item with the given `key`. + +##### `Cache#setCacheFlushInterval(cacheFlushInterval)` + +Set the `cacheFlushInterval` for the cache. + +##### `Cache#setCapacity(capacity)` + +Set the `capacity` for the cache. Setting this lower than the current item count will result in those items being removed. + +##### `Cache#setDeleteOnExpire(deleteOnExpire)` + +Set the `deleteOnExpire` for the cache. + +##### `Cache#setMaxAge(maxAge)` + +Set the `maxAge` for the cache. + +##### `Cache#setOnExpire(onExpire)` + +Set the `onExpire` for the cache. + +##### `Cache#setRecycleFreq(recycleFreq)` + +Set the `recycleFreq` for the cache. + +##### `Cache#setStorageMode(storageMode)` + +Set the `storageMode` for the cache. This will move data from the current storage medium to the new one. + +##### `Cache#setOptions(options[, strict])` + +Set multiple options for the cache at a time. Setting `strict` to `true` will reset options for the cache that are not specifically set in the `options` hash to `CacheFactoryProvider.defaults`. + +### License +[MIT License](https://github.com/jmdobry/angular-cache/blob/master/LICENSE) + +Copyright (C) 2013-2016 angular-cache project authors + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/TRANSITION.md b/ecomp-portal-FE/client/bower_components/angular-cache/TRANSITION.md new file mode 100644 index 00000000..0c0920dc --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/TRANSITION.md @@ -0,0 +1,98 @@ +### 2.x.x. ---> 3.x.x - xx April 2013 +TODO + +### 1.x.x. ---> 2.0.0 - 30 October 2013 + +#### Breaking API changes +##### Swapped `aggressiveDelete` option for `deleteOnExpire` option. + +###### 1.x.x +Aggressively delete expiring items. +```javascript +$angularCacheFactory('myNewCache', { + maxAge: 90000, // Items added to this cache expire after 15 minutes + aggressiveDelete: true // Items will be actively deleted when they expire +}); +``` + +Passively delete items when they are requested after they have expired. +```javascript +$angularCacheFactory('myNewCache', { + maxAge: 90000, // Items added to this cache expire after 15 minutes + aggressiveDelete: false // Items will be actively deleted when they expire +}); +``` + +###### 2.0.0 +Aggressively delete expiring items. +```javascript +$angularCacheFactory('myNewCache', { + maxAge: 90000, // Items added to this cache expire after 15 minutes + deleteOnExpire: 'aggressive' // Items will be actively deleted when they expire +}); +``` + +Passively delete items when they are requested after they have expired. +```javascript +$angularCacheFactory('myNewCache', { + maxAge: 90000, // Items added to this cache expire after 15 minutes + deleteOnExpire: 'passive' // Items will be passively deleted when requested after expiration +}); +``` + +Do nothing with expired items (not in 1.x.x). +```javascript +$angularCacheFactory('myNewCache', { + maxAge: 90000, // Items added to this cache expire after 15 minutes + deleteOnExpire: 'none' // Items will expire but not be removed +}); +``` + +##### Substituted `localStorageImpl` and `sessionStorageImpl` options for just `storageImpl` option. + +###### 1.x.x +```javascript +$angularCacheFactory('myNewCache', { + storageMode: 'localStorage', + localStorageImpl: myLocalStoragePolyfill // Use custom localStorage implementation +}); + +$angularCacheFactory('myNewCache2', { + storageMode: 'sessionStorage', + sessionStorageImpl: mySessionStoragePolyfill // Use custom sessionStorage implementation +}); +``` + +###### 2.0.0 +```javascript +$angularCacheFactory('myNewCache', { + storageMode: 'localStorage', + storageImpl: myLocalStoragePolyfill // Use custom localStorage implementation +}); + +$angularCacheFactory('myNewCache2', { + storageMode: 'sessionStorage', + storageImpl: mySessionStoragePolyfill // Use custom sessionStorage implementation +}); +``` + +##### Installation +The Bower package now contains only `dist/angular-cache.js` and `dist/angular-cache.min.js`. + +##### onExpire + +###### 1.x.x +```javascript +cache.get('someKey', function (key, value) { + // do something with expired item +}); +``` + +###### 2.0.0 +```javascript +cache.get('someKey', { + onExpire: function (key, value) { + // do something with expired item + } +}); +``` diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/bower.json b/ecomp-portal-FE/client/bower_components/angular-cache/bower.json new file mode 100644 index 00000000..18882a7b --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/bower.json @@ -0,0 +1,35 @@ +{ + "author": "Jason Dobry", + "name": "angular-cache", + "description": "angular-cache is a very useful replacement for Angular's $cacheFactory.", + "homepage": "https://github.com/jmdobry/angular-cache", + "repository": { + "type": "git", + "url": "https://github.com/jmdobry/angular-cache.git" + }, + "main": "./dist/angular-cache.js", + "ignore": [ + ".idea/", + ".*", + "*.iml", + "src/", + "bower_components/", + "coverage/", + "Gruntfile.js", + "package.json", + "CONTRIBUTING.md", + "node_modules/", + "test/", + "karma.conf.js", + "karma.start.js", + "libpeerconnection.log" + ], + "devDependencies": { + "angular-1.2.25": "angular#1.2.25", + "angular-resource": "angular-resource#1.2.25", + "angular-mocks-1.2.25": "angular-mocks#1.2.25" + }, + "dependencies": { + "angular": ">=1.x" + } +} diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/browserify/README.md b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/browserify/README.md new file mode 100644 index 00000000..0d762e6d --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/browserify/README.md @@ -0,0 +1 @@ +Running `browserify app.js > bundle.js` in this directory will produce `bundle.js` diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/browserify/app.js b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/browserify/app.js new file mode 100644 index 00000000..6f841e7f --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/browserify/app.js @@ -0,0 +1,11 @@ +var angular = require('angular'); + +angular.module('app', [ + // this is what you would do in a real app + // require('angular-cache') + + // for the example to work + require('../../dist/angular-cache.js') +]).run(function ($rootScope, CacheFactory) { + $rootScope.test = 'It works! Using ' + (CacheFactory ? 'angular-cache' : 'undefined'); +}); diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/browserify/index.html b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/browserify/index.html new file mode 100644 index 00000000..9769ccd3 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/browserify/index.html @@ -0,0 +1,11 @@ + + + + My App + + + + +

      {{ test }}

      + + diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/README.md b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/README.md new file mode 100644 index 00000000..0632f376 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/README.md @@ -0,0 +1,3 @@ +Running `r.js -o require.config.js` in this directory will produce `bundle.js` + +In `index.html` switch `script/main` between `main` (load scripts dynamically) and `bundle` (load bundled scripts) diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/app.js b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/app.js new file mode 100644 index 00000000..74cb9040 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/app.js @@ -0,0 +1,9 @@ +define('app', [ + 'angular', + 'angular-cache' +], function (angular, angularCacheModuleName) { + return angular.module('app', ['angular-cache']) + .run(function ($rootScope) { + $rootScope.test = 'It works! Using ' + angularCacheModuleName; + }); +}); diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/index.html b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/index.html new file mode 100644 index 00000000..781e49df --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/index.html @@ -0,0 +1,14 @@ + + + + My App + + + + + + + +

      {{ test }}

      + + diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/main.js b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/main.js new file mode 100644 index 00000000..0fb6e498 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/main.js @@ -0,0 +1,22 @@ +require.config({ + paths: { + angular: '../../bower_components/angular/angular', + 'angular-cache': '../../dist/angular-cache', + }, + shim: { + 'angular': { + exports: 'angular' + } + } +}); + +require([ + 'angular', + 'app' + ], function (angular, app) { + angular.element(document.getElementsByTagName('html')[0]).ready(function () { + // bootstrap the app manually + angular.bootstrap(document, ['app']); + }); + } +); diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/require.config.js b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/require.config.js new file mode 100644 index 00000000..a89ae0fb --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/r.js/require.config.js @@ -0,0 +1,6 @@ +({ + name: 'main', + mainConfigFile: 'main.js', + out: 'bundle.js', + optimize: 'none' +}) diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack/README.md b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack/README.md new file mode 100644 index 00000000..77d2d8f2 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack/README.md @@ -0,0 +1 @@ +Running `webpack` in this directory will produce `bundle.js` diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack/app.js b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack/app.js new file mode 100644 index 00000000..7b904ee5 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack/app.js @@ -0,0 +1,8 @@ +var angular = require('angular'); +var angularCacheModuleName = require('angular-cache'); + +var app = angular.module('app', [ + angularCacheModuleName +]).run(function ($rootScope) { + $rootScope.test = 'It works, imported ' + angularCacheModuleName; +}); diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack/index.html b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack/index.html new file mode 100644 index 00000000..9769ccd3 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack/index.html @@ -0,0 +1,11 @@ + + + + My App + + + + +

      {{ test }}

      + + diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack/webpack.config.js b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack/webpack.config.js new file mode 100644 index 00000000..1b3a6dfb --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack/webpack.config.js @@ -0,0 +1,11 @@ +module.exports = { + entry: './app.js', + output: { + filename: 'bundle.js' + }, + resolve: { + alias: { + 'angular-cache': '../../dist/angular-cache.js' + } + } +}; diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6/README.md b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6/README.md new file mode 100644 index 00000000..77d2d8f2 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6/README.md @@ -0,0 +1 @@ +Running `webpack` in this directory will produce `bundle.js` diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6/app.js b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6/app.js new file mode 100644 index 00000000..78a3d254 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6/app.js @@ -0,0 +1,8 @@ +import angular from 'angular'; +import angularCacheModuleName from 'angular-cache'; + +let app = angular.module('app', [ + angularCacheModuleName +]).run($rootScope => { + $rootScope.test = 'It works, imported ' + angularCacheModuleName; +}); diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6/index.html b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6/index.html new file mode 100644 index 00000000..9769ccd3 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6/index.html @@ -0,0 +1,11 @@ + + + + My App + + + + +

      {{ test }}

      + + diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6/webpack.config.js b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6/webpack.config.js new file mode 100644 index 00000000..3caf2bef --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6/webpack.config.js @@ -0,0 +1,16 @@ +module.exports = { + entry: './app.js', + output: { + filename: 'bundle.js' + }, + resolve: { + alias: { + 'angular-cache': '../../dist/angular-cache.js' + } + }, + module: { + loaders: [ + { test: /(.+)\.js$/, loader: 'babel-loader?blacklist=useStrict' } + ] + } +}; diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6_2/README.md b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6_2/README.md new file mode 100644 index 00000000..77d2d8f2 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6_2/README.md @@ -0,0 +1 @@ +Running `webpack` in this directory will produce `bundle.js` diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6_2/app.js b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6_2/app.js new file mode 100644 index 00000000..c75a0b36 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6_2/app.js @@ -0,0 +1,8 @@ +import 'angular'; +import 'angular-cache'; + +let app = angular.module('app', [ + 'angular-cache' +]).run(($rootScope, CacheFactory) => { + $rootScope.test = 'It works, imported ' + (CacheFactory ? 'angular-cache' : 'undefined'); +}); diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6_2/index.html b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6_2/index.html new file mode 100644 index 00000000..9769ccd3 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6_2/index.html @@ -0,0 +1,11 @@ + + + + My App + + + + +

      {{ test }}

      + + diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6_2/webpack.config.js b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6_2/webpack.config.js new file mode 100644 index 00000000..3caf2bef --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/build_examples/webpack_es6_2/webpack.config.js @@ -0,0 +1,16 @@ +module.exports = { + entry: './app.js', + output: { + filename: 'bundle.js' + }, + resolve: { + alias: { + 'angular-cache': '../../dist/angular-cache.js' + } + }, + module: { + loaders: [ + { test: /(.+)\.js$/, loader: 'babel-loader?blacklist=useStrict' } + ] + } +}; diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/circle.yml b/ecomp-portal-FE/client/bower_components/angular-cache/circle.yml new file mode 100644 index 00000000..cfe85771 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/circle.yml @@ -0,0 +1,6 @@ +machine: + node: + version: 4.1.0 +test: + override: + - npm run ci diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/dist/angular-cache.js b/ecomp-portal-FE/client/bower_components/angular-cache/dist/angular-cache.js new file mode 100644 index 00000000..6a62e9b7 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/dist/angular-cache.js @@ -0,0 +1,1111 @@ +/** + * angular-cache + * @version 4.6.0 - Homepage + * @copyright (c) 2013-2016 angular-cache project authors + * @license MIT + * @overview angular-cache is a very useful replacement for Angular's $cacheFactory. + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(typeof angular === 'undefined' ? require('angular') : angular) : + typeof define === 'function' && define.amd ? define('angular-cache', ['angular'], factory) : + (global.angularCacheModuleName = factory(global.angular)); +}(this, function (angular) { 'use strict'; + + angular = 'default' in angular ? angular['default'] : angular; + + var babelHelpers = {}; + babelHelpers.typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; + } : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; + }; + + /** + * @method bubbleUp + * @param {array} heap The heap. + * @param {function} weightFunc The weight function. + * @param {number} n The index of the element to bubble up. + */ + var bubbleUp = function bubbleUp(heap, weightFunc, n) { + var element = heap[n]; + var weight = weightFunc(element); + // When at 0, an element can not go up any further. + while (n > 0) { + // Compute the parent element's index, and fetch it. + var parentN = Math.floor((n + 1) / 2) - 1; + var parent = heap[parentN]; + // If the parent has a lesser weight, things are in order and we + // are done. + if (weight >= weightFunc(parent)) { + break; + } else { + heap[parentN] = element; + heap[n] = parent; + n = parentN; + } + } + }; + + /** + * @method bubbleDown + * @param {array} heap The heap. + * @param {function} weightFunc The weight function. + * @param {number} n The index of the element to sink down. + */ + var bubbleDown = function bubbleDown(heap, weightFunc, n) { + var length = heap.length; + var node = heap[n]; + var nodeWeight = weightFunc(node); + + while (true) { + var child2N = (n + 1) * 2; + var child1N = child2N - 1; + var swap = null; + if (child1N < length) { + var child1 = heap[child1N]; + var child1Weight = weightFunc(child1); + // If the score is less than our node's, we need to swap. + if (child1Weight < nodeWeight) { + swap = child1N; + } + } + // Do the same checks for the other child. + if (child2N < length) { + var child2 = heap[child2N]; + var child2Weight = weightFunc(child2); + if (child2Weight < (swap === null ? nodeWeight : weightFunc(heap[child1N]))) { + swap = child2N; + } + } + + if (swap === null) { + break; + } else { + heap[n] = heap[swap]; + heap[swap] = node; + n = swap; + } + } + }; + + function BinaryHeap(weightFunc, compareFunc) { + if (!weightFunc) { + weightFunc = function weightFunc(x) { + return x; + }; + } + if (!compareFunc) { + compareFunc = function compareFunc(x, y) { + return x === y; + }; + } + if (typeof weightFunc !== 'function') { + throw new Error('BinaryHeap([weightFunc][, compareFunc]): "weightFunc" must be a function!'); + } + if (typeof compareFunc !== 'function') { + throw new Error('BinaryHeap([weightFunc][, compareFunc]): "compareFunc" must be a function!'); + } + this.weightFunc = weightFunc; + this.compareFunc = compareFunc; + this.heap = []; + } + + var BHProto = BinaryHeap.prototype; + + BHProto.push = function (node) { + this.heap.push(node); + bubbleUp(this.heap, this.weightFunc, this.heap.length - 1); + }; + + BHProto.peek = function () { + return this.heap[0]; + }; + + BHProto.pop = function () { + var front = this.heap[0]; + var end = this.heap.pop(); + if (this.heap.length > 0) { + this.heap[0] = end; + bubbleDown(this.heap, this.weightFunc, 0); + } + return front; + }; + + BHProto.remove = function (node) { + var length = this.heap.length; + for (var i = 0; i < length; i++) { + if (this.compareFunc(this.heap[i], node)) { + var removed = this.heap[i]; + var end = this.heap.pop(); + if (i !== length - 1) { + this.heap[i] = end; + bubbleUp(this.heap, this.weightFunc, i); + bubbleDown(this.heap, this.weightFunc, i); + } + return removed; + } + } + return null; + }; + + BHProto.removeAll = function () { + this.heap = []; + }; + + BHProto.size = function () { + return this.heap.length; + }; + + var _Promise = null; + try { + _Promise = window.Promise; + } catch (e) {} + + var utils = { + isNumber: function isNumber(value) { + return typeof value === 'number'; + }, + isString: function isString(value) { + return typeof value === 'string'; + }, + isObject: function isObject(value) { + return value !== null && (typeof value === 'undefined' ? 'undefined' : babelHelpers.typeof(value)) === 'object'; + }, + isFunction: function isFunction(value) { + return typeof value === 'function'; + }, + fromJson: function fromJson(value) { + return JSON.parse(value); + }, + equals: function equals(a, b) { + return a === b; + }, + + Promise: _Promise + }; + + function _keys(collection) { + var keys = []; + var key = void 0; + if (!utils.isObject(collection)) { + return keys; + } + for (key in collection) { + if (collection.hasOwnProperty(key)) { + keys.push(key); + } + } + return keys; + } + + function _isPromiseLike(value) { + return value && typeof value.then === 'function'; + } + + function _stringifyNumber(number) { + if (utils.isNumber(number)) { + return number.toString(); + } + return number; + } + + function _keySet(collection) { + var keySet = {}; + var key = void 0; + if (!utils.isObject(collection)) { + return keySet; + } + for (key in collection) { + if (collection.hasOwnProperty(key)) { + keySet[key] = key; + } + } + return keySet; + } + + var defaults = { + capacity: Number.MAX_VALUE, + maxAge: Number.MAX_VALUE, + deleteOnExpire: 'none', + onExpire: null, + cacheFlushInterval: null, + recycleFreq: 1000, + storageMode: 'memory', + storageImpl: null, + disabled: false, + storagePrefix: 'cachefactory.caches.', + storeOnResolve: false, + storeOnReject: false + }; + + var caches = {}; + + function createCache(cacheId, options) { + if (cacheId in caches) { + throw new Error(cacheId + ' already exists!'); + } else if (!utils.isString(cacheId)) { + throw new Error('cacheId must be a string!'); + } + + var $$data = {}; + var $$promises = {}; + var $$storage = null; + var $$expiresHeap = new BinaryHeap(function (x) { + return x.expires; + }, utils.equals); + var $$lruHeap = new BinaryHeap(function (x) { + return x.accessed; + }, utils.equals); + + var cache = caches[cacheId] = { + + $$id: cacheId, + + destroy: function destroy() { + clearInterval(this.$$cacheFlushIntervalId); + clearInterval(this.$$recycleFreqId); + this.removeAll(); + if ($$storage) { + $$storage().removeItem(this.$$prefix + '.keys'); + $$storage().removeItem(this.$$prefix); + } + $$storage = null; + $$data = null; + $$lruHeap = null; + $$expiresHeap = null; + this.$$prefix = null; + delete caches[this.$$id]; + }, + disable: function disable() { + this.$$disabled = true; + }, + enable: function enable() { + delete this.$$disabled; + }, + get: function get(key, options) { + var _this2 = this; + + if (Array.isArray(key)) { + var _ret = function () { + var keys = key; + var values = []; + + keys.forEach(function (key) { + var value = _this2.get(key, options); + if (value !== null && value !== undefined) { + values.push(value); + } + }); + + return { + v: values + }; + }(); + + if ((typeof _ret === 'undefined' ? 'undefined' : babelHelpers.typeof(_ret)) === "object") return _ret.v; + } else { + key = _stringifyNumber(key); + + if (this.$$disabled) { + return; + } + } + + options = options || {}; + if (!utils.isString(key)) { + throw new Error('key must be a string!'); + } else if (options && !utils.isObject(options)) { + throw new Error('options must be an object!'); + } else if (options.onExpire && !utils.isFunction(options.onExpire)) { + throw new Error('options.onExpire must be a function!'); + } + + var item = void 0; + + if ($$storage) { + if ($$promises[key]) { + return $$promises[key]; + } + + var itemJson = $$storage().getItem(this.$$prefix + '.data.' + key); + + if (itemJson) { + item = utils.fromJson(itemJson); + } else { + return; + } + } else if (utils.isObject($$data)) { + if (!(key in $$data)) { + return; + } + + item = $$data[key]; + } + + var value = item.value; + var now = new Date().getTime(); + + if ($$storage) { + $$lruHeap.remove({ + key: key, + accessed: item.accessed + }); + item.accessed = now; + $$lruHeap.push({ + key: key, + accessed: now + }); + } else { + $$lruHeap.remove(item); + item.accessed = now; + $$lruHeap.push(item); + } + + if (this.$$deleteOnExpire === 'passive' && 'expires' in item && item.expires < now) { + this.remove(key); + + if (this.$$onExpire) { + this.$$onExpire(key, item.value, options.onExpire); + } else if (options.onExpire) { + options.onExpire.call(this, key, item.value); + } + value = undefined; + } else if ($$storage) { + $$storage().setItem(this.$$prefix + '.data.' + key, JSON.stringify(item)); + } + + return value; + }, + info: function info(key) { + if (key) { + var item = void 0; + if ($$storage) { + var itemJson = $$storage().getItem(this.$$prefix + '.data.' + key); + + if (itemJson) { + item = utils.fromJson(itemJson); + return { + created: item.created, + accessed: item.accessed, + expires: item.expires, + isExpired: new Date().getTime() - item.created > (item.maxAge || this.$$maxAge) + }; + } else { + return undefined; + } + } else if (utils.isObject($$data) && key in $$data) { + item = $$data[key]; + + return { + created: item.created, + accessed: item.accessed, + expires: item.expires, + isExpired: new Date().getTime() - item.created > (item.maxAge || this.$$maxAge) + }; + } else { + return undefined; + } + } else { + return { + id: this.$$id, + capacity: this.$$capacity, + maxAge: this.$$maxAge, + deleteOnExpire: this.$$deleteOnExpire, + onExpire: this.$$onExpire, + cacheFlushInterval: this.$$cacheFlushInterval, + recycleFreq: this.$$recycleFreq, + storageMode: this.$$storageMode, + storageImpl: $$storage ? $$storage() : undefined, + disabled: !!this.$$disabled, + size: $$lruHeap && $$lruHeap.size() || 0 + }; + } + }, + keys: function keys() { + if ($$storage) { + var keysJson = $$storage().getItem(this.$$prefix + '.keys'); + + if (keysJson) { + return utils.fromJson(keysJson); + } else { + return []; + } + } else { + return _keys($$data); + } + }, + keySet: function keySet() { + if ($$storage) { + var keysJson = $$storage().getItem(this.$$prefix + '.keys'); + var kSet = {}; + + if (keysJson) { + var keys = utils.fromJson(keysJson); + + for (var i = 0; i < keys.length; i++) { + kSet[keys[i]] = keys[i]; + } + } + return kSet; + } else { + return _keySet($$data); + } + }, + put: function put(key, value, options) { + var _this3 = this; + + options || (options = {}); + + var storeOnResolve = 'storeOnResolve' in options ? !!options.storeOnResolve : this.$$storeOnResolve; + var storeOnReject = 'storeOnReject' in options ? !!options.storeOnReject : this.$$storeOnReject; + + var getHandler = function getHandler(store, isError) { + return function (v) { + if (store) { + delete $$promises[key]; + if (utils.isObject(v) && 'status' in v && 'data' in v) { + v = [v.status, v.data, v.headers(), v.statusText]; + _this3.put(key, v); + } else { + _this3.put(key, v); + } + } + if (isError) { + if (utils.Promise) { + return utils.Promise.reject(v); + } else { + throw v; + } + } else { + return v; + } + }; + }; + + if (this.$$disabled || !utils.isObject($$data) || value === null || value === undefined) { + return; + } + key = _stringifyNumber(key); + + if (!utils.isString(key)) { + throw new Error('key must be a string!'); + } + + var now = new Date().getTime(); + var item = { + key: key, + value: _isPromiseLike(value) ? value.then(getHandler(storeOnResolve, false), getHandler(storeOnReject, true)) : value, + created: options.created === undefined ? now : options.created, + accessed: options.accessed === undefined ? now : options.accessed + }; + if (options.maxAge) { + item.maxAge = options.maxAge; + } + + if (options.expires === undefined) { + item.expires = item.created + (item.maxAge || this.$$maxAge); + } else { + item.expires = options.expires; + } + + if ($$storage) { + if (_isPromiseLike(item.value)) { + $$promises[key] = item.value; + return $$promises[key]; + } + var keysJson = $$storage().getItem(this.$$prefix + '.keys'); + var keys = keysJson ? utils.fromJson(keysJson) : []; + var itemJson = $$storage().getItem(this.$$prefix + '.data.' + key); + + // Remove existing + if (itemJson) { + this.remove(key); + } + // Add to expires heap + $$expiresHeap.push({ + key: key, + expires: item.expires + }); + // Add to lru heap + $$lruHeap.push({ + key: key, + accessed: item.accessed + }); + // Set item + $$storage().setItem(this.$$prefix + '.data.' + key, JSON.stringify(item)); + var exists = false; + for (var i = 0; i < keys.length; i++) { + if (keys[i] === key) { + exists = true; + break; + } + } + if (!exists) { + keys.push(key); + } + $$storage().setItem(this.$$prefix + '.keys', JSON.stringify(keys)); + } else { + // Remove existing + if ($$data[key]) { + this.remove(key); + } + // Add to expires heap + $$expiresHeap.push(item); + // Add to lru heap + $$lruHeap.push(item); + // Set item + $$data[key] = item; + delete $$promises[key]; + } + + // Handle exceeded capacity + if ($$lruHeap.size() > this.$$capacity) { + this.remove($$lruHeap.peek().key); + } + + return value; + }, + remove: function remove(key) { + key += ''; + delete $$promises[key]; + if ($$storage) { + var itemJson = $$storage().getItem(this.$$prefix + '.data.' + key); + + if (itemJson) { + var item = utils.fromJson(itemJson); + $$lruHeap.remove({ + key: key, + accessed: item.accessed + }); + $$expiresHeap.remove({ + key: key, + expires: item.expires + }); + $$storage().removeItem(this.$$prefix + '.data.' + key); + var keysJson = $$storage().getItem(this.$$prefix + '.keys'); + var keys = keysJson ? utils.fromJson(keysJson) : []; + var index = keys.indexOf(key); + + if (index >= 0) { + keys.splice(index, 1); + } + $$storage().setItem(this.$$prefix + '.keys', JSON.stringify(keys)); + return item.value; + } + } else if (utils.isObject($$data)) { + var value = $$data[key] ? $$data[key].value : undefined; + $$lruHeap.remove($$data[key]); + $$expiresHeap.remove($$data[key]); + $$data[key] = null; + delete $$data[key]; + return value; + } + }, + removeAll: function removeAll() { + if ($$storage) { + $$lruHeap.removeAll(); + $$expiresHeap.removeAll(); + var keysJson = $$storage().getItem(this.$$prefix + '.keys'); + + if (keysJson) { + var keys = utils.fromJson(keysJson); + + for (var i = 0; i < keys.length; i++) { + this.remove(keys[i]); + } + } + $$storage().setItem(this.$$prefix + '.keys', JSON.stringify([])); + } else if (utils.isObject($$data)) { + $$lruHeap.removeAll(); + $$expiresHeap.removeAll(); + for (var key in $$data) { + $$data[key] = null; + } + $$data = {}; + } else { + $$lruHeap.removeAll(); + $$expiresHeap.removeAll(); + $$data = {}; + } + $$promises = {}; + }, + removeExpired: function removeExpired() { + var now = new Date().getTime(); + var expired = {}; + var key = void 0; + var expiredItem = void 0; + + while ((expiredItem = $$expiresHeap.peek()) && expiredItem.expires <= now) { + expired[expiredItem.key] = expiredItem.value ? expiredItem.value : null; + $$expiresHeap.pop(); + } + + if ($$storage) { + for (key in expired) { + var itemJson = $$storage().getItem(this.$$prefix + '.data.' + key); + if (itemJson) { + expired[key] = utils.fromJson(itemJson).value; + this.remove(key); + } + } + } else { + for (key in expired) { + this.remove(key); + } + } + + if (this.$$onExpire) { + for (key in expired) { + this.$$onExpire(key, expired[key]); + } + } + + return expired; + }, + setCacheFlushInterval: function setCacheFlushInterval(cacheFlushInterval) { + var _this = this; + if (cacheFlushInterval === null) { + delete _this.$$cacheFlushInterval; + } else if (!utils.isNumber(cacheFlushInterval)) { + throw new Error('cacheFlushInterval must be a number!'); + } else if (cacheFlushInterval < 0) { + throw new Error('cacheFlushInterval must be greater than zero!'); + } else if (cacheFlushInterval !== _this.$$cacheFlushInterval) { + _this.$$cacheFlushInterval = cacheFlushInterval; + + clearInterval(_this.$$cacheFlushIntervalId); // eslint-disable-line + + _this.$$cacheFlushIntervalId = setInterval(function () { + _this.removeAll(); + }, _this.$$cacheFlushInterval); + } + }, + setCapacity: function setCapacity(capacity) { + if (capacity === null) { + delete this.$$capacity; + } else if (!utils.isNumber(capacity)) { + throw new Error('capacity must be a number!'); + } else if (capacity < 0) { + throw new Error('capacity must be greater than zero!'); + } else { + this.$$capacity = capacity; + } + var removed = {}; + while ($$lruHeap.size() > this.$$capacity) { + removed[$$lruHeap.peek().key] = this.remove($$lruHeap.peek().key); + } + return removed; + }, + setDeleteOnExpire: function setDeleteOnExpire(deleteOnExpire, setRecycleFreq) { + if (deleteOnExpire === null) { + delete this.$$deleteOnExpire; + } else if (!utils.isString(deleteOnExpire)) { + throw new Error('deleteOnExpire must be a string!'); + } else if (deleteOnExpire !== 'none' && deleteOnExpire !== 'passive' && deleteOnExpire !== 'aggressive') { + throw new Error('deleteOnExpire must be "none", "passive" or "aggressive"!'); + } else { + this.$$deleteOnExpire = deleteOnExpire; + } + if (setRecycleFreq !== false) { + this.setRecycleFreq(this.$$recycleFreq); + } + }, + setMaxAge: function setMaxAge(maxAge) { + if (maxAge === null) { + this.$$maxAge = Number.MAX_VALUE; + } else if (!utils.isNumber(maxAge)) { + throw new Error('maxAge must be a number!'); + } else if (maxAge < 0) { + throw new Error('maxAge must be greater than zero!'); + } else { + this.$$maxAge = maxAge; + } + var i = void 0, + keys = void 0, + key = void 0; + + $$expiresHeap.removeAll(); + + if ($$storage) { + var keysJson = $$storage().getItem(this.$$prefix + '.keys'); + + keys = keysJson ? utils.fromJson(keysJson) : []; + + for (i = 0; i < keys.length; i++) { + key = keys[i]; + var itemJson = $$storage().getItem(this.$$prefix + '.data.' + key); + + if (itemJson) { + var item = utils.fromJson(itemJson); + if (this.$$maxAge === Number.MAX_VALUE) { + item.expires = Number.MAX_VALUE; + } else { + item.expires = item.created + (item.maxAge || this.$$maxAge); + } + $$expiresHeap.push({ + key: key, + expires: item.expires + }); + } + } + } else { + keys = _keys($$data); + + for (i = 0; i < keys.length; i++) { + key = keys[i]; + if (this.$$maxAge === Number.MAX_VALUE) { + $$data[key].expires = Number.MAX_VALUE; + } else { + $$data[key].expires = $$data[key].created + ($$data[key].maxAge || this.$$maxAge); + } + $$expiresHeap.push($$data[key]); + } + } + if (this.$$deleteOnExpire === 'aggressive') { + return this.removeExpired(); + } else { + return {}; + } + }, + setOnExpire: function setOnExpire(onExpire) { + if (onExpire === null) { + delete this.$$onExpire; + } else if (!utils.isFunction(onExpire)) { + throw new Error('onExpire must be a function!'); + } else { + this.$$onExpire = onExpire; + } + }, + setOptions: function setOptions(cacheOptions, strict) { + cacheOptions = cacheOptions || {}; + strict = !!strict; + if (!utils.isObject(cacheOptions)) { + throw new Error('cacheOptions must be an object!'); + } + + if ('storagePrefix' in cacheOptions) { + this.$$storagePrefix = cacheOptions.storagePrefix; + } else if (strict) { + this.$$storagePrefix = defaults.storagePrefix; + } + + this.$$prefix = this.$$storagePrefix + this.$$id; + + if ('disabled' in cacheOptions) { + this.$$disabled = !!cacheOptions.disabled; + } else if (strict) { + this.$$disabled = defaults.disabled; + } + + if ('deleteOnExpire' in cacheOptions) { + this.setDeleteOnExpire(cacheOptions.deleteOnExpire, false); + } else if (strict) { + this.setDeleteOnExpire(defaults.deleteOnExpire, false); + } + + if ('recycleFreq' in cacheOptions) { + this.setRecycleFreq(cacheOptions.recycleFreq); + } else if (strict) { + this.setRecycleFreq(defaults.recycleFreq); + } + + if ('maxAge' in cacheOptions) { + this.setMaxAge(cacheOptions.maxAge); + } else if (strict) { + this.setMaxAge(defaults.maxAge); + } + + if ('storeOnResolve' in cacheOptions) { + this.$$storeOnResolve = !!cacheOptions.storeOnResolve; + } else if (strict) { + this.$$storeOnResolve = defaults.storeOnResolve; + } + + if ('storeOnReject' in cacheOptions) { + this.$$storeOnReject = !!cacheOptions.storeOnReject; + } else if (strict) { + this.$$storeOnReject = defaults.storeOnReject; + } + + if ('capacity' in cacheOptions) { + this.setCapacity(cacheOptions.capacity); + } else if (strict) { + this.setCapacity(defaults.capacity); + } + + if ('cacheFlushInterval' in cacheOptions) { + this.setCacheFlushInterval(cacheOptions.cacheFlushInterval); + } else if (strict) { + this.setCacheFlushInterval(defaults.cacheFlushInterval); + } + + if ('onExpire' in cacheOptions) { + this.setOnExpire(cacheOptions.onExpire); + } else if (strict) { + this.setOnExpire(defaults.onExpire); + } + + if ('storageMode' in cacheOptions || 'storageImpl' in cacheOptions) { + this.setStorageMode(cacheOptions.storageMode || defaults.storageMode, cacheOptions.storageImpl || defaults.storageImpl); + } else if (strict) { + this.setStorageMode(defaults.storageMode, defaults.storageImpl); + } + }, + setRecycleFreq: function setRecycleFreq(recycleFreq) { + if (recycleFreq === null) { + delete this.$$recycleFreq; + } else if (!utils.isNumber(recycleFreq)) { + throw new Error('recycleFreq must be a number!'); + } else if (recycleFreq < 0) { + throw new Error('recycleFreq must be greater than zero!'); + } else { + this.$$recycleFreq = recycleFreq; + } + clearInterval(this.$$recycleFreqId); + if (this.$$deleteOnExpire === 'aggressive') { + (function (self) { + self.$$recycleFreqId = setInterval(function () { + self.removeExpired(); + }, self.$$recycleFreq); + })(this); + } else { + delete this.$$recycleFreqId; + } + }, + setStorageMode: function setStorageMode(storageMode, storageImpl) { + if (!utils.isString(storageMode)) { + throw new Error('storageMode must be a string!'); + } else if (storageMode !== 'memory' && storageMode !== 'localStorage' && storageMode !== 'sessionStorage') { + throw new Error('storageMode must be "memory", "localStorage" or "sessionStorage"!'); + } + + var prevStorage = $$storage; + var prevData = $$data; + var shouldReInsert = false; + var items = {}; + + function load(prevStorage, prevData) { + var keys = this.keys(); + var length = keys.length; + if (length) { + var _key = void 0; + var prevDataIsObject = utils.isObject(prevData); + for (var i = 0; i < length; i++) { + _key = keys[i]; + if (prevStorage) { + var itemJson = prevStorage().getItem(this.$$prefix + '.data.' + _key); + if (itemJson) { + items[_key] = utils.fromJson(itemJson); + } + } else if (prevDataIsObject) { + items[_key] = prevData[_key]; + } + this.remove(_key); + } + shouldReInsert = true; + } + } + + if (!this.$$initializing) { + load.call(this, prevStorage, prevData); + } + + this.$$storageMode = storageMode; + + if (storageImpl) { + if (!utils.isObject(storageImpl)) { + throw new Error('storageImpl must be an object!'); + } else if (!('setItem' in storageImpl) || typeof storageImpl.setItem !== 'function') { + throw new Error('storageImpl must implement "setItem(key, value)"!'); + } else if (!('getItem' in storageImpl) || typeof storageImpl.getItem !== 'function') { + throw new Error('storageImpl must implement "getItem(key)"!'); + } else if (!('removeItem' in storageImpl) || typeof storageImpl.removeItem !== 'function') { + throw new Error('storageImpl must implement "removeItem(key)"!'); + } + $$storage = function $$storage() { + return storageImpl; + }; + } else if (this.$$storageMode === 'localStorage') { + try { + localStorage.setItem('cachefactory', 'cachefactory'); + localStorage.removeItem('cachefactory'); + $$storage = function $$storage() { + return localStorage; + }; + } catch (e) { + $$storage = null; + this.$$storageMode = 'memory'; + } + } else if (this.$$storageMode === 'sessionStorage') { + try { + sessionStorage.setItem('cachefactory', 'cachefactory'); + sessionStorage.removeItem('cachefactory'); + $$storage = function $$storage() { + return sessionStorage; + }; + } catch (e) { + $$storage = null; + this.$$storageMode = 'memory'; + } + } else { + $$storage = null; + this.$$storageMode = 'memory'; + } + + if (this.$$initializing) { + load.call(this, $$storage, $$data); + } + + if (shouldReInsert) { + var item = void 0; + for (var key in items) { + item = items[key]; + this.put(key, item.value, { + created: item.created, + accessed: item.accessed, + expires: item.expires + }); + } + } + }, + touch: function touch(key, options) { + var _this4 = this; + + if (key) { + var val = this.get(key, { + onExpire: function onExpire(k, v) { + return _this4.put(k, v); + } + }); + if (val) { + this.put(key, val, options); + } + } else { + var keys = this.keys(); + for (var i = 0; i < keys.length; i++) { + this.touch(keys[i], options); + } + } + }, + values: function values() { + var keys = this.keys(); + var items = []; + for (var i = 0; i < keys.length; i++) { + items.push(this.get(keys[i])); + } + return items; + } + }; + + cache.$$initializing = true; + cache.setOptions(options, true); + cache.$$initializing = false; + + return cache; + } + + function CacheFactory(cacheId, options) { + return createCache(cacheId, options); + } + + CacheFactory.createCache = createCache; + CacheFactory.defaults = defaults; + + CacheFactory.info = function () { + var keys = _keys(caches); + var info = { + size: keys.length, + caches: {} + }; + for (var opt in defaults) { + if (defaults.hasOwnProperty(opt)) { + info[opt] = defaults[opt]; + } + } + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + info.caches[key] = caches[key].info(); + } + return info; + }; + + CacheFactory.get = function (cacheId) { + return caches[cacheId]; + }; + CacheFactory.keySet = function () { + return _keySet(caches); + }; + CacheFactory.keys = function () { + return _keys(caches); + }; + CacheFactory.destroy = function (cacheId) { + if (caches[cacheId]) { + caches[cacheId].destroy(); + delete caches[cacheId]; + } + }; + CacheFactory.destroyAll = function () { + for (var cacheId in caches) { + caches[cacheId].destroy(); + } + caches = {}; + }; + CacheFactory.clearAll = function () { + for (var cacheId in caches) { + caches[cacheId].removeAll(); + } + }; + CacheFactory.removeExpiredFromAll = function () { + var expired = {}; + for (var cacheId in caches) { + expired[cacheId] = caches[cacheId].removeExpired(); + } + return expired; + }; + CacheFactory.enableAll = function () { + for (var cacheId in caches) { + caches[cacheId].$$disabled = false; + } + }; + CacheFactory.disableAll = function () { + for (var cacheId in caches) { + caches[cacheId].$$disabled = true; + } + }; + CacheFactory.touchAll = function () { + for (var cacheId in caches) { + caches[cacheId].touch(); + } + }; + + CacheFactory.utils = utils; + CacheFactory.BinaryHeap = BinaryHeap; + + CacheFactory.utils.equals = angular.equals; + CacheFactory.utils.isObject = angular.isObject; + CacheFactory.utils.fromJson = angular.fromJson; + + function BinaryHeapProvider() { + this.$get = function () { + return CacheFactory.BinaryHeap; + }; + } + + function CacheFactoryProvider() { + this.defaults = CacheFactory.defaults; + this.defaults.storagePrefix = 'angular-cache.caches.'; + + this.$get = ['$q', function ($q) { + CacheFactory.utils.Promise = $q; + return CacheFactory; + }]; + } + + angular.module('angular-cache', []).provider('BinaryHeap', BinaryHeapProvider).provider('CacheFactory', CacheFactoryProvider); + + var index = 'angular-cache'; + + return index; + +})); +//# sourceMappingURL=angular-cache.js.map \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/dist/angular-cache.js.map b/ecomp-portal-FE/client/bower_components/angular-cache/dist/angular-cache.js.map new file mode 100644 index 00000000..ca27a6fd --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/dist/angular-cache.js.map @@ -0,0 +1 @@ +{"version":3,"file":"angular-cache.js","sources":["../node_modules/cachefactory/dist/cachefactory.es2015.js","../src/index.js"],"sourcesContent":["/**\n * CacheFactory\n * @version 1.5.1 - Homepage \n * @copyright (c) 2013-2016 CacheFactory project authors\n * @license MIT \n * @overview CacheFactory is a very simple and useful cache for the browser.\n */\nvar babelHelpers = {};\nbabelHelpers.typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) {\n return typeof obj;\n} : function (obj) {\n return obj && typeof Symbol === \"function\" && obj.constructor === Symbol ? \"symbol\" : typeof obj;\n};\nbabelHelpers;\n\n/**\n * @method bubbleUp\n * @param {array} heap The heap.\n * @param {function} weightFunc The weight function.\n * @param {number} n The index of the element to bubble up.\n */\nvar bubbleUp = function bubbleUp(heap, weightFunc, n) {\n var element = heap[n];\n var weight = weightFunc(element);\n // When at 0, an element can not go up any further.\n while (n > 0) {\n // Compute the parent element's index, and fetch it.\n var parentN = Math.floor((n + 1) / 2) - 1;\n var parent = heap[parentN];\n // If the parent has a lesser weight, things are in order and we\n // are done.\n if (weight >= weightFunc(parent)) {\n break;\n } else {\n heap[parentN] = element;\n heap[n] = parent;\n n = parentN;\n }\n }\n};\n\n/**\n * @method bubbleDown\n * @param {array} heap The heap.\n * @param {function} weightFunc The weight function.\n * @param {number} n The index of the element to sink down.\n */\nvar bubbleDown = function bubbleDown(heap, weightFunc, n) {\n var length = heap.length;\n var node = heap[n];\n var nodeWeight = weightFunc(node);\n\n while (true) {\n var child2N = (n + 1) * 2;\n var child1N = child2N - 1;\n var swap = null;\n if (child1N < length) {\n var child1 = heap[child1N];\n var child1Weight = weightFunc(child1);\n // If the score is less than our node's, we need to swap.\n if (child1Weight < nodeWeight) {\n swap = child1N;\n }\n }\n // Do the same checks for the other child.\n if (child2N < length) {\n var child2 = heap[child2N];\n var child2Weight = weightFunc(child2);\n if (child2Weight < (swap === null ? nodeWeight : weightFunc(heap[child1N]))) {\n swap = child2N;\n }\n }\n\n if (swap === null) {\n break;\n } else {\n heap[n] = heap[swap];\n heap[swap] = node;\n n = swap;\n }\n }\n};\n\nfunction BinaryHeap(weightFunc, compareFunc) {\n if (!weightFunc) {\n weightFunc = function weightFunc(x) {\n return x;\n };\n }\n if (!compareFunc) {\n compareFunc = function compareFunc(x, y) {\n return x === y;\n };\n }\n if (typeof weightFunc !== 'function') {\n throw new Error('BinaryHeap([weightFunc][, compareFunc]): \"weightFunc\" must be a function!');\n }\n if (typeof compareFunc !== 'function') {\n throw new Error('BinaryHeap([weightFunc][, compareFunc]): \"compareFunc\" must be a function!');\n }\n this.weightFunc = weightFunc;\n this.compareFunc = compareFunc;\n this.heap = [];\n}\n\nvar BHProto = BinaryHeap.prototype;\n\nBHProto.push = function (node) {\n this.heap.push(node);\n bubbleUp(this.heap, this.weightFunc, this.heap.length - 1);\n};\n\nBHProto.peek = function () {\n return this.heap[0];\n};\n\nBHProto.pop = function () {\n var front = this.heap[0];\n var end = this.heap.pop();\n if (this.heap.length > 0) {\n this.heap[0] = end;\n bubbleDown(this.heap, this.weightFunc, 0);\n }\n return front;\n};\n\nBHProto.remove = function (node) {\n var length = this.heap.length;\n for (var i = 0; i < length; i++) {\n if (this.compareFunc(this.heap[i], node)) {\n var removed = this.heap[i];\n var end = this.heap.pop();\n if (i !== length - 1) {\n this.heap[i] = end;\n bubbleUp(this.heap, this.weightFunc, i);\n bubbleDown(this.heap, this.weightFunc, i);\n }\n return removed;\n }\n }\n return null;\n};\n\nBHProto.removeAll = function () {\n this.heap = [];\n};\n\nBHProto.size = function () {\n return this.heap.length;\n};\n\nvar _Promise = null;\ntry {\n _Promise = window.Promise;\n} catch (e) {}\n\nvar utils = {\n isNumber: function isNumber(value) {\n return typeof value === 'number';\n },\n isString: function isString(value) {\n return typeof value === 'string';\n },\n isObject: function isObject(value) {\n return value !== null && (typeof value === 'undefined' ? 'undefined' : babelHelpers.typeof(value)) === 'object';\n },\n isFunction: function isFunction(value) {\n return typeof value === 'function';\n },\n fromJson: function fromJson(value) {\n return JSON.parse(value);\n },\n equals: function equals(a, b) {\n return a === b;\n },\n\n\n Promise: _Promise\n};\n\nfunction _keys(collection) {\n var keys = [];\n var key = void 0;\n if (!utils.isObject(collection)) {\n return keys;\n }\n for (key in collection) {\n if (collection.hasOwnProperty(key)) {\n keys.push(key);\n }\n }\n return keys;\n}\n\nfunction _isPromiseLike(value) {\n return value && typeof value.then === 'function';\n}\n\nfunction _stringifyNumber(number) {\n if (utils.isNumber(number)) {\n return number.toString();\n }\n return number;\n}\n\nfunction _keySet(collection) {\n var keySet = {};\n var key = void 0;\n if (!utils.isObject(collection)) {\n return keySet;\n }\n for (key in collection) {\n if (collection.hasOwnProperty(key)) {\n keySet[key] = key;\n }\n }\n return keySet;\n}\n\nvar defaults = {\n capacity: Number.MAX_VALUE,\n maxAge: Number.MAX_VALUE,\n deleteOnExpire: 'none',\n onExpire: null,\n cacheFlushInterval: null,\n recycleFreq: 1000,\n storageMode: 'memory',\n storageImpl: null,\n disabled: false,\n storagePrefix: 'cachefactory.caches.',\n storeOnResolve: false,\n storeOnReject: false\n};\n\nvar caches = {};\n\nfunction createCache(cacheId, options) {\n if (cacheId in caches) {\n throw new Error(cacheId + ' already exists!');\n } else if (!utils.isString(cacheId)) {\n throw new Error('cacheId must be a string!');\n }\n\n var $$data = {};\n var $$promises = {};\n var $$storage = null;\n var $$expiresHeap = new BinaryHeap(function (x) {\n return x.expires;\n }, utils.equals);\n var $$lruHeap = new BinaryHeap(function (x) {\n return x.accessed;\n }, utils.equals);\n\n var cache = caches[cacheId] = {\n\n $$id: cacheId,\n\n destroy: function destroy() {\n clearInterval(this.$$cacheFlushIntervalId);\n clearInterval(this.$$recycleFreqId);\n this.removeAll();\n if ($$storage) {\n $$storage().removeItem(this.$$prefix + '.keys');\n $$storage().removeItem(this.$$prefix);\n }\n $$storage = null;\n $$data = null;\n $$lruHeap = null;\n $$expiresHeap = null;\n this.$$prefix = null;\n delete caches[this.$$id];\n },\n disable: function disable() {\n this.$$disabled = true;\n },\n enable: function enable() {\n delete this.$$disabled;\n },\n get: function get(key, options) {\n var _this2 = this;\n\n if (Array.isArray(key)) {\n var _ret = function () {\n var keys = key;\n var values = [];\n\n keys.forEach(function (key) {\n var value = _this2.get(key, options);\n if (value !== null && value !== undefined) {\n values.push(value);\n }\n });\n\n return {\n v: values\n };\n }();\n\n if ((typeof _ret === 'undefined' ? 'undefined' : babelHelpers.typeof(_ret)) === \"object\") return _ret.v;\n } else {\n key = _stringifyNumber(key);\n\n if (this.$$disabled) {\n return;\n }\n }\n\n options = options || {};\n if (!utils.isString(key)) {\n throw new Error('key must be a string!');\n } else if (options && !utils.isObject(options)) {\n throw new Error('options must be an object!');\n } else if (options.onExpire && !utils.isFunction(options.onExpire)) {\n throw new Error('options.onExpire must be a function!');\n }\n\n var item = void 0;\n\n if ($$storage) {\n if ($$promises[key]) {\n return $$promises[key];\n }\n\n var itemJson = $$storage().getItem(this.$$prefix + '.data.' + key);\n\n if (itemJson) {\n item = utils.fromJson(itemJson);\n } else {\n return;\n }\n } else if (utils.isObject($$data)) {\n if (!(key in $$data)) {\n return;\n }\n\n item = $$data[key];\n }\n\n var value = item.value;\n var now = new Date().getTime();\n\n if ($$storage) {\n $$lruHeap.remove({\n key: key,\n accessed: item.accessed\n });\n item.accessed = now;\n $$lruHeap.push({\n key: key,\n accessed: now\n });\n } else {\n $$lruHeap.remove(item);\n item.accessed = now;\n $$lruHeap.push(item);\n }\n\n if (this.$$deleteOnExpire === 'passive' && 'expires' in item && item.expires < now) {\n this.remove(key);\n\n if (this.$$onExpire) {\n this.$$onExpire(key, item.value, options.onExpire);\n } else if (options.onExpire) {\n options.onExpire.call(this, key, item.value);\n }\n value = undefined;\n } else if ($$storage) {\n $$storage().setItem(this.$$prefix + '.data.' + key, JSON.stringify(item));\n }\n\n return value;\n },\n info: function info(key) {\n if (key) {\n var item = void 0;\n if ($$storage) {\n var itemJson = $$storage().getItem(this.$$prefix + '.data.' + key);\n\n if (itemJson) {\n item = utils.fromJson(itemJson);\n return {\n created: item.created,\n accessed: item.accessed,\n expires: item.expires,\n isExpired: new Date().getTime() - item.created > (item.maxAge || this.$$maxAge)\n };\n } else {\n return undefined;\n }\n } else if (utils.isObject($$data) && key in $$data) {\n item = $$data[key];\n\n return {\n created: item.created,\n accessed: item.accessed,\n expires: item.expires,\n isExpired: new Date().getTime() - item.created > (item.maxAge || this.$$maxAge)\n };\n } else {\n return undefined;\n }\n } else {\n return {\n id: this.$$id,\n capacity: this.$$capacity,\n maxAge: this.$$maxAge,\n deleteOnExpire: this.$$deleteOnExpire,\n onExpire: this.$$onExpire,\n cacheFlushInterval: this.$$cacheFlushInterval,\n recycleFreq: this.$$recycleFreq,\n storageMode: this.$$storageMode,\n storageImpl: $$storage ? $$storage() : undefined,\n disabled: !!this.$$disabled,\n size: $$lruHeap && $$lruHeap.size() || 0\n };\n }\n },\n keys: function keys() {\n if ($$storage) {\n var keysJson = $$storage().getItem(this.$$prefix + '.keys');\n\n if (keysJson) {\n return utils.fromJson(keysJson);\n } else {\n return [];\n }\n } else {\n return _keys($$data);\n }\n },\n keySet: function keySet() {\n if ($$storage) {\n var keysJson = $$storage().getItem(this.$$prefix + '.keys');\n var kSet = {};\n\n if (keysJson) {\n var keys = utils.fromJson(keysJson);\n\n for (var i = 0; i < keys.length; i++) {\n kSet[keys[i]] = keys[i];\n }\n }\n return kSet;\n } else {\n return _keySet($$data);\n }\n },\n put: function put(key, value, options) {\n var _this3 = this;\n\n options || (options = {});\n\n var storeOnResolve = 'storeOnResolve' in options ? !!options.storeOnResolve : this.$$storeOnResolve;\n var storeOnReject = 'storeOnReject' in options ? !!options.storeOnReject : this.$$storeOnReject;\n\n var getHandler = function getHandler(store, isError) {\n return function (v) {\n if (store) {\n delete $$promises[key];\n if (utils.isObject(v) && 'status' in v && 'data' in v) {\n v = [v.status, v.data, v.headers(), v.statusText];\n _this3.put(key, v);\n } else {\n _this3.put(key, v);\n }\n }\n if (isError) {\n if (utils.Promise) {\n return utils.Promise.reject(v);\n } else {\n throw v;\n }\n } else {\n return v;\n }\n };\n };\n\n if (this.$$disabled || !utils.isObject($$data) || value === null || value === undefined) {\n return;\n }\n key = _stringifyNumber(key);\n\n if (!utils.isString(key)) {\n throw new Error('key must be a string!');\n }\n\n var now = new Date().getTime();\n var item = {\n key: key,\n value: _isPromiseLike(value) ? value.then(getHandler(storeOnResolve, false), getHandler(storeOnReject, true)) : value,\n created: options.created === undefined ? now : options.created,\n accessed: options.accessed === undefined ? now : options.accessed\n };\n if (options.maxAge) {\n item.maxAge = options.maxAge;\n }\n\n if (options.expires === undefined) {\n item.expires = item.created + (item.maxAge || this.$$maxAge);\n } else {\n item.expires = options.expires;\n }\n\n if ($$storage) {\n if (_isPromiseLike(item.value)) {\n $$promises[key] = item.value;\n return $$promises[key];\n }\n var keysJson = $$storage().getItem(this.$$prefix + '.keys');\n var keys = keysJson ? utils.fromJson(keysJson) : [];\n var itemJson = $$storage().getItem(this.$$prefix + '.data.' + key);\n\n // Remove existing\n if (itemJson) {\n this.remove(key);\n }\n // Add to expires heap\n $$expiresHeap.push({\n key: key,\n expires: item.expires\n });\n // Add to lru heap\n $$lruHeap.push({\n key: key,\n accessed: item.accessed\n });\n // Set item\n $$storage().setItem(this.$$prefix + '.data.' + key, JSON.stringify(item));\n var exists = false;\n for (var i = 0; i < keys.length; i++) {\n if (keys[i] === key) {\n exists = true;\n break;\n }\n }\n if (!exists) {\n keys.push(key);\n }\n $$storage().setItem(this.$$prefix + '.keys', JSON.stringify(keys));\n } else {\n // Remove existing\n if ($$data[key]) {\n this.remove(key);\n }\n // Add to expires heap\n $$expiresHeap.push(item);\n // Add to lru heap\n $$lruHeap.push(item);\n // Set item\n $$data[key] = item;\n delete $$promises[key];\n }\n\n // Handle exceeded capacity\n if ($$lruHeap.size() > this.$$capacity) {\n this.remove($$lruHeap.peek().key);\n }\n\n return value;\n },\n remove: function remove(key) {\n key += '';\n delete $$promises[key];\n if ($$storage) {\n var itemJson = $$storage().getItem(this.$$prefix + '.data.' + key);\n\n if (itemJson) {\n var item = utils.fromJson(itemJson);\n $$lruHeap.remove({\n key: key,\n accessed: item.accessed\n });\n $$expiresHeap.remove({\n key: key,\n expires: item.expires\n });\n $$storage().removeItem(this.$$prefix + '.data.' + key);\n var keysJson = $$storage().getItem(this.$$prefix + '.keys');\n var keys = keysJson ? utils.fromJson(keysJson) : [];\n var index = keys.indexOf(key);\n\n if (index >= 0) {\n keys.splice(index, 1);\n }\n $$storage().setItem(this.$$prefix + '.keys', JSON.stringify(keys));\n return item.value;\n }\n } else if (utils.isObject($$data)) {\n var value = $$data[key] ? $$data[key].value : undefined;\n $$lruHeap.remove($$data[key]);\n $$expiresHeap.remove($$data[key]);\n $$data[key] = null;\n delete $$data[key];\n return value;\n }\n },\n removeAll: function removeAll() {\n if ($$storage) {\n $$lruHeap.removeAll();\n $$expiresHeap.removeAll();\n var keysJson = $$storage().getItem(this.$$prefix + '.keys');\n\n if (keysJson) {\n var keys = utils.fromJson(keysJson);\n\n for (var i = 0; i < keys.length; i++) {\n this.remove(keys[i]);\n }\n }\n $$storage().setItem(this.$$prefix + '.keys', JSON.stringify([]));\n } else if (utils.isObject($$data)) {\n $$lruHeap.removeAll();\n $$expiresHeap.removeAll();\n for (var key in $$data) {\n $$data[key] = null;\n }\n $$data = {};\n } else {\n $$lruHeap.removeAll();\n $$expiresHeap.removeAll();\n $$data = {};\n }\n $$promises = {};\n },\n removeExpired: function removeExpired() {\n var now = new Date().getTime();\n var expired = {};\n var key = void 0;\n var expiredItem = void 0;\n\n while ((expiredItem = $$expiresHeap.peek()) && expiredItem.expires <= now) {\n expired[expiredItem.key] = expiredItem.value ? expiredItem.value : null;\n $$expiresHeap.pop();\n }\n\n if ($$storage) {\n for (key in expired) {\n var itemJson = $$storage().getItem(this.$$prefix + '.data.' + key);\n if (itemJson) {\n expired[key] = utils.fromJson(itemJson).value;\n this.remove(key);\n }\n }\n } else {\n for (key in expired) {\n this.remove(key);\n }\n }\n\n if (this.$$onExpire) {\n for (key in expired) {\n this.$$onExpire(key, expired[key]);\n }\n }\n\n return expired;\n },\n setCacheFlushInterval: function setCacheFlushInterval(cacheFlushInterval) {\n var _this = this;\n if (cacheFlushInterval === null) {\n delete _this.$$cacheFlushInterval;\n } else if (!utils.isNumber(cacheFlushInterval)) {\n throw new Error('cacheFlushInterval must be a number!');\n } else if (cacheFlushInterval < 0) {\n throw new Error('cacheFlushInterval must be greater than zero!');\n } else if (cacheFlushInterval !== _this.$$cacheFlushInterval) {\n _this.$$cacheFlushInterval = cacheFlushInterval;\n\n clearInterval(_this.$$cacheFlushIntervalId); // eslint-disable-line\n\n _this.$$cacheFlushIntervalId = setInterval(function () {\n _this.removeAll();\n }, _this.$$cacheFlushInterval);\n }\n },\n setCapacity: function setCapacity(capacity) {\n if (capacity === null) {\n delete this.$$capacity;\n } else if (!utils.isNumber(capacity)) {\n throw new Error('capacity must be a number!');\n } else if (capacity < 0) {\n throw new Error('capacity must be greater than zero!');\n } else {\n this.$$capacity = capacity;\n }\n var removed = {};\n while ($$lruHeap.size() > this.$$capacity) {\n removed[$$lruHeap.peek().key] = this.remove($$lruHeap.peek().key);\n }\n return removed;\n },\n setDeleteOnExpire: function setDeleteOnExpire(deleteOnExpire, setRecycleFreq) {\n if (deleteOnExpire === null) {\n delete this.$$deleteOnExpire;\n } else if (!utils.isString(deleteOnExpire)) {\n throw new Error('deleteOnExpire must be a string!');\n } else if (deleteOnExpire !== 'none' && deleteOnExpire !== 'passive' && deleteOnExpire !== 'aggressive') {\n throw new Error('deleteOnExpire must be \"none\", \"passive\" or \"aggressive\"!');\n } else {\n this.$$deleteOnExpire = deleteOnExpire;\n }\n if (setRecycleFreq !== false) {\n this.setRecycleFreq(this.$$recycleFreq);\n }\n },\n setMaxAge: function setMaxAge(maxAge) {\n if (maxAge === null) {\n this.$$maxAge = Number.MAX_VALUE;\n } else if (!utils.isNumber(maxAge)) {\n throw new Error('maxAge must be a number!');\n } else if (maxAge < 0) {\n throw new Error('maxAge must be greater than zero!');\n } else {\n this.$$maxAge = maxAge;\n }\n var i = void 0,\n keys = void 0,\n key = void 0;\n\n $$expiresHeap.removeAll();\n\n if ($$storage) {\n var keysJson = $$storage().getItem(this.$$prefix + '.keys');\n\n keys = keysJson ? utils.fromJson(keysJson) : [];\n\n for (i = 0; i < keys.length; i++) {\n key = keys[i];\n var itemJson = $$storage().getItem(this.$$prefix + '.data.' + key);\n\n if (itemJson) {\n var item = utils.fromJson(itemJson);\n if (this.$$maxAge === Number.MAX_VALUE) {\n item.expires = Number.MAX_VALUE;\n } else {\n item.expires = item.created + (item.maxAge || this.$$maxAge);\n }\n $$expiresHeap.push({\n key: key,\n expires: item.expires\n });\n }\n }\n } else {\n keys = _keys($$data);\n\n for (i = 0; i < keys.length; i++) {\n key = keys[i];\n if (this.$$maxAge === Number.MAX_VALUE) {\n $$data[key].expires = Number.MAX_VALUE;\n } else {\n $$data[key].expires = $$data[key].created + ($$data[key].maxAge || this.$$maxAge);\n }\n $$expiresHeap.push($$data[key]);\n }\n }\n if (this.$$deleteOnExpire === 'aggressive') {\n return this.removeExpired();\n } else {\n return {};\n }\n },\n setOnExpire: function setOnExpire(onExpire) {\n if (onExpire === null) {\n delete this.$$onExpire;\n } else if (!utils.isFunction(onExpire)) {\n throw new Error('onExpire must be a function!');\n } else {\n this.$$onExpire = onExpire;\n }\n },\n setOptions: function setOptions(cacheOptions, strict) {\n cacheOptions = cacheOptions || {};\n strict = !!strict;\n if (!utils.isObject(cacheOptions)) {\n throw new Error('cacheOptions must be an object!');\n }\n\n if ('storagePrefix' in cacheOptions) {\n this.$$storagePrefix = cacheOptions.storagePrefix;\n } else if (strict) {\n this.$$storagePrefix = defaults.storagePrefix;\n }\n\n this.$$prefix = this.$$storagePrefix + this.$$id;\n\n if ('disabled' in cacheOptions) {\n this.$$disabled = !!cacheOptions.disabled;\n } else if (strict) {\n this.$$disabled = defaults.disabled;\n }\n\n if ('deleteOnExpire' in cacheOptions) {\n this.setDeleteOnExpire(cacheOptions.deleteOnExpire, false);\n } else if (strict) {\n this.setDeleteOnExpire(defaults.deleteOnExpire, false);\n }\n\n if ('recycleFreq' in cacheOptions) {\n this.setRecycleFreq(cacheOptions.recycleFreq);\n } else if (strict) {\n this.setRecycleFreq(defaults.recycleFreq);\n }\n\n if ('maxAge' in cacheOptions) {\n this.setMaxAge(cacheOptions.maxAge);\n } else if (strict) {\n this.setMaxAge(defaults.maxAge);\n }\n\n if ('storeOnResolve' in cacheOptions) {\n this.$$storeOnResolve = !!cacheOptions.storeOnResolve;\n } else if (strict) {\n this.$$storeOnResolve = defaults.storeOnResolve;\n }\n\n if ('storeOnReject' in cacheOptions) {\n this.$$storeOnReject = !!cacheOptions.storeOnReject;\n } else if (strict) {\n this.$$storeOnReject = defaults.storeOnReject;\n }\n\n if ('capacity' in cacheOptions) {\n this.setCapacity(cacheOptions.capacity);\n } else if (strict) {\n this.setCapacity(defaults.capacity);\n }\n\n if ('cacheFlushInterval' in cacheOptions) {\n this.setCacheFlushInterval(cacheOptions.cacheFlushInterval);\n } else if (strict) {\n this.setCacheFlushInterval(defaults.cacheFlushInterval);\n }\n\n if ('onExpire' in cacheOptions) {\n this.setOnExpire(cacheOptions.onExpire);\n } else if (strict) {\n this.setOnExpire(defaults.onExpire);\n }\n\n if ('storageMode' in cacheOptions || 'storageImpl' in cacheOptions) {\n this.setStorageMode(cacheOptions.storageMode || defaults.storageMode, cacheOptions.storageImpl || defaults.storageImpl);\n } else if (strict) {\n this.setStorageMode(defaults.storageMode, defaults.storageImpl);\n }\n },\n setRecycleFreq: function setRecycleFreq(recycleFreq) {\n if (recycleFreq === null) {\n delete this.$$recycleFreq;\n } else if (!utils.isNumber(recycleFreq)) {\n throw new Error('recycleFreq must be a number!');\n } else if (recycleFreq < 0) {\n throw new Error('recycleFreq must be greater than zero!');\n } else {\n this.$$recycleFreq = recycleFreq;\n }\n clearInterval(this.$$recycleFreqId);\n if (this.$$deleteOnExpire === 'aggressive') {\n (function (self) {\n self.$$recycleFreqId = setInterval(function () {\n self.removeExpired();\n }, self.$$recycleFreq);\n })(this);\n } else {\n delete this.$$recycleFreqId;\n }\n },\n setStorageMode: function setStorageMode(storageMode, storageImpl) {\n if (!utils.isString(storageMode)) {\n throw new Error('storageMode must be a string!');\n } else if (storageMode !== 'memory' && storageMode !== 'localStorage' && storageMode !== 'sessionStorage') {\n throw new Error('storageMode must be \"memory\", \"localStorage\" or \"sessionStorage\"!');\n }\n\n var prevStorage = $$storage;\n var prevData = $$data;\n var shouldReInsert = false;\n var items = {};\n\n function load(prevStorage, prevData) {\n var keys = this.keys();\n var length = keys.length;\n if (length) {\n var _key = void 0;\n var prevDataIsObject = utils.isObject(prevData);\n for (var i = 0; i < length; i++) {\n _key = keys[i];\n if (prevStorage) {\n var itemJson = prevStorage().getItem(this.$$prefix + '.data.' + _key);\n if (itemJson) {\n items[_key] = utils.fromJson(itemJson);\n }\n } else if (prevDataIsObject) {\n items[_key] = prevData[_key];\n }\n this.remove(_key);\n }\n shouldReInsert = true;\n }\n }\n\n if (!this.$$initializing) {\n load.call(this, prevStorage, prevData);\n }\n\n this.$$storageMode = storageMode;\n\n if (storageImpl) {\n if (!utils.isObject(storageImpl)) {\n throw new Error('storageImpl must be an object!');\n } else if (!('setItem' in storageImpl) || typeof storageImpl.setItem !== 'function') {\n throw new Error('storageImpl must implement \"setItem(key, value)\"!');\n } else if (!('getItem' in storageImpl) || typeof storageImpl.getItem !== 'function') {\n throw new Error('storageImpl must implement \"getItem(key)\"!');\n } else if (!('removeItem' in storageImpl) || typeof storageImpl.removeItem !== 'function') {\n throw new Error('storageImpl must implement \"removeItem(key)\"!');\n }\n $$storage = function $$storage() {\n return storageImpl;\n };\n } else if (this.$$storageMode === 'localStorage') {\n try {\n localStorage.setItem('cachefactory', 'cachefactory');\n localStorage.removeItem('cachefactory');\n $$storage = function $$storage() {\n return localStorage;\n };\n } catch (e) {\n $$storage = null;\n this.$$storageMode = 'memory';\n }\n } else if (this.$$storageMode === 'sessionStorage') {\n try {\n sessionStorage.setItem('cachefactory', 'cachefactory');\n sessionStorage.removeItem('cachefactory');\n $$storage = function $$storage() {\n return sessionStorage;\n };\n } catch (e) {\n $$storage = null;\n this.$$storageMode = 'memory';\n }\n } else {\n $$storage = null;\n this.$$storageMode = 'memory';\n }\n\n if (this.$$initializing) {\n load.call(this, $$storage, $$data);\n }\n\n if (shouldReInsert) {\n var item = void 0;\n for (var key in items) {\n item = items[key];\n this.put(key, item.value, {\n created: item.created,\n accessed: item.accessed,\n expires: item.expires\n });\n }\n }\n },\n touch: function touch(key, options) {\n var _this4 = this;\n\n if (key) {\n var val = this.get(key, {\n onExpire: function onExpire(k, v) {\n return _this4.put(k, v);\n }\n });\n if (val) {\n this.put(key, val, options);\n }\n } else {\n var keys = this.keys();\n for (var i = 0; i < keys.length; i++) {\n this.touch(keys[i], options);\n }\n }\n },\n values: function values() {\n var keys = this.keys();\n var items = [];\n for (var i = 0; i < keys.length; i++) {\n items.push(this.get(keys[i]));\n }\n return items;\n }\n };\n\n cache.$$initializing = true;\n cache.setOptions(options, true);\n cache.$$initializing = false;\n\n return cache;\n}\n\nfunction CacheFactory(cacheId, options) {\n return createCache(cacheId, options);\n}\n\nCacheFactory.createCache = createCache;\nCacheFactory.defaults = defaults;\n\nCacheFactory.info = function () {\n var keys = _keys(caches);\n var info = {\n size: keys.length,\n caches: {}\n };\n for (var opt in defaults) {\n if (defaults.hasOwnProperty(opt)) {\n info[opt] = defaults[opt];\n }\n }\n for (var i = 0; i < keys.length; i++) {\n var key = keys[i];\n info.caches[key] = caches[key].info();\n }\n return info;\n};\n\nCacheFactory.get = function (cacheId) {\n return caches[cacheId];\n};\nCacheFactory.keySet = function () {\n return _keySet(caches);\n};\nCacheFactory.keys = function () {\n return _keys(caches);\n};\nCacheFactory.destroy = function (cacheId) {\n if (caches[cacheId]) {\n caches[cacheId].destroy();\n delete caches[cacheId];\n }\n};\nCacheFactory.destroyAll = function () {\n for (var cacheId in caches) {\n caches[cacheId].destroy();\n }\n caches = {};\n};\nCacheFactory.clearAll = function () {\n for (var cacheId in caches) {\n caches[cacheId].removeAll();\n }\n};\nCacheFactory.removeExpiredFromAll = function () {\n var expired = {};\n for (var cacheId in caches) {\n expired[cacheId] = caches[cacheId].removeExpired();\n }\n return expired;\n};\nCacheFactory.enableAll = function () {\n for (var cacheId in caches) {\n caches[cacheId].$$disabled = false;\n }\n};\nCacheFactory.disableAll = function () {\n for (var cacheId in caches) {\n caches[cacheId].$$disabled = true;\n }\n};\nCacheFactory.touchAll = function () {\n for (var cacheId in caches) {\n caches[cacheId].touch();\n }\n};\n\nCacheFactory.utils = utils;\nCacheFactory.BinaryHeap = BinaryHeap;\n\nexport default CacheFactory;\n//# sourceMappingURL=cachefactory.es2015.js.map","import angular from 'angular'\nimport CacheFactory from '../node_modules/cachefactory/dist/cachefactory.es2015'\n\nCacheFactory.utils.equals = angular.equals\nCacheFactory.utils.isObject = angular.isObject\nCacheFactory.utils.fromJson = angular.fromJson\n\nfunction BinaryHeapProvider () {\n this.$get = function () { return CacheFactory.BinaryHeap }\n}\n\nfunction CacheFactoryProvider () {\n this.defaults = CacheFactory.defaults\n this.defaults.storagePrefix = 'angular-cache.caches.'\n\n this.$get = ['$q', function ($q) {\n CacheFactory.utils.Promise = $q\n return CacheFactory\n }]\n}\n\nangular.module('angular-cache', [])\n .provider('BinaryHeap', BinaryHeapProvider)\n .provider('CacheFactory', CacheFactoryProvider)\n\nexport default 'angular-cache'\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,EAAA,IAAI,eAAe,EAAnB;AACA,EAAA,aAAa,MAAb,GAAsB,OAAO,MAAP,KAAkB,UAAlB,IAAgC,oBAAO,OAAO,QAAd,MAA2B,QAA3D,GAAsE,UAAU,GAAV,EAAe;AACzG,EAAA,gBAAc,GAAd,qDAAc,GAAd;AACD,EAAA,CAFqB,GAElB,UAAU,GAAV,EAAe;AACjB,EAAA,SAAO,OAAO,OAAO,MAAP,KAAkB,UAAzB,IAAuC,IAAI,WAAJ,KAAoB,MAA3D,GAAoE,QAApE,UAAsF,GAAtF,qDAAsF,GAAtF,CAAP;AACD,EAAA,CAJD;AAKA;;;;;;AAQA,EAAA,IAAI,WAAW,SAAS,QAAT,CAAkB,IAAlB,EAAwB,UAAxB,EAAoC,CAApC,EAAuC;AACpD,EAAA,MAAI,UAAU,KAAK,CAAL,CAAd;AACA,EAAA,MAAI,SAAS,WAAW,OAAX,CAAb;;AAEA,EAAA,SAAO,IAAI,CAAX,EAAc;;AAEZ,EAAA,QAAI,UAAU,KAAK,KAAL,CAAW,CAAC,IAAI,CAAL,IAAU,CAArB,IAA0B,CAAxC;AACA,EAAA,QAAI,SAAS,KAAK,OAAL,CAAb;;;AAGA,EAAA,QAAI,UAAU,WAAW,MAAX,CAAd,EAAkC;AAChC,EAAA;AACD,EAAA,KAFD,MAEO;AACL,EAAA,WAAK,OAAL,IAAgB,OAAhB;AACA,EAAA,WAAK,CAAL,IAAU,MAAV;AACA,EAAA,UAAI,OAAJ;AACD,EAAA;AACF,EAAA;AACF,EAAA,CAlBD;;;;;;;;AA0BA,EAAA,IAAI,aAAa,SAAS,UAAT,CAAoB,IAApB,EAA0B,UAA1B,EAAsC,CAAtC,EAAyC;AACxD,EAAA,MAAI,SAAS,KAAK,MAAlB;AACA,EAAA,MAAI,OAAO,KAAK,CAAL,CAAX;AACA,EAAA,MAAI,aAAa,WAAW,IAAX,CAAjB;;AAEA,EAAA,SAAO,IAAP,EAAa;AACX,EAAA,QAAI,UAAU,CAAC,IAAI,CAAL,IAAU,CAAxB;AACA,EAAA,QAAI,UAAU,UAAU,CAAxB;AACA,EAAA,QAAI,OAAO,IAAX;AACA,EAAA,QAAI,UAAU,MAAd,EAAsB;AACpB,EAAA,UAAI,SAAS,KAAK,OAAL,CAAb;AACA,EAAA,UAAI,eAAe,WAAW,MAAX,CAAnB;;AAEA,EAAA,UAAI,eAAe,UAAnB,EAA+B;AAC7B,EAAA,eAAO,OAAP;AACD,EAAA;AACF,EAAA;;AAED,EAAA,QAAI,UAAU,MAAd,EAAsB;AACpB,EAAA,UAAI,SAAS,KAAK,OAAL,CAAb;AACA,EAAA,UAAI,eAAe,WAAW,MAAX,CAAnB;AACA,EAAA,UAAI,gBAAgB,SAAS,IAAT,GAAgB,UAAhB,GAA6B,WAAW,KAAK,OAAL,CAAX,CAA7C,CAAJ,EAA6E;AAC3E,EAAA,eAAO,OAAP;AACD,EAAA;AACF,EAAA;;AAED,EAAA,QAAI,SAAS,IAAb,EAAmB;AACjB,EAAA;AACD,EAAA,KAFD,MAEO;AACL,EAAA,WAAK,CAAL,IAAU,KAAK,IAAL,CAAV;AACA,EAAA,WAAK,IAAL,IAAa,IAAb;AACA,EAAA,UAAI,IAAJ;AACD,EAAA;AACF,EAAA;AACF,EAAA,CAlCD;;AAoCA,EAAA,SAAS,UAAT,CAAoB,UAApB,EAAgC,WAAhC,EAA6C;AAC3C,EAAA,MAAI,CAAC,UAAL,EAAiB;AACf,EAAA,iBAAa,SAAS,UAAT,CAAoB,CAApB,EAAuB;AAClC,EAAA,aAAO,CAAP;AACD,EAAA,KAFD;AAGD,EAAA;AACD,EAAA,MAAI,CAAC,WAAL,EAAkB;AAChB,EAAA,kBAAc,SAAS,WAAT,CAAqB,CAArB,EAAwB,CAAxB,EAA2B;AACvC,EAAA,aAAO,MAAM,CAAb;AACD,EAAA,KAFD;AAGD,EAAA;AACD,EAAA,MAAI,OAAO,UAAP,KAAsB,UAA1B,EAAsC;AACpC,EAAA,UAAM,IAAI,KAAJ,CAAU,2EAAV,CAAN;AACD,EAAA;AACD,EAAA,MAAI,OAAO,WAAP,KAAuB,UAA3B,EAAuC;AACrC,EAAA,UAAM,IAAI,KAAJ,CAAU,4EAAV,CAAN;AACD,EAAA;AACD,EAAA,OAAK,UAAL,GAAkB,UAAlB;AACA,EAAA,OAAK,WAAL,GAAmB,WAAnB;AACA,EAAA,OAAK,IAAL,GAAY,EAAZ;AACD,EAAA;;AAED,EAAA,IAAI,UAAU,WAAW,SAAzB;;AAEA,EAAA,QAAQ,IAAR,GAAe,UAAU,IAAV,EAAgB;AAC7B,EAAA,OAAK,IAAL,CAAU,IAAV,CAAe,IAAf;AACA,EAAA,WAAS,KAAK,IAAd,EAAoB,KAAK,UAAzB,EAAqC,KAAK,IAAL,CAAU,MAAV,GAAmB,CAAxD;AACD,EAAA,CAHD;;AAKA,EAAA,QAAQ,IAAR,GAAe,YAAY;AACzB,EAAA,SAAO,KAAK,IAAL,CAAU,CAAV,CAAP;AACD,EAAA,CAFD;;AAIA,EAAA,QAAQ,GAAR,GAAc,YAAY;AACxB,EAAA,MAAI,QAAQ,KAAK,IAAL,CAAU,CAAV,CAAZ;AACA,EAAA,MAAI,MAAM,KAAK,IAAL,CAAU,GAAV,EAAV;AACA,EAAA,MAAI,KAAK,IAAL,CAAU,MAAV,GAAmB,CAAvB,EAA0B;AACxB,EAAA,SAAK,IAAL,CAAU,CAAV,IAAe,GAAf;AACA,EAAA,eAAW,KAAK,IAAhB,EAAsB,KAAK,UAA3B,EAAuC,CAAvC;AACD,EAAA;AACD,EAAA,SAAO,KAAP;AACD,EAAA,CARD;;AAUA,EAAA,QAAQ,MAAR,GAAiB,UAAU,IAAV,EAAgB;AAC/B,EAAA,MAAI,SAAS,KAAK,IAAL,CAAU,MAAvB;AACA,EAAA,OAAK,IAAI,IAAI,CAAb,EAAgB,IAAI,MAApB,EAA4B,GAA5B,EAAiC;AAC/B,EAAA,QAAI,KAAK,WAAL,CAAiB,KAAK,IAAL,CAAU,CAAV,CAAjB,EAA+B,IAA/B,CAAJ,EAA0C;AACxC,EAAA,UAAI,UAAU,KAAK,IAAL,CAAU,CAAV,CAAd;AACA,EAAA,UAAI,MAAM,KAAK,IAAL,CAAU,GAAV,EAAV;AACA,EAAA,UAAI,MAAM,SAAS,CAAnB,EAAsB;AACpB,EAAA,aAAK,IAAL,CAAU,CAAV,IAAe,GAAf;AACA,EAAA,iBAAS,KAAK,IAAd,EAAoB,KAAK,UAAzB,EAAqC,CAArC;AACA,EAAA,mBAAW,KAAK,IAAhB,EAAsB,KAAK,UAA3B,EAAuC,CAAvC;AACD,EAAA;AACD,EAAA,aAAO,OAAP;AACD,EAAA;AACF,EAAA;AACD,EAAA,SAAO,IAAP;AACD,EAAA,CAfD;;AAiBA,EAAA,QAAQ,SAAR,GAAoB,YAAY;AAC9B,EAAA,OAAK,IAAL,GAAY,EAAZ;AACD,EAAA,CAFD;;AAIA,EAAA,QAAQ,IAAR,GAAe,YAAY;AACzB,EAAA,SAAO,KAAK,IAAL,CAAU,MAAjB;AACD,EAAA,CAFD;;AAIA,EAAA,IAAI,WAAW,IAAf;AACA,EAAA,IAAI;AACF,EAAA,aAAW,OAAO,OAAlB;AACD,EAAA,CAFD,CAEE,OAAO,CAAP,EAAU;;AAEZ,EAAA,IAAI,QAAQ;AACV,EAAA,YAAU,SAAS,QAAT,CAAkB,KAAlB,EAAyB;AACjC,EAAA,WAAO,OAAO,KAAP,KAAiB,QAAxB;AACD,EAAA,GAHS;AAIV,EAAA,YAAU,SAAS,QAAT,CAAkB,KAAlB,EAAyB;AACjC,EAAA,WAAO,OAAO,KAAP,KAAiB,QAAxB;AACD,EAAA,GANS;AAOV,EAAA,YAAU,SAAS,QAAT,CAAkB,KAAlB,EAAyB;AACjC,EAAA,WAAO,UAAU,IAAV,IAAkB,CAAC,OAAO,KAAP,KAAiB,WAAjB,GAA+B,WAA/B,GAA6C,aAAa,MAAb,CAAoB,KAApB,CAA9C,MAA8E,QAAvG;AACD,EAAA,GATS;AAUV,EAAA,cAAY,SAAS,UAAT,CAAoB,KAApB,EAA2B;AACrC,EAAA,WAAO,OAAO,KAAP,KAAiB,UAAxB;AACD,EAAA,GAZS;AAaV,EAAA,YAAU,SAAS,QAAT,CAAkB,KAAlB,EAAyB;AACjC,EAAA,WAAO,KAAK,KAAL,CAAW,KAAX,CAAP;AACD,EAAA,GAfS;AAgBV,EAAA,UAAQ,SAAS,MAAT,CAAgB,CAAhB,EAAmB,CAAnB,EAAsB;AAC5B,EAAA,WAAO,MAAM,CAAb;AACD,EAAA,GAlBS;;AAqBV,EAAA,WAAS;AArBC,EAAA,CAAZ;;AAwBA,EAAA,SAAS,KAAT,CAAe,UAAf,EAA2B;AACzB,EAAA,MAAI,OAAO,EAAX;AACA,EAAA,MAAI,MAAM,KAAK,CAAf;AACA,EAAA,MAAI,CAAC,MAAM,QAAN,CAAe,UAAf,CAAL,EAAiC;AAC/B,EAAA,WAAO,IAAP;AACD,EAAA;AACD,EAAA,OAAK,GAAL,IAAY,UAAZ,EAAwB;AACtB,EAAA,QAAI,WAAW,cAAX,CAA0B,GAA1B,CAAJ,EAAoC;AAClC,EAAA,WAAK,IAAL,CAAU,GAAV;AACD,EAAA;AACF,EAAA;AACD,EAAA,SAAO,IAAP;AACD,EAAA;;AAED,EAAA,SAAS,cAAT,CAAwB,KAAxB,EAA+B;AAC7B,EAAA,SAAO,SAAS,OAAO,MAAM,IAAb,KAAsB,UAAtC;AACD,EAAA;;AAED,EAAA,SAAS,gBAAT,CAA0B,MAA1B,EAAkC;AAChC,EAAA,MAAI,MAAM,QAAN,CAAe,MAAf,CAAJ,EAA4B;AAC1B,EAAA,WAAO,OAAO,QAAP,EAAP;AACD,EAAA;AACD,EAAA,SAAO,MAAP;AACD,EAAA;;AAED,EAAA,SAAS,OAAT,CAAiB,UAAjB,EAA6B;AAC3B,EAAA,MAAI,SAAS,EAAb;AACA,EAAA,MAAI,MAAM,KAAK,CAAf;AACA,EAAA,MAAI,CAAC,MAAM,QAAN,CAAe,UAAf,CAAL,EAAiC;AAC/B,EAAA,WAAO,MAAP;AACD,EAAA;AACD,EAAA,OAAK,GAAL,IAAY,UAAZ,EAAwB;AACtB,EAAA,QAAI,WAAW,cAAX,CAA0B,GAA1B,CAAJ,EAAoC;AAClC,EAAA,aAAO,GAAP,IAAc,GAAd;AACD,EAAA;AACF,EAAA;AACD,EAAA,SAAO,MAAP;AACD,EAAA;;AAED,EAAA,IAAI,WAAW;AACb,EAAA,YAAU,OAAO,SADJ;AAEb,EAAA,UAAQ,OAAO,SAFF;AAGb,EAAA,kBAAgB,MAHH;AAIb,EAAA,YAAU,IAJG;AAKb,EAAA,sBAAoB,IALP;AAMb,EAAA,eAAa,IANA;AAOb,EAAA,eAAa,QAPA;AAQb,EAAA,eAAa,IARA;AASb,EAAA,YAAU,KATG;AAUb,EAAA,iBAAe,sBAVF;AAWb,EAAA,kBAAgB,KAXH;AAYb,EAAA,iBAAe;AAZF,EAAA,CAAf;;AAeA,EAAA,IAAI,SAAS,EAAb;;AAEA,EAAA,SAAS,WAAT,CAAqB,OAArB,EAA8B,OAA9B,EAAuC;AACrC,EAAA,MAAI,WAAW,MAAf,EAAuB;AACrB,EAAA,UAAM,IAAI,KAAJ,CAAU,UAAU,kBAApB,CAAN;AACD,EAAA,GAFD,MAEO,IAAI,CAAC,MAAM,QAAN,CAAe,OAAf,CAAL,EAA8B;AACnC,EAAA,UAAM,IAAI,KAAJ,CAAU,2BAAV,CAAN;AACD,EAAA;;AAED,EAAA,MAAI,SAAS,EAAb;AACA,EAAA,MAAI,aAAa,EAAjB;AACA,EAAA,MAAI,YAAY,IAAhB;AACA,EAAA,MAAI,gBAAgB,IAAI,UAAJ,CAAe,UAAU,CAAV,EAAa;AAC9C,EAAA,WAAO,EAAE,OAAT;AACD,EAAA,GAFmB,EAEjB,MAAM,MAFW,CAApB;AAGA,EAAA,MAAI,YAAY,IAAI,UAAJ,CAAe,UAAU,CAAV,EAAa;AAC1C,EAAA,WAAO,EAAE,QAAT;AACD,EAAA,GAFe,EAEb,MAAM,MAFO,CAAhB;;AAIA,EAAA,MAAI,QAAQ,OAAO,OAAP,IAAkB;;AAE5B,EAAA,UAAM,OAFsB;;AAI5B,EAAA,aAAS,SAAS,OAAT,GAAmB;AAC1B,EAAA,oBAAc,KAAK,sBAAnB;AACA,EAAA,oBAAc,KAAK,eAAnB;AACA,EAAA,WAAK,SAAL;AACA,EAAA,UAAI,SAAJ,EAAe;AACb,EAAA,oBAAY,UAAZ,CAAuB,KAAK,QAAL,GAAgB,OAAvC;AACA,EAAA,oBAAY,UAAZ,CAAuB,KAAK,QAA5B;AACD,EAAA;AACD,EAAA,kBAAY,IAAZ;AACA,EAAA,eAAS,IAAT;AACA,EAAA,kBAAY,IAAZ;AACA,EAAA,sBAAgB,IAAhB;AACA,EAAA,WAAK,QAAL,GAAgB,IAAhB;AACA,EAAA,aAAO,OAAO,KAAK,IAAZ,CAAP;AACD,EAAA,KAlB2B;AAmB5B,EAAA,aAAS,SAAS,OAAT,GAAmB;AAC1B,EAAA,WAAK,UAAL,GAAkB,IAAlB;AACD,EAAA,KArB2B;AAsB5B,EAAA,YAAQ,SAAS,MAAT,GAAkB;AACxB,EAAA,aAAO,KAAK,UAAZ;AACD,EAAA,KAxB2B;AAyB5B,EAAA,SAAK,SAAS,GAAT,CAAa,GAAb,EAAkB,OAAlB,EAA2B;AAC9B,EAAA,UAAI,SAAS,IAAb;;AAEA,EAAA,UAAI,MAAM,OAAN,CAAc,GAAd,CAAJ,EAAwB;AACtB,EAAA,YAAI,OAAO,YAAY;AACrB,EAAA,cAAI,OAAO,GAAX;AACA,EAAA,cAAI,SAAS,EAAb;;AAEA,EAAA,eAAK,OAAL,CAAa,UAAU,GAAV,EAAe;AAC1B,EAAA,gBAAI,QAAQ,OAAO,GAAP,CAAW,GAAX,EAAgB,OAAhB,CAAZ;AACA,EAAA,gBAAI,UAAU,IAAV,IAAkB,UAAU,SAAhC,EAA2C;AACzC,EAAA,qBAAO,IAAP,CAAY,KAAZ;AACD,EAAA;AACF,EAAA,WALD;;AAOA,EAAA,iBAAO;AACL,EAAA,eAAG;AADE,EAAA,WAAP;AAGD,EAAA,SAdU,EAAX;;AAgBA,EAAA,YAAI,CAAC,OAAO,IAAP,KAAgB,WAAhB,GAA8B,WAA9B,GAA4C,aAAa,MAAb,CAAoB,IAApB,CAA7C,MAA4E,QAAhF,EAA0F,OAAO,KAAK,CAAZ;AAC3F,EAAA,OAlBD,MAkBO;AACL,EAAA,cAAM,iBAAiB,GAAjB,CAAN;;AAEA,EAAA,YAAI,KAAK,UAAT,EAAqB;AACnB,EAAA;AACD,EAAA;AACF,EAAA;;AAED,EAAA,gBAAU,WAAW,EAArB;AACA,EAAA,UAAI,CAAC,MAAM,QAAN,CAAe,GAAf,CAAL,EAA0B;AACxB,EAAA,cAAM,IAAI,KAAJ,CAAU,uBAAV,CAAN;AACD,EAAA,OAFD,MAEO,IAAI,WAAW,CAAC,MAAM,QAAN,CAAe,OAAf,CAAhB,EAAyC;AAC9C,EAAA,cAAM,IAAI,KAAJ,CAAU,4BAAV,CAAN;AACD,EAAA,OAFM,MAEA,IAAI,QAAQ,QAAR,IAAoB,CAAC,MAAM,UAAN,CAAiB,QAAQ,QAAzB,CAAzB,EAA6D;AAClE,EAAA,cAAM,IAAI,KAAJ,CAAU,sCAAV,CAAN;AACD,EAAA;;AAED,EAAA,UAAI,OAAO,KAAK,CAAhB;;AAEA,EAAA,UAAI,SAAJ,EAAe;AACb,EAAA,YAAI,WAAW,GAAX,CAAJ,EAAqB;AACnB,EAAA,iBAAO,WAAW,GAAX,CAAP;AACD,EAAA;;AAED,EAAA,YAAI,WAAW,YAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,QAAhB,GAA2B,GAA/C,CAAf;;AAEA,EAAA,YAAI,QAAJ,EAAc;AACZ,EAAA,iBAAO,MAAM,QAAN,CAAe,QAAf,CAAP;AACD,EAAA,SAFD,MAEO;AACL,EAAA;AACD,EAAA;AACF,EAAA,OAZD,MAYO,IAAI,MAAM,QAAN,CAAe,MAAf,CAAJ,EAA4B;AACjC,EAAA,YAAI,EAAE,OAAO,MAAT,CAAJ,EAAsB;AACpB,EAAA;AACD,EAAA;;AAED,EAAA,eAAO,OAAO,GAAP,CAAP;AACD,EAAA;;AAED,EAAA,UAAI,QAAQ,KAAK,KAAjB;AACA,EAAA,UAAI,MAAM,IAAI,IAAJ,GAAW,OAAX,EAAV;;AAEA,EAAA,UAAI,SAAJ,EAAe;AACb,EAAA,kBAAU,MAAV,CAAiB;AACf,EAAA,eAAK,GADU;AAEf,EAAA,oBAAU,KAAK;AAFA,EAAA,SAAjB;AAIA,EAAA,aAAK,QAAL,GAAgB,GAAhB;AACA,EAAA,kBAAU,IAAV,CAAe;AACb,EAAA,eAAK,GADQ;AAEb,EAAA,oBAAU;AAFG,EAAA,SAAf;AAID,EAAA,OAVD,MAUO;AACL,EAAA,kBAAU,MAAV,CAAiB,IAAjB;AACA,EAAA,aAAK,QAAL,GAAgB,GAAhB;AACA,EAAA,kBAAU,IAAV,CAAe,IAAf;AACD,EAAA;;AAED,EAAA,UAAI,KAAK,gBAAL,KAA0B,SAA1B,IAAuC,aAAa,IAApD,IAA4D,KAAK,OAAL,GAAe,GAA/E,EAAoF;AAClF,EAAA,aAAK,MAAL,CAAY,GAAZ;;AAEA,EAAA,YAAI,KAAK,UAAT,EAAqB;AACnB,EAAA,eAAK,UAAL,CAAgB,GAAhB,EAAqB,KAAK,KAA1B,EAAiC,QAAQ,QAAzC;AACD,EAAA,SAFD,MAEO,IAAI,QAAQ,QAAZ,EAAsB;AAC3B,EAAA,kBAAQ,QAAR,CAAiB,IAAjB,CAAsB,IAAtB,EAA4B,GAA5B,EAAiC,KAAK,KAAtC;AACD,EAAA;AACD,EAAA,gBAAQ,SAAR;AACD,EAAA,OATD,MASO,IAAI,SAAJ,EAAe;AACpB,EAAA,oBAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,QAAhB,GAA2B,GAA/C,EAAoD,KAAK,SAAL,CAAe,IAAf,CAApD;AACD,EAAA;;AAED,EAAA,aAAO,KAAP;AACD,EAAA,KAtH2B;AAuH5B,EAAA,UAAM,SAAS,IAAT,CAAc,GAAd,EAAmB;AACvB,EAAA,UAAI,GAAJ,EAAS;AACP,EAAA,YAAI,OAAO,KAAK,CAAhB;AACA,EAAA,YAAI,SAAJ,EAAe;AACb,EAAA,cAAI,WAAW,YAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,QAAhB,GAA2B,GAA/C,CAAf;;AAEA,EAAA,cAAI,QAAJ,EAAc;AACZ,EAAA,mBAAO,MAAM,QAAN,CAAe,QAAf,CAAP;AACA,EAAA,mBAAO;AACL,EAAA,uBAAS,KAAK,OADT;AAEL,EAAA,wBAAU,KAAK,QAFV;AAGL,EAAA,uBAAS,KAAK,OAHT;AAIL,EAAA,yBAAW,IAAI,IAAJ,GAAW,OAAX,KAAuB,KAAK,OAA5B,IAAuC,KAAK,MAAL,IAAe,KAAK,QAA3D;AAJN,EAAA,aAAP;AAMD,EAAA,WARD,MAQO;AACL,EAAA,mBAAO,SAAP;AACD,EAAA;AACF,EAAA,SAdD,MAcO,IAAI,MAAM,QAAN,CAAe,MAAf,KAA0B,OAAO,MAArC,EAA6C;AAClD,EAAA,iBAAO,OAAO,GAAP,CAAP;;AAEA,EAAA,iBAAO;AACL,EAAA,qBAAS,KAAK,OADT;AAEL,EAAA,sBAAU,KAAK,QAFV;AAGL,EAAA,qBAAS,KAAK,OAHT;AAIL,EAAA,uBAAW,IAAI,IAAJ,GAAW,OAAX,KAAuB,KAAK,OAA5B,IAAuC,KAAK,MAAL,IAAe,KAAK,QAA3D;AAJN,EAAA,WAAP;AAMD,EAAA,SATM,MASA;AACL,EAAA,iBAAO,SAAP;AACD,EAAA;AACF,EAAA,OA5BD,MA4BO;AACL,EAAA,eAAO;AACL,EAAA,cAAI,KAAK,IADJ;AAEL,EAAA,oBAAU,KAAK,UAFV;AAGL,EAAA,kBAAQ,KAAK,QAHR;AAIL,EAAA,0BAAgB,KAAK,gBAJhB;AAKL,EAAA,oBAAU,KAAK,UALV;AAML,EAAA,8BAAoB,KAAK,oBANpB;AAOL,EAAA,uBAAa,KAAK,aAPb;AAQL,EAAA,uBAAa,KAAK,aARb;AASL,EAAA,uBAAa,YAAY,WAAZ,GAA0B,SATlC;AAUL,EAAA,oBAAU,CAAC,CAAC,KAAK,UAVZ;AAWL,EAAA,gBAAM,aAAa,UAAU,IAAV,EAAb,IAAiC;AAXlC,EAAA,SAAP;AAaD,EAAA;AACF,EAAA,KAnK2B;AAoK5B,EAAA,UAAM,SAAS,IAAT,GAAgB;AACpB,EAAA,UAAI,SAAJ,EAAe;AACb,EAAA,YAAI,WAAW,YAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,OAApC,CAAf;;AAEA,EAAA,YAAI,QAAJ,EAAc;AACZ,EAAA,iBAAO,MAAM,QAAN,CAAe,QAAf,CAAP;AACD,EAAA,SAFD,MAEO;AACL,EAAA,iBAAO,EAAP;AACD,EAAA;AACF,EAAA,OARD,MAQO;AACL,EAAA,eAAO,MAAM,MAAN,CAAP;AACD,EAAA;AACF,EAAA,KAhL2B;AAiL5B,EAAA,YAAQ,SAAS,MAAT,GAAkB;AACxB,EAAA,UAAI,SAAJ,EAAe;AACb,EAAA,YAAI,WAAW,YAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,OAApC,CAAf;AACA,EAAA,YAAI,OAAO,EAAX;;AAEA,EAAA,YAAI,QAAJ,EAAc;AACZ,EAAA,cAAI,OAAO,MAAM,QAAN,CAAe,QAAf,CAAX;;AAEA,EAAA,eAAK,IAAI,IAAI,CAAb,EAAgB,IAAI,KAAK,MAAzB,EAAiC,GAAjC,EAAsC;AACpC,EAAA,iBAAK,KAAK,CAAL,CAAL,IAAgB,KAAK,CAAL,CAAhB;AACD,EAAA;AACF,EAAA;AACD,EAAA,eAAO,IAAP;AACD,EAAA,OAZD,MAYO;AACL,EAAA,eAAO,QAAQ,MAAR,CAAP;AACD,EAAA;AACF,EAAA,KAjM2B;AAkM5B,EAAA,SAAK,SAAS,GAAT,CAAa,GAAb,EAAkB,KAAlB,EAAyB,OAAzB,EAAkC;AACrC,EAAA,UAAI,SAAS,IAAb;;AAEA,EAAA,kBAAY,UAAU,EAAtB;;AAEA,EAAA,UAAI,iBAAiB,oBAAoB,OAApB,GAA8B,CAAC,CAAC,QAAQ,cAAxC,GAAyD,KAAK,gBAAnF;AACA,EAAA,UAAI,gBAAgB,mBAAmB,OAAnB,GAA6B,CAAC,CAAC,QAAQ,aAAvC,GAAuD,KAAK,eAAhF;;AAEA,EAAA,UAAI,aAAa,SAAS,UAAT,CAAoB,KAApB,EAA2B,OAA3B,EAAoC;AACnD,EAAA,eAAO,UAAU,CAAV,EAAa;AAClB,EAAA,cAAI,KAAJ,EAAW;AACT,EAAA,mBAAO,WAAW,GAAX,CAAP;AACA,EAAA,gBAAI,MAAM,QAAN,CAAe,CAAf,KAAqB,YAAY,CAAjC,IAAsC,UAAU,CAApD,EAAuD;AACrD,EAAA,kBAAI,CAAC,EAAE,MAAH,EAAW,EAAE,IAAb,EAAmB,EAAE,OAAF,EAAnB,EAAgC,EAAE,UAAlC,CAAJ;AACA,EAAA,qBAAO,GAAP,CAAW,GAAX,EAAgB,CAAhB;AACD,EAAA,aAHD,MAGO;AACL,EAAA,qBAAO,GAAP,CAAW,GAAX,EAAgB,CAAhB;AACD,EAAA;AACF,EAAA;AACD,EAAA,cAAI,OAAJ,EAAa;AACX,EAAA,gBAAI,MAAM,OAAV,EAAmB;AACjB,EAAA,qBAAO,MAAM,OAAN,CAAc,MAAd,CAAqB,CAArB,CAAP;AACD,EAAA,aAFD,MAEO;AACL,EAAA,oBAAM,CAAN;AACD,EAAA;AACF,EAAA,WAND,MAMO;AACL,EAAA,mBAAO,CAAP;AACD,EAAA;AACF,EAAA,SAnBD;AAoBD,EAAA,OArBD;;AAuBA,EAAA,UAAI,KAAK,UAAL,IAAmB,CAAC,MAAM,QAAN,CAAe,MAAf,CAApB,IAA8C,UAAU,IAAxD,IAAgE,UAAU,SAA9E,EAAyF;AACvF,EAAA;AACD,EAAA;AACD,EAAA,YAAM,iBAAiB,GAAjB,CAAN;;AAEA,EAAA,UAAI,CAAC,MAAM,QAAN,CAAe,GAAf,CAAL,EAA0B;AACxB,EAAA,cAAM,IAAI,KAAJ,CAAU,uBAAV,CAAN;AACD,EAAA;;AAED,EAAA,UAAI,MAAM,IAAI,IAAJ,GAAW,OAAX,EAAV;AACA,EAAA,UAAI,OAAO;AACT,EAAA,aAAK,GADI;AAET,EAAA,eAAO,eAAe,KAAf,IAAwB,MAAM,IAAN,CAAW,WAAW,cAAX,EAA2B,KAA3B,CAAX,EAA8C,WAAW,aAAX,EAA0B,IAA1B,CAA9C,CAAxB,GAAyG,KAFvG;AAGT,EAAA,iBAAS,QAAQ,OAAR,KAAoB,SAApB,GAAgC,GAAhC,GAAsC,QAAQ,OAH9C;AAIT,EAAA,kBAAU,QAAQ,QAAR,KAAqB,SAArB,GAAiC,GAAjC,GAAuC,QAAQ;AAJhD,EAAA,OAAX;AAMA,EAAA,UAAI,QAAQ,MAAZ,EAAoB;AAClB,EAAA,aAAK,MAAL,GAAc,QAAQ,MAAtB;AACD,EAAA;;AAED,EAAA,UAAI,QAAQ,OAAR,KAAoB,SAAxB,EAAmC;AACjC,EAAA,aAAK,OAAL,GAAe,KAAK,OAAL,IAAgB,KAAK,MAAL,IAAe,KAAK,QAApC,CAAf;AACD,EAAA,OAFD,MAEO;AACL,EAAA,aAAK,OAAL,GAAe,QAAQ,OAAvB;AACD,EAAA;;AAED,EAAA,UAAI,SAAJ,EAAe;AACb,EAAA,YAAI,eAAe,KAAK,KAApB,CAAJ,EAAgC;AAC9B,EAAA,qBAAW,GAAX,IAAkB,KAAK,KAAvB;AACA,EAAA,iBAAO,WAAW,GAAX,CAAP;AACD,EAAA;AACD,EAAA,YAAI,WAAW,YAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,OAApC,CAAf;AACA,EAAA,YAAI,OAAO,WAAW,MAAM,QAAN,CAAe,QAAf,CAAX,GAAsC,EAAjD;AACA,EAAA,YAAI,WAAW,YAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,QAAhB,GAA2B,GAA/C,CAAf;;;AAGA,EAAA,YAAI,QAAJ,EAAc;AACZ,EAAA,eAAK,MAAL,CAAY,GAAZ;AACD,EAAA;;AAED,EAAA,sBAAc,IAAd,CAAmB;AACjB,EAAA,eAAK,GADY;AAEjB,EAAA,mBAAS,KAAK;AAFG,EAAA,SAAnB;;AAKA,EAAA,kBAAU,IAAV,CAAe;AACb,EAAA,eAAK,GADQ;AAEb,EAAA,oBAAU,KAAK;AAFF,EAAA,SAAf;;AAKA,EAAA,oBAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,QAAhB,GAA2B,GAA/C,EAAoD,KAAK,SAAL,CAAe,IAAf,CAApD;AACA,EAAA,YAAI,SAAS,KAAb;AACA,EAAA,aAAK,IAAI,IAAI,CAAb,EAAgB,IAAI,KAAK,MAAzB,EAAiC,GAAjC,EAAsC;AACpC,EAAA,cAAI,KAAK,CAAL,MAAY,GAAhB,EAAqB;AACnB,EAAA,qBAAS,IAAT;AACA,EAAA;AACD,EAAA;AACF,EAAA;AACD,EAAA,YAAI,CAAC,MAAL,EAAa;AACX,EAAA,eAAK,IAAL,CAAU,GAAV;AACD,EAAA;AACD,EAAA,oBAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,OAApC,EAA6C,KAAK,SAAL,CAAe,IAAf,CAA7C;AACD,EAAA,OApCD,MAoCO;;AAEL,EAAA,YAAI,OAAO,GAAP,CAAJ,EAAiB;AACf,EAAA,eAAK,MAAL,CAAY,GAAZ;AACD,EAAA;;AAED,EAAA,sBAAc,IAAd,CAAmB,IAAnB;;AAEA,EAAA,kBAAU,IAAV,CAAe,IAAf;;AAEA,EAAA,eAAO,GAAP,IAAc,IAAd;AACA,EAAA,eAAO,WAAW,GAAX,CAAP;AACD,EAAA;;;AAGD,EAAA,UAAI,UAAU,IAAV,KAAmB,KAAK,UAA5B,EAAwC;AACtC,EAAA,aAAK,MAAL,CAAY,UAAU,IAAV,GAAiB,GAA7B;AACD,EAAA;;AAED,EAAA,aAAO,KAAP;AACD,EAAA,KAnT2B;AAoT5B,EAAA,YAAQ,SAAS,MAAT,CAAgB,GAAhB,EAAqB;AAC3B,EAAA,aAAO,EAAP;AACA,EAAA,aAAO,WAAW,GAAX,CAAP;AACA,EAAA,UAAI,SAAJ,EAAe;AACb,EAAA,YAAI,WAAW,YAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,QAAhB,GAA2B,GAA/C,CAAf;;AAEA,EAAA,YAAI,QAAJ,EAAc;AACZ,EAAA,cAAI,OAAO,MAAM,QAAN,CAAe,QAAf,CAAX;AACA,EAAA,oBAAU,MAAV,CAAiB;AACf,EAAA,iBAAK,GADU;AAEf,EAAA,sBAAU,KAAK;AAFA,EAAA,WAAjB;AAIA,EAAA,wBAAc,MAAd,CAAqB;AACnB,EAAA,iBAAK,GADc;AAEnB,EAAA,qBAAS,KAAK;AAFK,EAAA,WAArB;AAIA,EAAA,sBAAY,UAAZ,CAAuB,KAAK,QAAL,GAAgB,QAAhB,GAA2B,GAAlD;AACA,EAAA,cAAI,WAAW,YAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,OAApC,CAAf;AACA,EAAA,cAAI,OAAO,WAAW,MAAM,QAAN,CAAe,QAAf,CAAX,GAAsC,EAAjD;AACA,EAAA,cAAI,QAAQ,KAAK,OAAL,CAAa,GAAb,CAAZ;;AAEA,EAAA,cAAI,SAAS,CAAb,EAAgB;AACd,EAAA,iBAAK,MAAL,CAAY,KAAZ,EAAmB,CAAnB;AACD,EAAA;AACD,EAAA,sBAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,OAApC,EAA6C,KAAK,SAAL,CAAe,IAAf,CAA7C;AACA,EAAA,iBAAO,KAAK,KAAZ;AACD,EAAA;AACF,EAAA,OAxBD,MAwBO,IAAI,MAAM,QAAN,CAAe,MAAf,CAAJ,EAA4B;AACjC,EAAA,YAAI,QAAQ,OAAO,GAAP,IAAc,OAAO,GAAP,EAAY,KAA1B,GAAkC,SAA9C;AACA,EAAA,kBAAU,MAAV,CAAiB,OAAO,GAAP,CAAjB;AACA,EAAA,sBAAc,MAAd,CAAqB,OAAO,GAAP,CAArB;AACA,EAAA,eAAO,GAAP,IAAc,IAAd;AACA,EAAA,eAAO,OAAO,GAAP,CAAP;AACA,EAAA,eAAO,KAAP;AACD,EAAA;AACF,EAAA,KAvV2B;AAwV5B,EAAA,eAAW,SAAS,SAAT,GAAqB;AAC9B,EAAA,UAAI,SAAJ,EAAe;AACb,EAAA,kBAAU,SAAV;AACA,EAAA,sBAAc,SAAd;AACA,EAAA,YAAI,WAAW,YAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,OAApC,CAAf;;AAEA,EAAA,YAAI,QAAJ,EAAc;AACZ,EAAA,cAAI,OAAO,MAAM,QAAN,CAAe,QAAf,CAAX;;AAEA,EAAA,eAAK,IAAI,IAAI,CAAb,EAAgB,IAAI,KAAK,MAAzB,EAAiC,GAAjC,EAAsC;AACpC,EAAA,iBAAK,MAAL,CAAY,KAAK,CAAL,CAAZ;AACD,EAAA;AACF,EAAA;AACD,EAAA,oBAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,OAApC,EAA6C,KAAK,SAAL,CAAe,EAAf,CAA7C;AACD,EAAA,OAbD,MAaO,IAAI,MAAM,QAAN,CAAe,MAAf,CAAJ,EAA4B;AACjC,EAAA,kBAAU,SAAV;AACA,EAAA,sBAAc,SAAd;AACA,EAAA,aAAK,IAAI,GAAT,IAAgB,MAAhB,EAAwB;AACtB,EAAA,iBAAO,GAAP,IAAc,IAAd;AACD,EAAA;AACD,EAAA,iBAAS,EAAT;AACD,EAAA,OAPM,MAOA;AACL,EAAA,kBAAU,SAAV;AACA,EAAA,sBAAc,SAAd;AACA,EAAA,iBAAS,EAAT;AACD,EAAA;AACD,EAAA,mBAAa,EAAb;AACD,EAAA,KAnX2B;AAoX5B,EAAA,mBAAe,SAAS,aAAT,GAAyB;AACtC,EAAA,UAAI,MAAM,IAAI,IAAJ,GAAW,OAAX,EAAV;AACA,EAAA,UAAI,UAAU,EAAd;AACA,EAAA,UAAI,MAAM,KAAK,CAAf;AACA,EAAA,UAAI,cAAc,KAAK,CAAvB;;AAEA,EAAA,aAAO,CAAC,cAAc,cAAc,IAAd,EAAf,KAAwC,YAAY,OAAZ,IAAuB,GAAtE,EAA2E;AACzE,EAAA,gBAAQ,YAAY,GAApB,IAA2B,YAAY,KAAZ,GAAoB,YAAY,KAAhC,GAAwC,IAAnE;AACA,EAAA,sBAAc,GAAd;AACD,EAAA;;AAED,EAAA,UAAI,SAAJ,EAAe;AACb,EAAA,aAAK,GAAL,IAAY,OAAZ,EAAqB;AACnB,EAAA,cAAI,WAAW,YAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,QAAhB,GAA2B,GAA/C,CAAf;AACA,EAAA,cAAI,QAAJ,EAAc;AACZ,EAAA,oBAAQ,GAAR,IAAe,MAAM,QAAN,CAAe,QAAf,EAAyB,KAAxC;AACA,EAAA,iBAAK,MAAL,CAAY,GAAZ;AACD,EAAA;AACF,EAAA;AACF,EAAA,OARD,MAQO;AACL,EAAA,aAAK,GAAL,IAAY,OAAZ,EAAqB;AACnB,EAAA,eAAK,MAAL,CAAY,GAAZ;AACD,EAAA;AACF,EAAA;;AAED,EAAA,UAAI,KAAK,UAAT,EAAqB;AACnB,EAAA,aAAK,GAAL,IAAY,OAAZ,EAAqB;AACnB,EAAA,eAAK,UAAL,CAAgB,GAAhB,EAAqB,QAAQ,GAAR,CAArB;AACD,EAAA;AACF,EAAA;;AAED,EAAA,aAAO,OAAP;AACD,EAAA,KApZ2B;AAqZ5B,EAAA,2BAAuB,SAAS,qBAAT,CAA+B,kBAA/B,EAAmD;AACxE,EAAA,UAAI,QAAQ,IAAZ;AACA,EAAA,UAAI,uBAAuB,IAA3B,EAAiC;AAC/B,EAAA,eAAO,MAAM,oBAAb;AACD,EAAA,OAFD,MAEO,IAAI,CAAC,MAAM,QAAN,CAAe,kBAAf,CAAL,EAAyC;AAC9C,EAAA,cAAM,IAAI,KAAJ,CAAU,sCAAV,CAAN;AACD,EAAA,OAFM,MAEA,IAAI,qBAAqB,CAAzB,EAA4B;AACjC,EAAA,cAAM,IAAI,KAAJ,CAAU,+CAAV,CAAN;AACD,EAAA,OAFM,MAEA,IAAI,uBAAuB,MAAM,oBAAjC,EAAuD;AAC5D,EAAA,cAAM,oBAAN,GAA6B,kBAA7B;;AAEA,EAAA,sBAAc,MAAM,sBAApB;;AAEA,EAAA,cAAM,sBAAN,GAA+B,YAAY,YAAY;AACrD,EAAA,gBAAM,SAAN;AACD,EAAA,SAF8B,EAE5B,MAAM,oBAFsB,CAA/B;AAGD,EAAA;AACF,EAAA,KAta2B;AAua5B,EAAA,iBAAa,SAAS,WAAT,CAAqB,QAArB,EAA+B;AAC1C,EAAA,UAAI,aAAa,IAAjB,EAAuB;AACrB,EAAA,eAAO,KAAK,UAAZ;AACD,EAAA,OAFD,MAEO,IAAI,CAAC,MAAM,QAAN,CAAe,QAAf,CAAL,EAA+B;AACpC,EAAA,cAAM,IAAI,KAAJ,CAAU,4BAAV,CAAN;AACD,EAAA,OAFM,MAEA,IAAI,WAAW,CAAf,EAAkB;AACvB,EAAA,cAAM,IAAI,KAAJ,CAAU,qCAAV,CAAN;AACD,EAAA,OAFM,MAEA;AACL,EAAA,aAAK,UAAL,GAAkB,QAAlB;AACD,EAAA;AACD,EAAA,UAAI,UAAU,EAAd;AACA,EAAA,aAAO,UAAU,IAAV,KAAmB,KAAK,UAA/B,EAA2C;AACzC,EAAA,gBAAQ,UAAU,IAAV,GAAiB,GAAzB,IAAgC,KAAK,MAAL,CAAY,UAAU,IAAV,GAAiB,GAA7B,CAAhC;AACD,EAAA;AACD,EAAA,aAAO,OAAP;AACD,EAAA,KAtb2B;AAub5B,EAAA,uBAAmB,SAAS,iBAAT,CAA2B,cAA3B,EAA2C,cAA3C,EAA2D;AAC5E,EAAA,UAAI,mBAAmB,IAAvB,EAA6B;AAC3B,EAAA,eAAO,KAAK,gBAAZ;AACD,EAAA,OAFD,MAEO,IAAI,CAAC,MAAM,QAAN,CAAe,cAAf,CAAL,EAAqC;AAC1C,EAAA,cAAM,IAAI,KAAJ,CAAU,kCAAV,CAAN;AACD,EAAA,OAFM,MAEA,IAAI,mBAAmB,MAAnB,IAA6B,mBAAmB,SAAhD,IAA6D,mBAAmB,YAApF,EAAkG;AACvG,EAAA,cAAM,IAAI,KAAJ,CAAU,2DAAV,CAAN;AACD,EAAA,OAFM,MAEA;AACL,EAAA,aAAK,gBAAL,GAAwB,cAAxB;AACD,EAAA;AACD,EAAA,UAAI,mBAAmB,KAAvB,EAA8B;AAC5B,EAAA,aAAK,cAAL,CAAoB,KAAK,aAAzB;AACD,EAAA;AACF,EAAA,KApc2B;AAqc5B,EAAA,eAAW,SAAS,SAAT,CAAmB,MAAnB,EAA2B;AACpC,EAAA,UAAI,WAAW,IAAf,EAAqB;AACnB,EAAA,aAAK,QAAL,GAAgB,OAAO,SAAvB;AACD,EAAA,OAFD,MAEO,IAAI,CAAC,MAAM,QAAN,CAAe,MAAf,CAAL,EAA6B;AAClC,EAAA,cAAM,IAAI,KAAJ,CAAU,0BAAV,CAAN;AACD,EAAA,OAFM,MAEA,IAAI,SAAS,CAAb,EAAgB;AACrB,EAAA,cAAM,IAAI,KAAJ,CAAU,mCAAV,CAAN;AACD,EAAA,OAFM,MAEA;AACL,EAAA,aAAK,QAAL,GAAgB,MAAhB;AACD,EAAA;AACD,EAAA,UAAI,IAAI,KAAK,CAAb;YACI,OAAO,KAAK,CADhB;YAEI,MAAM,KAAK,CAFf;;AAIA,EAAA,oBAAc,SAAd;;AAEA,EAAA,UAAI,SAAJ,EAAe;AACb,EAAA,YAAI,WAAW,YAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,OAApC,CAAf;;AAEA,EAAA,eAAO,WAAW,MAAM,QAAN,CAAe,QAAf,CAAX,GAAsC,EAA7C;;AAEA,EAAA,aAAK,IAAI,CAAT,EAAY,IAAI,KAAK,MAArB,EAA6B,GAA7B,EAAkC;AAChC,EAAA,gBAAM,KAAK,CAAL,CAAN;AACA,EAAA,cAAI,WAAW,YAAY,OAAZ,CAAoB,KAAK,QAAL,GAAgB,QAAhB,GAA2B,GAA/C,CAAf;;AAEA,EAAA,cAAI,QAAJ,EAAc;AACZ,EAAA,gBAAI,OAAO,MAAM,QAAN,CAAe,QAAf,CAAX;AACA,EAAA,gBAAI,KAAK,QAAL,KAAkB,OAAO,SAA7B,EAAwC;AACtC,EAAA,mBAAK,OAAL,GAAe,OAAO,SAAtB;AACD,EAAA,aAFD,MAEO;AACL,EAAA,mBAAK,OAAL,GAAe,KAAK,OAAL,IAAgB,KAAK,MAAL,IAAe,KAAK,QAApC,CAAf;AACD,EAAA;AACD,EAAA,0BAAc,IAAd,CAAmB;AACjB,EAAA,mBAAK,GADY;AAEjB,EAAA,uBAAS,KAAK;AAFG,EAAA,aAAnB;AAID,EAAA;AACF,EAAA;AACF,EAAA,OAtBD,MAsBO;AACL,EAAA,eAAO,MAAM,MAAN,CAAP;;AAEA,EAAA,aAAK,IAAI,CAAT,EAAY,IAAI,KAAK,MAArB,EAA6B,GAA7B,EAAkC;AAChC,EAAA,gBAAM,KAAK,CAAL,CAAN;AACA,EAAA,cAAI,KAAK,QAAL,KAAkB,OAAO,SAA7B,EAAwC;AACtC,EAAA,mBAAO,GAAP,EAAY,OAAZ,GAAsB,OAAO,SAA7B;AACD,EAAA,WAFD,MAEO;AACL,EAAA,mBAAO,GAAP,EAAY,OAAZ,GAAsB,OAAO,GAAP,EAAY,OAAZ,IAAuB,OAAO,GAAP,EAAY,MAAZ,IAAsB,KAAK,QAAlD,CAAtB;AACD,EAAA;AACD,EAAA,wBAAc,IAAd,CAAmB,OAAO,GAAP,CAAnB;AACD,EAAA;AACF,EAAA;AACD,EAAA,UAAI,KAAK,gBAAL,KAA0B,YAA9B,EAA4C;AAC1C,EAAA,eAAO,KAAK,aAAL,EAAP;AACD,EAAA,OAFD,MAEO;AACL,EAAA,eAAO,EAAP;AACD,EAAA;AACF,EAAA,KA7f2B;AA8f5B,EAAA,iBAAa,SAAS,WAAT,CAAqB,QAArB,EAA+B;AAC1C,EAAA,UAAI,aAAa,IAAjB,EAAuB;AACrB,EAAA,eAAO,KAAK,UAAZ;AACD,EAAA,OAFD,MAEO,IAAI,CAAC,MAAM,UAAN,CAAiB,QAAjB,CAAL,EAAiC;AACtC,EAAA,cAAM,IAAI,KAAJ,CAAU,8BAAV,CAAN;AACD,EAAA,OAFM,MAEA;AACL,EAAA,aAAK,UAAL,GAAkB,QAAlB;AACD,EAAA;AACF,EAAA,KAtgB2B;AAugB5B,EAAA,gBAAY,SAAS,UAAT,CAAoB,YAApB,EAAkC,MAAlC,EAA0C;AACpD,EAAA,qBAAe,gBAAgB,EAA/B;AACA,EAAA,eAAS,CAAC,CAAC,MAAX;AACA,EAAA,UAAI,CAAC,MAAM,QAAN,CAAe,YAAf,CAAL,EAAmC;AACjC,EAAA,cAAM,IAAI,KAAJ,CAAU,iCAAV,CAAN;AACD,EAAA;;AAED,EAAA,UAAI,mBAAmB,YAAvB,EAAqC;AACnC,EAAA,aAAK,eAAL,GAAuB,aAAa,aAApC;AACD,EAAA,OAFD,MAEO,IAAI,MAAJ,EAAY;AACjB,EAAA,aAAK,eAAL,GAAuB,SAAS,aAAhC;AACD,EAAA;;AAED,EAAA,WAAK,QAAL,GAAgB,KAAK,eAAL,GAAuB,KAAK,IAA5C;;AAEA,EAAA,UAAI,cAAc,YAAlB,EAAgC;AAC9B,EAAA,aAAK,UAAL,GAAkB,CAAC,CAAC,aAAa,QAAjC;AACD,EAAA,OAFD,MAEO,IAAI,MAAJ,EAAY;AACjB,EAAA,aAAK,UAAL,GAAkB,SAAS,QAA3B;AACD,EAAA;;AAED,EAAA,UAAI,oBAAoB,YAAxB,EAAsC;AACpC,EAAA,aAAK,iBAAL,CAAuB,aAAa,cAApC,EAAoD,KAApD;AACD,EAAA,OAFD,MAEO,IAAI,MAAJ,EAAY;AACjB,EAAA,aAAK,iBAAL,CAAuB,SAAS,cAAhC,EAAgD,KAAhD;AACD,EAAA;;AAED,EAAA,UAAI,iBAAiB,YAArB,EAAmC;AACjC,EAAA,aAAK,cAAL,CAAoB,aAAa,WAAjC;AACD,EAAA,OAFD,MAEO,IAAI,MAAJ,EAAY;AACjB,EAAA,aAAK,cAAL,CAAoB,SAAS,WAA7B;AACD,EAAA;;AAED,EAAA,UAAI,YAAY,YAAhB,EAA8B;AAC5B,EAAA,aAAK,SAAL,CAAe,aAAa,MAA5B;AACD,EAAA,OAFD,MAEO,IAAI,MAAJ,EAAY;AACjB,EAAA,aAAK,SAAL,CAAe,SAAS,MAAxB;AACD,EAAA;;AAED,EAAA,UAAI,oBAAoB,YAAxB,EAAsC;AACpC,EAAA,aAAK,gBAAL,GAAwB,CAAC,CAAC,aAAa,cAAvC;AACD,EAAA,OAFD,MAEO,IAAI,MAAJ,EAAY;AACjB,EAAA,aAAK,gBAAL,GAAwB,SAAS,cAAjC;AACD,EAAA;;AAED,EAAA,UAAI,mBAAmB,YAAvB,EAAqC;AACnC,EAAA,aAAK,eAAL,GAAuB,CAAC,CAAC,aAAa,aAAtC;AACD,EAAA,OAFD,MAEO,IAAI,MAAJ,EAAY;AACjB,EAAA,aAAK,eAAL,GAAuB,SAAS,aAAhC;AACD,EAAA;;AAED,EAAA,UAAI,cAAc,YAAlB,EAAgC;AAC9B,EAAA,aAAK,WAAL,CAAiB,aAAa,QAA9B;AACD,EAAA,OAFD,MAEO,IAAI,MAAJ,EAAY;AACjB,EAAA,aAAK,WAAL,CAAiB,SAAS,QAA1B;AACD,EAAA;;AAED,EAAA,UAAI,wBAAwB,YAA5B,EAA0C;AACxC,EAAA,aAAK,qBAAL,CAA2B,aAAa,kBAAxC;AACD,EAAA,OAFD,MAEO,IAAI,MAAJ,EAAY;AACjB,EAAA,aAAK,qBAAL,CAA2B,SAAS,kBAApC;AACD,EAAA;;AAED,EAAA,UAAI,cAAc,YAAlB,EAAgC;AAC9B,EAAA,aAAK,WAAL,CAAiB,aAAa,QAA9B;AACD,EAAA,OAFD,MAEO,IAAI,MAAJ,EAAY;AACjB,EAAA,aAAK,WAAL,CAAiB,SAAS,QAA1B;AACD,EAAA;;AAED,EAAA,UAAI,iBAAiB,YAAjB,IAAiC,iBAAiB,YAAtD,EAAoE;AAClE,EAAA,aAAK,cAAL,CAAoB,aAAa,WAAb,IAA4B,SAAS,WAAzD,EAAsE,aAAa,WAAb,IAA4B,SAAS,WAA3G;AACD,EAAA,OAFD,MAEO,IAAI,MAAJ,EAAY;AACjB,EAAA,aAAK,cAAL,CAAoB,SAAS,WAA7B,EAA0C,SAAS,WAAnD;AACD,EAAA;AACF,EAAA,KAjlB2B;AAklB5B,EAAA,oBAAgB,SAAS,cAAT,CAAwB,WAAxB,EAAqC;AACnD,EAAA,UAAI,gBAAgB,IAApB,EAA0B;AACxB,EAAA,eAAO,KAAK,aAAZ;AACD,EAAA,OAFD,MAEO,IAAI,CAAC,MAAM,QAAN,CAAe,WAAf,CAAL,EAAkC;AACvC,EAAA,cAAM,IAAI,KAAJ,CAAU,+BAAV,CAAN;AACD,EAAA,OAFM,MAEA,IAAI,cAAc,CAAlB,EAAqB;AAC1B,EAAA,cAAM,IAAI,KAAJ,CAAU,wCAAV,CAAN;AACD,EAAA,OAFM,MAEA;AACL,EAAA,aAAK,aAAL,GAAqB,WAArB;AACD,EAAA;AACD,EAAA,oBAAc,KAAK,eAAnB;AACA,EAAA,UAAI,KAAK,gBAAL,KAA0B,YAA9B,EAA4C;AAC1C,EAAA,SAAC,UAAU,IAAV,EAAgB;AACf,EAAA,eAAK,eAAL,GAAuB,YAAY,YAAY;AAC7C,EAAA,iBAAK,aAAL;AACD,EAAA,WAFsB,EAEpB,KAAK,aAFe,CAAvB;AAGD,EAAA,SAJD,EAIG,IAJH;AAKD,EAAA,OAND,MAMO;AACL,EAAA,eAAO,KAAK,eAAZ;AACD,EAAA;AACF,EAAA,KAtmB2B;AAumB5B,EAAA,oBAAgB,SAAS,cAAT,CAAwB,WAAxB,EAAqC,WAArC,EAAkD;AAChE,EAAA,UAAI,CAAC,MAAM,QAAN,CAAe,WAAf,CAAL,EAAkC;AAChC,EAAA,cAAM,IAAI,KAAJ,CAAU,+BAAV,CAAN;AACD,EAAA,OAFD,MAEO,IAAI,gBAAgB,QAAhB,IAA4B,gBAAgB,cAA5C,IAA8D,gBAAgB,gBAAlF,EAAoG;AACzG,EAAA,cAAM,IAAI,KAAJ,CAAU,mEAAV,CAAN;AACD,EAAA;;AAED,EAAA,UAAI,cAAc,SAAlB;AACA,EAAA,UAAI,WAAW,MAAf;AACA,EAAA,UAAI,iBAAiB,KAArB;AACA,EAAA,UAAI,QAAQ,EAAZ;;AAEA,EAAA,eAAS,IAAT,CAAc,WAAd,EAA2B,QAA3B,EAAqC;AACnC,EAAA,YAAI,OAAO,KAAK,IAAL,EAAX;AACA,EAAA,YAAI,SAAS,KAAK,MAAlB;AACA,EAAA,YAAI,MAAJ,EAAY;AACV,EAAA,cAAI,OAAO,KAAK,CAAhB;AACA,EAAA,cAAI,mBAAmB,MAAM,QAAN,CAAe,QAAf,CAAvB;AACA,EAAA,eAAK,IAAI,IAAI,CAAb,EAAgB,IAAI,MAApB,EAA4B,GAA5B,EAAiC;AAC/B,EAAA,mBAAO,KAAK,CAAL,CAAP;AACA,EAAA,gBAAI,WAAJ,EAAiB;AACf,EAAA,kBAAI,WAAW,cAAc,OAAd,CAAsB,KAAK,QAAL,GAAgB,QAAhB,GAA2B,IAAjD,CAAf;AACA,EAAA,kBAAI,QAAJ,EAAc;AACZ,EAAA,sBAAM,IAAN,IAAc,MAAM,QAAN,CAAe,QAAf,CAAd;AACD,EAAA;AACF,EAAA,aALD,MAKO,IAAI,gBAAJ,EAAsB;AAC3B,EAAA,oBAAM,IAAN,IAAc,SAAS,IAAT,CAAd;AACD,EAAA;AACD,EAAA,iBAAK,MAAL,CAAY,IAAZ;AACD,EAAA;AACD,EAAA,2BAAiB,IAAjB;AACD,EAAA;AACF,EAAA;;AAED,EAAA,UAAI,CAAC,KAAK,cAAV,EAA0B;AACxB,EAAA,aAAK,IAAL,CAAU,IAAV,EAAgB,WAAhB,EAA6B,QAA7B;AACD,EAAA;;AAED,EAAA,WAAK,aAAL,GAAqB,WAArB;;AAEA,EAAA,UAAI,WAAJ,EAAiB;AACf,EAAA,YAAI,CAAC,MAAM,QAAN,CAAe,WAAf,CAAL,EAAkC;AAChC,EAAA,gBAAM,IAAI,KAAJ,CAAU,gCAAV,CAAN;AACD,EAAA,SAFD,MAEO,IAAI,EAAE,aAAa,WAAf,KAA+B,OAAO,YAAY,OAAnB,KAA+B,UAAlE,EAA8E;AACnF,EAAA,gBAAM,IAAI,KAAJ,CAAU,mDAAV,CAAN;AACD,EAAA,SAFM,MAEA,IAAI,EAAE,aAAa,WAAf,KAA+B,OAAO,YAAY,OAAnB,KAA+B,UAAlE,EAA8E;AACnF,EAAA,gBAAM,IAAI,KAAJ,CAAU,4CAAV,CAAN;AACD,EAAA,SAFM,MAEA,IAAI,EAAE,gBAAgB,WAAlB,KAAkC,OAAO,YAAY,UAAnB,KAAkC,UAAxE,EAAoF;AACzF,EAAA,gBAAM,IAAI,KAAJ,CAAU,+CAAV,CAAN;AACD,EAAA;AACD,EAAA,oBAAY,SAAS,SAAT,GAAqB;AAC/B,EAAA,iBAAO,WAAP;AACD,EAAA,SAFD;AAGD,EAAA,OAbD,MAaO,IAAI,KAAK,aAAL,KAAuB,cAA3B,EAA2C;AAChD,EAAA,YAAI;AACF,EAAA,uBAAa,OAAb,CAAqB,cAArB,EAAqC,cAArC;AACA,EAAA,uBAAa,UAAb,CAAwB,cAAxB;AACA,EAAA,sBAAY,SAAS,SAAT,GAAqB;AAC/B,EAAA,mBAAO,YAAP;AACD,EAAA,WAFD;AAGD,EAAA,SAND,CAME,OAAO,CAAP,EAAU;AACV,EAAA,sBAAY,IAAZ;AACA,EAAA,eAAK,aAAL,GAAqB,QAArB;AACD,EAAA;AACF,EAAA,OAXM,MAWA,IAAI,KAAK,aAAL,KAAuB,gBAA3B,EAA6C;AAClD,EAAA,YAAI;AACF,EAAA,yBAAe,OAAf,CAAuB,cAAvB,EAAuC,cAAvC;AACA,EAAA,yBAAe,UAAf,CAA0B,cAA1B;AACA,EAAA,sBAAY,SAAS,SAAT,GAAqB;AAC/B,EAAA,mBAAO,cAAP;AACD,EAAA,WAFD;AAGD,EAAA,SAND,CAME,OAAO,CAAP,EAAU;AACV,EAAA,sBAAY,IAAZ;AACA,EAAA,eAAK,aAAL,GAAqB,QAArB;AACD,EAAA;AACF,EAAA,OAXM,MAWA;AACL,EAAA,oBAAY,IAAZ;AACA,EAAA,aAAK,aAAL,GAAqB,QAArB;AACD,EAAA;;AAED,EAAA,UAAI,KAAK,cAAT,EAAyB;AACvB,EAAA,aAAK,IAAL,CAAU,IAAV,EAAgB,SAAhB,EAA2B,MAA3B;AACD,EAAA;;AAED,EAAA,UAAI,cAAJ,EAAoB;AAClB,EAAA,YAAI,OAAO,KAAK,CAAhB;AACA,EAAA,aAAK,IAAI,GAAT,IAAgB,KAAhB,EAAuB;AACrB,EAAA,iBAAO,MAAM,GAAN,CAAP;AACA,EAAA,eAAK,GAAL,CAAS,GAAT,EAAc,KAAK,KAAnB,EAA0B;AACxB,EAAA,qBAAS,KAAK,OADU;AAExB,EAAA,sBAAU,KAAK,QAFS;AAGxB,EAAA,qBAAS,KAAK;AAHU,EAAA,WAA1B;AAKD,EAAA;AACF,EAAA;AACF,EAAA,KAtsB2B;AAusB5B,EAAA,WAAO,SAAS,KAAT,CAAe,GAAf,EAAoB,OAApB,EAA6B;AAClC,EAAA,UAAI,SAAS,IAAb;;AAEA,EAAA,UAAI,GAAJ,EAAS;AACP,EAAA,YAAI,MAAM,KAAK,GAAL,CAAS,GAAT,EAAc;AACtB,EAAA,oBAAU,SAAS,QAAT,CAAkB,CAAlB,EAAqB,CAArB,EAAwB;AAChC,EAAA,mBAAO,OAAO,GAAP,CAAW,CAAX,EAAc,CAAd,CAAP;AACD,EAAA;AAHqB,EAAA,SAAd,CAAV;AAKA,EAAA,YAAI,GAAJ,EAAS;AACP,EAAA,eAAK,GAAL,CAAS,GAAT,EAAc,GAAd,EAAmB,OAAnB;AACD,EAAA;AACF,EAAA,OATD,MASO;AACL,EAAA,YAAI,OAAO,KAAK,IAAL,EAAX;AACA,EAAA,aAAK,IAAI,IAAI,CAAb,EAAgB,IAAI,KAAK,MAAzB,EAAiC,GAAjC,EAAsC;AACpC,EAAA,eAAK,KAAL,CAAW,KAAK,CAAL,CAAX,EAAoB,OAApB;AACD,EAAA;AACF,EAAA;AACF,EAAA,KAztB2B;AA0tB5B,EAAA,YAAQ,SAAS,MAAT,GAAkB;AACxB,EAAA,UAAI,OAAO,KAAK,IAAL,EAAX;AACA,EAAA,UAAI,QAAQ,EAAZ;AACA,EAAA,WAAK,IAAI,IAAI,CAAb,EAAgB,IAAI,KAAK,MAAzB,EAAiC,GAAjC,EAAsC;AACpC,EAAA,cAAM,IAAN,CAAW,KAAK,GAAL,CAAS,KAAK,CAAL,CAAT,CAAX;AACD,EAAA;AACD,EAAA,aAAO,KAAP;AACD,EAAA;AAjuB2B,EAAA,GAA9B;;AAouBA,EAAA,QAAM,cAAN,GAAuB,IAAvB;AACA,EAAA,QAAM,UAAN,CAAiB,OAAjB,EAA0B,IAA1B;AACA,EAAA,QAAM,cAAN,GAAuB,KAAvB;;AAEA,EAAA,SAAO,KAAP;AACD,EAAA;;AAED,EAAA,SAAS,YAAT,CAAsB,OAAtB,EAA+B,OAA/B,EAAwC;AACtC,EAAA,SAAO,YAAY,OAAZ,EAAqB,OAArB,CAAP;AACD,EAAA;;AAED,EAAA,aAAa,WAAb,GAA2B,WAA3B;AACA,EAAA,aAAa,QAAb,GAAwB,QAAxB;;AAEA,EAAA,aAAa,IAAb,GAAoB,YAAY;AAC9B,EAAA,MAAI,OAAO,MAAM,MAAN,CAAX;AACA,EAAA,MAAI,OAAO;AACT,EAAA,UAAM,KAAK,MADF;AAET,EAAA,YAAQ;AAFC,EAAA,GAAX;AAIA,EAAA,OAAK,IAAI,GAAT,IAAgB,QAAhB,EAA0B;AACxB,EAAA,QAAI,SAAS,cAAT,CAAwB,GAAxB,CAAJ,EAAkC;AAChC,EAAA,WAAK,GAAL,IAAY,SAAS,GAAT,CAAZ;AACD,EAAA;AACF,EAAA;AACD,EAAA,OAAK,IAAI,IAAI,CAAb,EAAgB,IAAI,KAAK,MAAzB,EAAiC,GAAjC,EAAsC;AACpC,EAAA,QAAI,MAAM,KAAK,CAAL,CAAV;AACA,EAAA,SAAK,MAAL,CAAY,GAAZ,IAAmB,OAAO,GAAP,EAAY,IAAZ,EAAnB;AACD,EAAA;AACD,EAAA,SAAO,IAAP;AACD,EAAA,CAhBD;;AAkBA,EAAA,aAAa,GAAb,GAAmB,UAAU,OAAV,EAAmB;AACpC,EAAA,SAAO,OAAO,OAAP,CAAP;AACD,EAAA,CAFD;AAGA,EAAA,aAAa,MAAb,GAAsB,YAAY;AAChC,EAAA,SAAO,QAAQ,MAAR,CAAP;AACD,EAAA,CAFD;AAGA,EAAA,aAAa,IAAb,GAAoB,YAAY;AAC9B,EAAA,SAAO,MAAM,MAAN,CAAP;AACD,EAAA,CAFD;AAGA,EAAA,aAAa,OAAb,GAAuB,UAAU,OAAV,EAAmB;AACxC,EAAA,MAAI,OAAO,OAAP,CAAJ,EAAqB;AACnB,EAAA,WAAO,OAAP,EAAgB,OAAhB;AACA,EAAA,WAAO,OAAO,OAAP,CAAP;AACD,EAAA;AACF,EAAA,CALD;AAMA,EAAA,aAAa,UAAb,GAA0B,YAAY;AACpC,EAAA,OAAK,IAAI,OAAT,IAAoB,MAApB,EAA4B;AAC1B,EAAA,WAAO,OAAP,EAAgB,OAAhB;AACD,EAAA;AACD,EAAA,WAAS,EAAT;AACD,EAAA,CALD;AAMA,EAAA,aAAa,QAAb,GAAwB,YAAY;AAClC,EAAA,OAAK,IAAI,OAAT,IAAoB,MAApB,EAA4B;AAC1B,EAAA,WAAO,OAAP,EAAgB,SAAhB;AACD,EAAA;AACF,EAAA,CAJD;AAKA,EAAA,aAAa,oBAAb,GAAoC,YAAY;AAC9C,EAAA,MAAI,UAAU,EAAd;AACA,EAAA,OAAK,IAAI,OAAT,IAAoB,MAApB,EAA4B;AAC1B,EAAA,YAAQ,OAAR,IAAmB,OAAO,OAAP,EAAgB,aAAhB,EAAnB;AACD,EAAA;AACD,EAAA,SAAO,OAAP;AACD,EAAA,CAND;AAOA,EAAA,aAAa,SAAb,GAAyB,YAAY;AACnC,EAAA,OAAK,IAAI,OAAT,IAAoB,MAApB,EAA4B;AAC1B,EAAA,WAAO,OAAP,EAAgB,UAAhB,GAA6B,KAA7B;AACD,EAAA;AACF,EAAA,CAJD;AAKA,EAAA,aAAa,UAAb,GAA0B,YAAY;AACpC,EAAA,OAAK,IAAI,OAAT,IAAoB,MAApB,EAA4B;AAC1B,EAAA,WAAO,OAAP,EAAgB,UAAhB,GAA6B,IAA7B;AACD,EAAA;AACF,EAAA,CAJD;AAKA,EAAA,aAAa,QAAb,GAAwB,YAAY;AAClC,EAAA,OAAK,IAAI,OAAT,IAAoB,MAApB,EAA4B;AAC1B,EAAA,WAAO,OAAP,EAAgB,KAAhB;AACD,EAAA;AACF,EAAA,CAJD;;AAMA,EAAA,aAAa,KAAb,GAAqB,KAArB;AACA,EAAA,aAAa,UAAb,GAA0B,UAA1B;;EChjCA,aAAa,KAAb,CAAmB,MAAnB,GAA4B,QAAQ,MAApC;AACA,EAAA,aAAa,KAAb,CAAmB,QAAnB,GAA8B,QAAQ,QAAtC;AACA,EAAA,aAAa,KAAb,CAAmB,QAAnB,GAA8B,QAAQ,QAAtC;;AAEA,EAAA,SAAS,kBAAT,GAA+B;AAC7B,EAAA,OAAK,IAAL,GAAY,YAAY;AAAE,EAAA,WAAO,aAAa,UAApB;AAAgC,EAAA,GAA1D;AACD,EAAA;;AAED,EAAA,SAAS,oBAAT,GAAiC;AAC/B,EAAA,OAAK,QAAL,GAAgB,aAAa,QAA7B;AACA,EAAA,OAAK,QAAL,CAAc,aAAd,GAA8B,uBAA9B;;AAEA,EAAA,OAAK,IAAL,GAAY,CAAC,IAAD,EAAO,UAAU,EAAV,EAAc;AAC/B,EAAA,iBAAa,KAAb,CAAmB,OAAnB,GAA6B,EAA7B;AACA,EAAA,WAAO,YAAP;AACD,EAAA,GAHW,CAAZ;AAID,EAAA;;AAED,EAAA,QAAQ,MAAR,CAAe,eAAf,EAAgC,EAAhC,EACG,QADH,CACY,YADZ,EAC0B,kBAD1B,EAEG,QAFH,CAEY,cAFZ,EAE4B,oBAF5B;;AAIA,cAAe,eAAf;;;;"} \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/dist/angular-cache.min.js b/ecomp-portal-FE/client/bower_components/angular-cache/dist/angular-cache.min.js new file mode 100644 index 00000000..ae78ddcb --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/dist/angular-cache.min.js @@ -0,0 +1,2 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t("undefined"==typeof angular?require("angular"):angular):"function"==typeof define&&define.amd?define("angular-cache",["angular"],t):e.angularCacheModuleName=t(e.angular)}(this,function(e){"use strict";function t(e,t){if(e||(e=function(e){return e}),t||(t=function(e,t){return e===t}),"function"!=typeof e)throw new Error('BinaryHeap([weightFunc][, compareFunc]): "weightFunc" must be a function!');if("function"!=typeof t)throw new Error('BinaryHeap([weightFunc][, compareFunc]): "compareFunc" must be a function!');this.weightFunc=e,this.compareFunc=t,this.heap=[]}function r(e){var t=[],r=void 0;if(!d.isObject(e))return t;for(r in e)e.hasOwnProperty(r)&&t.push(r);return t}function i(e){return e&&"function"==typeof e.then}function s(e){return d.isNumber(e)?e.toString():e}function n(e){var t={},r=void 0;if(!d.isObject(e))return t;for(r in e)e.hasOwnProperty(r)&&(t[r]=r);return t}function o(e,o){if(e in g)throw new Error(e+" already exists!");if(!d.isString(e))throw new Error("cacheId must be a string!");var a={},c={},l=null,h=new t(function(e){return e.expires},d.equals),f=new t(function(e){return e.accessed},d.equals),p=g[e]={$$id:e,destroy:function(){clearInterval(this.$$cacheFlushIntervalId),clearInterval(this.$$recycleFreqId),this.removeAll(),l&&(l().removeItem(this.$$prefix+".keys"),l().removeItem(this.$$prefix)),l=null,a=null,f=null,h=null,this.$$prefix=null,delete g[this.$$id]},disable:function(){this.$$disabled=!0},enable:function(){delete this.$$disabled},get:function(e,t){var r=this;if(Array.isArray(e)){var i=function(){var i=e,s=[];return i.forEach(function(e){var i=r.get(e,t);null!==i&&void 0!==i&&s.push(i)}),{v:s}}();if("object"===("undefined"==typeof i?"undefined":u["typeof"](i)))return i.v}else if(e=s(e),this.$$disabled)return;if(t=t||{},!d.isString(e))throw new Error("key must be a string!");if(t&&!d.isObject(t))throw new Error("options must be an object!");if(t.onExpire&&!d.isFunction(t.onExpire))throw new Error("options.onExpire must be a function!");var n=void 0;if(l){if(c[e])return c[e];var o=l().getItem(this.$$prefix+".data."+e);if(!o)return;n=d.fromJson(o)}else if(d.isObject(a)){if(!(e in a))return;n=a[e]}var h=n.value,p=(new Date).getTime();return l?(f.remove({key:e,accessed:n.accessed}),n.accessed=p,f.push({key:e,accessed:p})):(f.remove(n),n.accessed=p,f.push(n)),"passive"===this.$$deleteOnExpire&&"expires"in n&&n.expires(t.maxAge||this.$$maxAge)}):void 0}return d.isObject(a)&&e in a?(t=a[e],{created:t.created,accessed:t.accessed,expires:t.expires,isExpired:(new Date).getTime()-t.created>(t.maxAge||this.$$maxAge)}):void 0}return{id:this.$$id,capacity:this.$$capacity,maxAge:this.$$maxAge,deleteOnExpire:this.$$deleteOnExpire,onExpire:this.$$onExpire,cacheFlushInterval:this.$$cacheFlushInterval,recycleFreq:this.$$recycleFreq,storageMode:this.$$storageMode,storageImpl:l?l():void 0,disabled:!!this.$$disabled,size:f&&f.size()||0}},keys:function(){if(l){var e=l().getItem(this.$$prefix+".keys");return e?d.fromJson(e):[]}return r(a)},keySet:function(){if(l){var e=l().getItem(this.$$prefix+".keys"),t={};if(e)for(var r=d.fromJson(e),i=0;ithis.$$capacity&&this.remove(f.peek().key),t}},remove:function(e){if(e+="",delete c[e],l){var t=l().getItem(this.$$prefix+".data."+e);if(t){var r=d.fromJson(t);f.remove({key:e,accessed:r.accessed}),h.remove({key:e,expires:r.expires}),l().removeItem(this.$$prefix+".data."+e);var i=l().getItem(this.$$prefix+".keys"),s=i?d.fromJson(i):[],n=s.indexOf(e);return n>=0&&s.splice(n,1),l().setItem(this.$$prefix+".keys",JSON.stringify(s)),r.value}}else if(d.isObject(a)){var o=a[e]?a[e].value:void 0;return f.remove(a[e]),h.remove(a[e]),a[e]=null,delete a[e],o}},removeAll:function(){if(l){f.removeAll(),h.removeAll();var e=l().getItem(this.$$prefix+".keys");if(e)for(var t=d.fromJson(e),r=0;re)throw new Error("cacheFlushInterval must be greater than zero!");e!==t.$$cacheFlushInterval&&(t.$$cacheFlushInterval=e,clearInterval(t.$$cacheFlushIntervalId),t.$$cacheFlushIntervalId=setInterval(function(){t.removeAll()},t.$$cacheFlushInterval))}},setCapacity:function(e){if(null===e)delete this.$$capacity;else{if(!d.isNumber(e))throw new Error("capacity must be a number!");if(0>e)throw new Error("capacity must be greater than zero!");this.$$capacity=e}for(var t={};f.size()>this.$$capacity;)t[f.peek().key]=this.remove(f.peek().key);return t},setDeleteOnExpire:function(e,t){if(null===e)delete this.$$deleteOnExpire;else{if(!d.isString(e))throw new Error("deleteOnExpire must be a string!");if("none"!==e&&"passive"!==e&&"aggressive"!==e)throw new Error('deleteOnExpire must be "none", "passive" or "aggressive"!');this.$$deleteOnExpire=e}t!==!1&&this.setRecycleFreq(this.$$recycleFreq)},setMaxAge:function(e){if(null===e)this.$$maxAge=Number.MAX_VALUE;else{if(!d.isNumber(e))throw new Error("maxAge must be a number!");if(0>e)throw new Error("maxAge must be greater than zero!");this.$$maxAge=e}var t=void 0,i=void 0,s=void 0;if(h.removeAll(),l){var n=l().getItem(this.$$prefix+".keys");for(i=n?d.fromJson(n):[],t=0;te)throw new Error("recycleFreq must be greater than zero!");this.$$recycleFreq=e}clearInterval(this.$$recycleFreqId),"aggressive"===this.$$deleteOnExpire?!function(e){e.$$recycleFreqId=setInterval(function(){e.removeExpired()},e.$$recycleFreq)}(this):delete this.$$recycleFreqId},setStorageMode:function(e,t){function r(e,t){var r=this.keys(),i=r.length;if(i){for(var s=void 0,a=d.isObject(t),c=0;i>c;c++){if(s=r[c],e){var l=e().getItem(this.$$prefix+".data."+s);l&&(o[s]=d.fromJson(l))}else a&&(o[s]=t[s]);this.remove(s)}n=!0}}if(!d.isString(e))throw new Error("storageMode must be a string!");if("memory"!==e&&"localStorage"!==e&&"sessionStorage"!==e)throw new Error('storageMode must be "memory", "localStorage" or "sessionStorage"!');var i=l,s=a,n=!1,o={};if(this.$$initializing||r.call(this,i,s),this.$$storageMode=e,t){if(!d.isObject(t))throw new Error("storageImpl must be an object!");if(!("setItem"in t&&"function"==typeof t.setItem))throw new Error('storageImpl must implement "setItem(key, value)"!');if(!("getItem"in t&&"function"==typeof t.getItem))throw new Error('storageImpl must implement "getItem(key)"!');if(!("removeItem"in t)||"function"!=typeof t.removeItem)throw new Error('storageImpl must implement "removeItem(key)"!');l=function(){return t}}else if("localStorage"===this.$$storageMode)try{localStorage.setItem("cachefactory","cachefactory"),localStorage.removeItem("cachefactory"),l=function(){return localStorage}}catch(c){l=null,this.$$storageMode="memory"}else if("sessionStorage"===this.$$storageMode)try{sessionStorage.setItem("cachefactory","cachefactory"),sessionStorage.removeItem("cachefactory"),l=function(){return sessionStorage}}catch(c){l=null,this.$$storageMode="memory"}else l=null,this.$$storageMode="memory";if(this.$$initializing&&r.call(this,l,a),n){var u=void 0;for(var h in o)u=o[h],this.put(h,u.value,{created:u.created,accessed:u.accessed,expires:u.expires})}},touch:function(e,t){var r=this;if(e){var i=this.get(e,{onExpire:function(e,t){return r.put(e,t)}});i&&this.put(e,i,t)}else for(var s=this.keys(),n=0;n0;){var n=Math.floor((r+1)/2)-1,o=e[n];if(s>=t(o))break;e[n]=i,e[r]=o,r=n}},f=function(e,t,r){for(var i=e.length,s=e[r],n=t(s);;){var o=2*(r+1),a=o-1,c=null;if(i>a){var l=e[a],u=t(l);n>u&&(c=a)}if(i>o){var h=e[o],f=t(h);f<(null===c?n:t(e[a]))&&(c=o)}if(null===c)break;e[r]=e[c],e[c]=s,r=c}},p=t.prototype;p.push=function(e){this.heap.push(e),h(this.heap,this.weightFunc,this.heap.length-1)},p.peek=function(){return this.heap[0]},p.pop=function(){var e=this.heap[0],t=this.heap.pop();return this.heap.length>0&&(this.heap[0]=t,f(this.heap,this.weightFunc,0)),e},p.remove=function(e){for(var t=this.heap.length,r=0;t>r;r++)if(this.compareFunc(this.heap[r],e)){var i=this.heap[r],s=this.heap.pop();return r!==t-1&&(this.heap[r]=s,h(this.heap,this.weightFunc,r),f(this.heap,this.weightFunc,r)),i}return null},p.removeAll=function(){this.heap=[]},p.size=function(){return this.heap.length};var m=null;try{m=window.Promise}catch($){}var d={isNumber:function(e){return"number"==typeof e},isString:function(e){return"string"==typeof e},isObject:function(e){return null!==e&&"object"===("undefined"==typeof e?"undefined":u["typeof"](e))},isFunction:function(e){return"function"==typeof e},fromJson:function(e){return JSON.parse(e)},equals:function(e,t){return e===t},Promise:m},v={capacity:Number.MAX_VALUE,maxAge:Number.MAX_VALUE,deleteOnExpire:"none",onExpire:null,cacheFlushInterval:null,recycleFreq:1e3,storageMode:"memory",storageImpl:null,disabled:!1,storagePrefix:"cachefactory.caches.",storeOnResolve:!1,storeOnReject:!1},g={};a.createCache=o,a.defaults=v,a.info=function(){var e=r(g),t={size:e.length,caches:{}};for(var i in v)v.hasOwnProperty(i)&&(t[i]=v[i]);for(var s=0;s\n' + + ' * @copyright (c) 2013-2016 angular-cache project authors\n' + + ' * @license MIT \n' + + ' * @overview angular-cache is a very useful replacement for Angular\'s $cacheFactory.\n' + + ' */', + plugins: [ + babel({ + babelrc: false, + presets: [ + 'es2015-rollup' + ] + }) + ] +} diff --git a/ecomp-portal-FE/client/bower_components/angular-cache/scripts/postbuild.js b/ecomp-portal-FE/client/bower_components/angular-cache/scripts/postbuild.js new file mode 100644 index 00000000..20c3d9a5 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cache/scripts/postbuild.js @@ -0,0 +1,21 @@ +var fs = require('fs') +var path = __dirname + '/../dist/angular-cache.js' +var file = fs.readFileSync(path, { encoding: 'utf8' }) +file = file.replace( + 'module.exports = factory(require(\'angular\'))', + 'module.exports = factory(typeof angular === \'undefined\' ? require(\'angular\') : angular)' +) +var index = file.indexOf('babelHelpers;') +var str = 'var babelHelpers = {};\n' + +' babelHelpers.typeof = typeof Symbol === "function" && babelHelpers.typeof(Symbol.iterator) === "symbol" ? function (obj) {\n' + +' return typeof obj === "undefined" ? "undefined" : babelHelpers.typeof(obj);\n' + +' } : function (obj) {\n' + +' return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj === "undefined" ? "undefined" : babelHelpers.typeof(obj);\n' + +' };' + +var index2 = file.indexOf(str) + +var file2 = file.substring(index2 + str.length) +file = file.substring(0, index) + file2 + +fs.writeFileSync(path, file) diff --git a/ecomp-portal-FE/client/bower_components/angular-cookies/.bower.json b/ecomp-portal-FE/client/bower_components/angular-cookies/.bower.json new file mode 100644 index 00000000..c848f4c5 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cookies/.bower.json @@ -0,0 +1,20 @@ +{ + "name": "angular-cookies", + "version": "1.5.0", + "license": "MIT", + "main": "./angular-cookies.js", + "ignore": [], + "dependencies": { + "angular": "1.5.0" + }, + "homepage": "https://github.com/angular/bower-angular-cookies", + "_release": "1.5.0", + "_resolution": { + "type": "version", + "tag": "v1.5.0", + "commit": "3852e28d49ee37a9b28bb788f0e3b14aa0c9ea14" + }, + "_source": "https://github.com/angular/bower-angular-cookies.git", + "_target": "1.5.0", + "_originalSource": "angular-cookies" +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-cookies/README.md b/ecomp-portal-FE/client/bower_components/angular-cookies/README.md new file mode 100644 index 00000000..7b190d34 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cookies/README.md @@ -0,0 +1,68 @@ +# packaged angular-cookies + +This repo is for distribution on `npm` and `bower`. The source for this module is in the +[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngCookies). +Please file issues and pull requests against that repo. + +## Install + +You can install this package either with `npm` or with `bower`. + +### npm + +```shell +npm install angular-cookies +``` + +Then add `ngCookies` as a dependency for your app: + +```javascript +angular.module('myApp', [require('angular-cookies')]); +``` + +### bower + +```shell +bower install angular-cookies +``` + +Add a ` +``` + +Then add `ngCookies` as a dependency for your app: + +```javascript +angular.module('myApp', ['ngCookies']); +``` + +## Documentation + +Documentation is available on the +[AngularJS docs site](http://docs.angularjs.org/api/ngCookies). + +## License + +The MIT License + +Copyright (c) 2010-2015 Google, Inc. http://angularjs.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ecomp-portal-FE/client/bower_components/angular-cookies/angular-cookies.js b/ecomp-portal-FE/client/bower_components/angular-cookies/angular-cookies.js new file mode 100644 index 00000000..2157ef03 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cookies/angular-cookies.js @@ -0,0 +1,322 @@ +/** + * @license AngularJS v1.5.0 + * (c) 2010-2016 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, angular, undefined) {'use strict'; + +/** + * @ngdoc module + * @name ngCookies + * @description + * + * # ngCookies + * + * The `ngCookies` module provides a convenient wrapper for reading and writing browser cookies. + * + * + *
      + * + * See {@link ngCookies.$cookies `$cookies`} for usage. + */ + + +angular.module('ngCookies', ['ng']). + /** + * @ngdoc provider + * @name $cookiesProvider + * @description + * Use `$cookiesProvider` to change the default behavior of the {@link ngCookies.$cookies $cookies} service. + * */ + provider('$cookies', [function $CookiesProvider() { + /** + * @ngdoc property + * @name $cookiesProvider#defaults + * @description + * + * Object containing default options to pass when setting cookies. + * + * The object may have following properties: + * + * - **path** - `{string}` - The cookie will be available only for this path and its + * sub-paths. By default, this is the URL that appears in your `` tag. + * - **domain** - `{string}` - The cookie will be available only for this domain and + * its sub-domains. For security reasons the user agent will not accept the cookie + * if the current domain is not a sub-domain of this domain or equal to it. + * - **expires** - `{string|Date}` - String of the form "Wdy, DD Mon YYYY HH:MM:SS GMT" + * or a Date object indicating the exact date/time this cookie will expire. + * - **secure** - `{boolean}` - If `true`, then the cookie will only be available through a + * secured connection. + * + * Note: By default, the address that appears in your `` tag will be used as the path. + * This is important so that cookies will be visible for all routes when html5mode is enabled. + * + **/ + var defaults = this.defaults = {}; + + function calcOptions(options) { + return options ? angular.extend({}, defaults, options) : defaults; + } + + /** + * @ngdoc service + * @name $cookies + * + * @description + * Provides read/write access to browser's cookies. + * + *
      + * Up until Angular 1.3, `$cookies` exposed properties that represented the + * current browser cookie values. In version 1.4, this behavior has changed, and + * `$cookies` now provides a standard api of getters, setters etc. + *
      + * + * Requires the {@link ngCookies `ngCookies`} module to be installed. + * + * @example + * + * ```js + * angular.module('cookiesExample', ['ngCookies']) + * .controller('ExampleController', ['$cookies', function($cookies) { + * // Retrieving a cookie + * var favoriteCookie = $cookies.get('myFavorite'); + * // Setting a cookie + * $cookies.put('myFavorite', 'oatmeal'); + * }]); + * ``` + */ + this.$get = ['$$cookieReader', '$$cookieWriter', function($$cookieReader, $$cookieWriter) { + return { + /** + * @ngdoc method + * @name $cookies#get + * + * @description + * Returns the value of given cookie key + * + * @param {string} key Id to use for lookup. + * @returns {string} Raw cookie value. + */ + get: function(key) { + return $$cookieReader()[key]; + }, + + /** + * @ngdoc method + * @name $cookies#getObject + * + * @description + * Returns the deserialized value of given cookie key + * + * @param {string} key Id to use for lookup. + * @returns {Object} Deserialized cookie value. + */ + getObject: function(key) { + var value = this.get(key); + return value ? angular.fromJson(value) : value; + }, + + /** + * @ngdoc method + * @name $cookies#getAll + * + * @description + * Returns a key value object with all the cookies + * + * @returns {Object} All cookies + */ + getAll: function() { + return $$cookieReader(); + }, + + /** + * @ngdoc method + * @name $cookies#put + * + * @description + * Sets a value for given cookie key + * + * @param {string} key Id for the `value`. + * @param {string} value Raw value to be stored. + * @param {Object=} options Options object. + * See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults} + */ + put: function(key, value, options) { + $$cookieWriter(key, value, calcOptions(options)); + }, + + /** + * @ngdoc method + * @name $cookies#putObject + * + * @description + * Serializes and sets a value for given cookie key + * + * @param {string} key Id for the `value`. + * @param {Object} value Value to be stored. + * @param {Object=} options Options object. + * See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults} + */ + putObject: function(key, value, options) { + this.put(key, angular.toJson(value), options); + }, + + /** + * @ngdoc method + * @name $cookies#remove + * + * @description + * Remove given cookie + * + * @param {string} key Id of the key-value pair to delete. + * @param {Object=} options Options object. + * See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults} + */ + remove: function(key, options) { + $$cookieWriter(key, undefined, calcOptions(options)); + } + }; + }]; + }]); + +angular.module('ngCookies'). +/** + * @ngdoc service + * @name $cookieStore + * @deprecated + * @requires $cookies + * + * @description + * Provides a key-value (string-object) storage, that is backed by session cookies. + * Objects put or retrieved from this storage are automatically serialized or + * deserialized by angular's toJson/fromJson. + * + * Requires the {@link ngCookies `ngCookies`} module to be installed. + * + *
      + * **Note:** The $cookieStore service is **deprecated**. + * Please use the {@link ngCookies.$cookies `$cookies`} service instead. + *
      + * + * @example + * + * ```js + * angular.module('cookieStoreExample', ['ngCookies']) + * .controller('ExampleController', ['$cookieStore', function($cookieStore) { + * // Put cookie + * $cookieStore.put('myFavorite','oatmeal'); + * // Get cookie + * var favoriteCookie = $cookieStore.get('myFavorite'); + * // Removing a cookie + * $cookieStore.remove('myFavorite'); + * }]); + * ``` + */ + factory('$cookieStore', ['$cookies', function($cookies) { + + return { + /** + * @ngdoc method + * @name $cookieStore#get + * + * @description + * Returns the value of given cookie key + * + * @param {string} key Id to use for lookup. + * @returns {Object} Deserialized cookie value, undefined if the cookie does not exist. + */ + get: function(key) { + return $cookies.getObject(key); + }, + + /** + * @ngdoc method + * @name $cookieStore#put + * + * @description + * Sets a value for given cookie key + * + * @param {string} key Id for the `value`. + * @param {Object} value Value to be stored. + */ + put: function(key, value) { + $cookies.putObject(key, value); + }, + + /** + * @ngdoc method + * @name $cookieStore#remove + * + * @description + * Remove given cookie + * + * @param {string} key Id of the key-value pair to delete. + */ + remove: function(key) { + $cookies.remove(key); + } + }; + + }]); + +/** + * @name $$cookieWriter + * @requires $document + * + * @description + * This is a private service for writing cookies + * + * @param {string} name Cookie name + * @param {string=} value Cookie value (if undefined, cookie will be deleted) + * @param {Object=} options Object with options that need to be stored for the cookie. + */ +function $$CookieWriter($document, $log, $browser) { + var cookiePath = $browser.baseHref(); + var rawDocument = $document[0]; + + function buildCookieString(name, value, options) { + var path, expires; + options = options || {}; + expires = options.expires; + path = angular.isDefined(options.path) ? options.path : cookiePath; + if (angular.isUndefined(value)) { + expires = 'Thu, 01 Jan 1970 00:00:00 GMT'; + value = ''; + } + if (angular.isString(expires)) { + expires = new Date(expires); + } + + var str = encodeURIComponent(name) + '=' + encodeURIComponent(value); + str += path ? ';path=' + path : ''; + str += options.domain ? ';domain=' + options.domain : ''; + str += expires ? ';expires=' + expires.toUTCString() : ''; + str += options.secure ? ';secure' : ''; + + // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum: + // - 300 cookies + // - 20 cookies per unique domain + // - 4096 bytes per cookie + var cookieLength = str.length + 1; + if (cookieLength > 4096) { + $log.warn("Cookie '" + name + + "' possibly not set or overflowed because it was too large (" + + cookieLength + " > 4096 bytes)!"); + } + + return str; + } + + return function(name, value, options) { + rawDocument.cookie = buildCookieString(name, value, options); + }; +} + +$$CookieWriter.$inject = ['$document', '$log', '$browser']; + +angular.module('ngCookies').provider('$$cookieWriter', function $$CookieWriterProvider() { + this.$get = $$CookieWriter; +}); + + +})(window, window.angular); diff --git a/ecomp-portal-FE/client/bower_components/angular-cookies/angular-cookies.min.js b/ecomp-portal-FE/client/bower_components/angular-cookies/angular-cookies.min.js new file mode 100644 index 00000000..d0f126a9 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cookies/angular-cookies.min.js @@ -0,0 +1,9 @@ +/* + AngularJS v1.5.0 + (c) 2010-2016 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(p,c,n){'use strict';function l(b,a,g){var d=g.baseHref(),k=b[0];return function(b,e,f){var g,h;f=f||{};h=f.expires;g=c.isDefined(f.path)?f.path:d;c.isUndefined(e)&&(h="Thu, 01 Jan 1970 00:00:00 GMT",e="");c.isString(h)&&(h=new Date(h));e=encodeURIComponent(b)+"="+encodeURIComponent(e);e=e+(g?";path="+g:"")+(f.domain?";domain="+f.domain:"");e+=h?";expires="+h.toUTCString():"";e+=f.secure?";secure":"";f=e.length+1;4096 4096 bytes)!");k.cookie=e}}c.module("ngCookies",["ng"]).provider("$cookies",[function(){var b=this.defaults={};this.$get=["$$cookieReader","$$cookieWriter",function(a,g){return{get:function(d){return a()[d]},getObject:function(d){return(d=this.get(d))?c.fromJson(d):d},getAll:function(){return a()},put:function(d,a,m){g(d,a,m?c.extend({},b,m):b)},putObject:function(d,b,a){this.put(d,c.toJson(b),a)},remove:function(a,k){g(a,n,k?c.extend({},b,k):b)}}}]}]);c.module("ngCookies").factory("$cookieStore", +["$cookies",function(b){return{get:function(a){return b.getObject(a)},put:function(a,c){b.putObject(a,c)},remove:function(a){b.remove(a)}}}]);l.$inject=["$document","$log","$browser"];c.module("ngCookies").provider("$$cookieWriter",function(){this.$get=l})})(window,window.angular); +//# sourceMappingURL=angular-cookies.min.js.map diff --git a/ecomp-portal-FE/client/bower_components/angular-cookies/angular-cookies.min.js.map b/ecomp-portal-FE/client/bower_components/angular-cookies/angular-cookies.min.js.map new file mode 100644 index 00000000..555b5103 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cookies/angular-cookies.min.js.map @@ -0,0 +1,8 @@ +{ +"version":3, +"file":"angular-cookies.min.js", +"lineCount":8, +"mappings":"A;;;;;aAKC,SAAQ,CAACA,CAAD,CAASC,CAAT,CAAkBC,CAAlB,CAA6B,CA2QtCC,QAASA,EAAc,CAACC,CAAD,CAAYC,CAAZ,CAAkBC,CAAlB,CAA4B,CACjD,IAAIC,EAAaD,CAAAE,SAAA,EAAjB,CACIC,EAAcL,CAAA,CAAU,CAAV,CAmClB,OAAO,SAAQ,CAACM,CAAD,CAAOC,CAAP,CAAcC,CAAd,CAAuB,CAjCW,IAC3CC,CAD2C,CACrCC,CACVF,EAAA,CAgCoDA,CAhCpD,EAAqB,EACrBE,EAAA,CAAUF,CAAAE,QACVD,EAAA,CAAOZ,CAAAc,UAAA,CAAkBH,CAAAC,KAAlB,CAAA,CAAkCD,CAAAC,KAAlC,CAAiDN,CACpDN,EAAAe,YAAA,CAAoBL,CAApB,CAAJ,GACEG,CACA,CADU,+BACV,CAAAH,CAAA,CAAQ,EAFV,CAIIV,EAAAgB,SAAA,CAAiBH,CAAjB,CAAJ,GACEA,CADF,CACY,IAAII,IAAJ,CAASJ,CAAT,CADZ,CAIIK,EAAAA,CAAMC,kBAAA,CAqB6BV,CArB7B,CAANS,CAAiC,GAAjCA,CAAuCC,kBAAA,CAAmBT,CAAnB,CAE3CQ,EAAA,CADAA,CACA,EADON,CAAA,CAAO,QAAP,CAAkBA,CAAlB,CAAyB,EAChC,GAAOD,CAAAS,OAAA,CAAiB,UAAjB,CAA8BT,CAAAS,OAA9B,CAA+C,EAAtD,CACAF,EAAA,EAAOL,CAAA,CAAU,WAAV,CAAwBA,CAAAQ,YAAA,EAAxB,CAAgD,EACvDH,EAAA,EAAOP,CAAAW,OAAA,CAAiB,SAAjB,CAA6B,EAMhCC,EAAAA,CAAeL,CAAAM,OAAfD,CAA4B,CACb,KAAnB,CAAIA,CAAJ,EACEnB,CAAAqB,KAAA,CAAU,UAAV,CASqChB,CATrC,CACE,6DADF;AAEEc,CAFF,CAEiB,iBAFjB,CASFf,EAAAkB,OAAA,CAJOR,CAG6B,CArCW,CAzPnDlB,CAAA2B,OAAA,CAAe,WAAf,CAA4B,CAAC,IAAD,CAA5B,CAAAC,SAAA,CAOY,UAPZ,CAOwB,CAACC,QAAyB,EAAG,CAwBjD,IAAIC,EAAW,IAAAA,SAAXA,CAA2B,EAiC/B,KAAAC,KAAA,CAAY,CAAC,gBAAD,CAAmB,gBAAnB,CAAqC,QAAQ,CAACC,CAAD,CAAiBC,CAAjB,CAAiC,CACxF,MAAO,CAWLC,IAAKA,QAAQ,CAACC,CAAD,CAAM,CACjB,MAAOH,EAAA,EAAA,CAAiBG,CAAjB,CADU,CAXd,CAyBLC,UAAWA,QAAQ,CAACD,CAAD,CAAM,CAEvB,MAAO,CADHzB,CACG,CADK,IAAAwB,IAAA,CAASC,CAAT,CACL,EAAQnC,CAAAqC,SAAA,CAAiB3B,CAAjB,CAAR,CAAkCA,CAFlB,CAzBpB,CAuCL4B,OAAQA,QAAQ,EAAG,CACjB,MAAON,EAAA,EADU,CAvCd,CAuDLO,IAAKA,QAAQ,CAACJ,CAAD,CAAMzB,CAAN,CAAaC,CAAb,CAAsB,CACjCsB,CAAA,CAAeE,CAAf,CAAoBzB,CAApB,CAAuCC,CAvFpC,CAAUX,CAAAwC,OAAA,CAAe,EAAf,CAAmBV,CAAnB,CAuF0BnB,CAvF1B,CAAV,CAAkDmB,CAuFrD,CADiC,CAvD9B,CAuELW,UAAWA,QAAQ,CAACN,CAAD,CAAMzB,CAAN,CAAaC,CAAb,CAAsB,CACvC,IAAA4B,IAAA,CAASJ,CAAT,CAAcnC,CAAA0C,OAAA,CAAehC,CAAf,CAAd,CAAqCC,CAArC,CADuC,CAvEpC,CAsFLgC,OAAQA,QAAQ,CAACR,CAAD,CAAMxB,CAAN,CAAe,CAC7BsB,CAAA,CAAeE,CAAf,CAAoBlC,CAApB,CAA2CU,CAtHxC,CAAUX,CAAAwC,OAAA,CAAe,EAAf,CAAmBV,CAAnB,CAsH8BnB,CAtH9B,CAAV,CAAkDmB,CAsHrD,CAD6B,CAtF1B,CADiF,CAA9E,CAzDqC,CAA7B,CAPxB,CA8JA9B,EAAA2B,OAAA,CAAe,WAAf,CAAAiB,QAAA,CAiCS,cAjCT;AAiCyB,CAAC,UAAD,CAAa,QAAQ,CAACC,CAAD,CAAW,CAErD,MAAO,CAWLX,IAAKA,QAAQ,CAACC,CAAD,CAAM,CACjB,MAAOU,EAAAT,UAAA,CAAmBD,CAAnB,CADU,CAXd,CAyBLI,IAAKA,QAAQ,CAACJ,CAAD,CAAMzB,CAAN,CAAa,CACxBmC,CAAAJ,UAAA,CAAmBN,CAAnB,CAAwBzB,CAAxB,CADwB,CAzBrB,CAsCLiC,OAAQA,QAAQ,CAACR,CAAD,CAAM,CACpBU,CAAAF,OAAA,CAAgBR,CAAhB,CADoB,CAtCjB,CAF8C,CAAhC,CAjCzB,CAqIAjC,EAAA4C,QAAA,CAAyB,CAAC,WAAD,CAAc,MAAd,CAAsB,UAAtB,CAEzB9C,EAAA2B,OAAA,CAAe,WAAf,CAAAC,SAAA,CAAqC,gBAArC,CAAuDmB,QAA+B,EAAG,CACvF,IAAAhB,KAAA,CAAY7B,CAD2E,CAAzF,CAvTsC,CAArC,CAAD,CA4TGH,MA5TH,CA4TWA,MAAAC,QA5TX;", +"sources":["angular-cookies.js"], +"names":["window","angular","undefined","$$CookieWriter","$document","$log","$browser","cookiePath","baseHref","rawDocument","name","value","options","path","expires","isDefined","isUndefined","isString","Date","str","encodeURIComponent","domain","toUTCString","secure","cookieLength","length","warn","cookie","module","provider","$CookiesProvider","defaults","$get","$$cookieReader","$$cookieWriter","get","key","getObject","fromJson","getAll","put","extend","putObject","toJson","remove","factory","$cookies","$inject","$$CookieWriterProvider"] +} diff --git a/ecomp-portal-FE/client/bower_components/angular-cookies/bower.json b/ecomp-portal-FE/client/bower_components/angular-cookies/bower.json new file mode 100644 index 00000000..2171b173 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cookies/bower.json @@ -0,0 +1,10 @@ +{ + "name": "angular-cookies", + "version": "1.5.0", + "license": "MIT", + "main": "./angular-cookies.js", + "ignore": [], + "dependencies": { + "angular": "1.5.0" + } +} diff --git a/ecomp-portal-FE/client/bower_components/angular-cookies/index.js b/ecomp-portal-FE/client/bower_components/angular-cookies/index.js new file mode 100644 index 00000000..65766754 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cookies/index.js @@ -0,0 +1,2 @@ +require('./angular-cookies'); +module.exports = 'ngCookies'; diff --git a/ecomp-portal-FE/client/bower_components/angular-cookies/package.json b/ecomp-portal-FE/client/bower_components/angular-cookies/package.json new file mode 100644 index 00000000..ea0fb55c --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-cookies/package.json @@ -0,0 +1,26 @@ +{ + "name": "angular-cookies", + "version": "1.5.0", + "description": "AngularJS module for cookies", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/angular/angular.js.git" + }, + "keywords": [ + "angular", + "framework", + "browser", + "cookies", + "client-side" + ], + "author": "Angular Core Team ", + "license": "MIT", + "bugs": { + "url": "https://github.com/angular/angular.js/issues" + }, + "homepage": "http://angularjs.org" +} diff --git a/ecomp-portal-FE/client/bower_components/angular-gestures/.bower.json b/ecomp-portal-FE/client/bower_components/angular-gestures/.bower.json new file mode 100644 index 00000000..11ffa80f --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-gestures/.bower.json @@ -0,0 +1,32 @@ +{ + "name": "angular-gestures", + "description": "AngularJS directive that adds support for multi touch gestures to your app. Based on hammer.js.", + "version": "0.3.1", + "main": [ + "gestures.min.js" + ], + "homepage": "http://github.com/wzr1337/angular-gestures", + "repository": { + "type": "git", + "url": "git://github.com/wzr1337/bower-angular-gestures" + }, + "author": "wzr1337", + "license": "MIT", + "readmeFilename": "README.md", + "dependencies": { + "angular": ">=1.2.0 <=1.4.0", + "hammerjs": "~2.0.0" + }, + "devDependencies": { + "angular-mocks": ">=1.2.0 <=1.4.0" + }, + "_release": "0.3.1", + "_resolution": { + "type": "version", + "tag": "0.3.1", + "commit": "cc39f1235f3f68d579853bad101455a5d2c6165e" + }, + "_source": "https://github.com/wzr1337/bower-angular-gestures.git", + "_target": "*", + "_originalSource": "angular-gestures" +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-gestures/LICENSE.md b/ecomp-portal-FE/client/bower_components/angular-gestures/LICENSE.md new file mode 100644 index 00000000..4347861c --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-gestures/LICENSE.md @@ -0,0 +1,21 @@ +## The MIT License + +Copyright (c) 2012-2013 Patrick Bartsch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-gestures/README.md b/ecomp-portal-FE/client/bower_components/angular-gestures/README.md new file mode 100644 index 00000000..89e74389 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-gestures/README.md @@ -0,0 +1,82 @@ +# angular-gestures + +AngularJS directive that adds support for multi touch gestures to your app, based on hammer.js. + +## Usage + +* Include `gestures.js` or `gestures.min.js` into your page +* Declare `'angular-gestures'` as a dependency for your angular app: `angular.module('myApp', ['angular-gestures']);` +* Use attributes on containers the same way you use `ng-click`: e.g. `hm-tap` +```HTML + +``` +* You can use angular interpolations like this : `hm-swipe="remove_something({{ id }})"` +* You can also use Hammer.js options by e.g. `hm-tap-opts="{hold: false}"` + +### Event data + +Pass the `$event` object in the usual way e.g. `hm-drag="myDrag($event)"` then access its internals like so: +```JS +$scope.myDrag = function(event) { + console.log(event.gesture); +} +``` +Refer to the [Hammer.js docs](https://github.com/EightMedia/hammer.js/wiki/Getting-Started) for more details on the properties of `event`. + +## Supported events + + +* hmDoubleTap : 'doubletap', +* hmDragstart : 'dragstart', +* hmDrag : 'drag', +* hmDragUp : 'dragup', +* hmDragDown : 'dragdown', +* hmDragLeft : 'dragleft', +* hmDragRight : 'dragright', +* hmDragend : 'dragend', +* hmHold : 'hold', +* hmPinch : 'pinch', +* hmPinchIn : 'pinchin', +* hmPinchOut : 'pinchout', +* hmRelease : 'release', +* hmRotate : 'rotate', +* hmSwipe : 'swipe', +* hmSwipeUp : 'swipeup', +* hmSwipeDown : 'swipedown', +* hmSwipeLeft : 'swipeleft', +* hmSwipeRight : 'swiperight', +* hmTap : 'tap', +* hmTouch : 'touch', +* hmTransformstart : 'transformstart', +* hmTransform : 'transform', +* hmTransformend : 'transformend' + + +All [Hammerjs events](https://github.com/EightMedia/hammer.js/wiki/Getting-Started) are supported. The corresponding Angularjs attribute has `hm-` prepended to the name. So for example, the 'doubletap' event becomes `hm-double-tap` etc. + +*Attention* : *end and *start events are NOT CamelCased because of issues caused by $animate interference. + +## Default options +To set recognizer default options you can use `hammerDefaultOptsProvider`. Access it like in the demo: + +``` +angular.module('angularGesturesDemoApp', ['angular-gestures', 'ngRoute']) + .config(function ($routeProvider, hammerDefaultOptsProvider) { + $routeProvider + .when('/', { + templateUrl: 'views/main.html', + controller: 'MainCtrl' + }) + .otherwise({ + redirectTo: '/' + }); + hammerDefaultOptsProvider.set({ + recognizers: [[Hammer.Tap, {time: 250}]] + }); + }); +``` + +## Bower +If you want to use angular-momentum-scroll with bower, add the following dependency to your component.json + +`"angular-gestures": "latest"` diff --git a/ecomp-portal-FE/client/bower_components/angular-gestures/bower.json b/ecomp-portal-FE/client/bower_components/angular-gestures/bower.json new file mode 100644 index 00000000..78f484e1 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-gestures/bower.json @@ -0,0 +1,23 @@ +{ + "name": "angular-gestures", + "description": "AngularJS directive that adds support for multi touch gestures to your app. Based on hammer.js.", + "version": "0.3.1", + "main": [ + "gestures.min.js" + ], + "homepage": "http://github.com/wzr1337/angular-gestures", + "repository": { + "type": "git", + "url": "git://github.com/wzr1337/bower-angular-gestures" + }, + "author": "wzr1337", + "license": "MIT", + "readmeFilename": "README.md", + "dependencies": { + "angular": ">=1.2.0 <=1.4.0", + "hammerjs": "~2.0.0" + }, + "devDependencies": { + "angular-mocks": ">=1.2.0 <=1.4.0" + } +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-gestures/gestures.js b/ecomp-portal-FE/client/bower_components/angular-gestures/gestures.js new file mode 100644 index 00000000..68d78eb9 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-gestures/gestures.js @@ -0,0 +1,158 @@ +'use strict'; + +angular.module('angular-gestures', []); + +/** + * Inspired by AngularJS' implementation of "click dblclick mousedown..." + * + * This ties in the Hammer 2 events to attributes like: + * + * hm-tap="add_something()" hm-swipe="remove_something()" + * + * and also has support for Hammer options with: + * + * hm-tap-opts="{hold: false}" + * + * or any other of the "hm-event" listed underneath. + */ +var HGESTURES = { + hmDoubleTap: 'doubletap', + hmDragstart: 'panstart', // will bedeprecated soon, us Pan* + hmDrag: 'pan', // will bedeprecated soon, us Pan* + hmDragUp: 'panup', // will bedeprecated soon, us Pan* + hmDragDown: 'pandown', // will bedeprecated soon, us Pan* + hmDragLeft: 'panleft', // will bedeprecated soon, us Pan* + hmDragRight: 'panright', // will bedeprecated soon, us Pan* + hmDragend: 'panend', // will bedeprecated soon, us Pan* + hmPanstart: 'panstart', + hmPan: 'pan', + hmPanUp: 'panup', + hmPanDown: 'pandown', + hmPanLeft: 'panleft', + hmPanRight: 'panright', + hmPanend: 'panend', + hmHold: 'press', + hmPinch: 'pinch', + hmPinchIn: 'pinchin', + hmPinchOut: 'pinchout', + hmPress: 'press', + hmRelease: 'release', + hmRotate: 'rotate', + hmSwipe: 'swipe', + hmSwipeUp: 'swipeup', + hmSwipeDown: 'swipedown', + hmSwipeLeft: 'swipeleft', + hmSwipeRight: 'swiperight', + hmTap: 'tap', + hmTouch: 'touch', + hmTransformstart: 'transformstart', + hmTransform: 'transform', + hmTransformend: 'transformend' + }; + +var HRECOGNIZERS = { + hmDoubleTap: [Hammer.Tap, 'Hammer.Tap'], + hmDragstart: [Hammer.Pan, 'Hammer.Pan'], + hmDrag: [Hammer.Pan, 'Hammer.Pan'], + hmDragUp: [Hammer.Pan, 'Hammer.Pan'], + hmDragDown: [Hammer.Pan, 'Hammer.Pan'], + hmDragLeft: [Hammer.Pan, 'Hammer.Pan'], + hmDragRight: [Hammer.Pan, 'Hammer.Pan'], + hmDragend: [Hammer.Pan, 'Hammer.Pan'], + hmPanstart: [Hammer.Pan, 'Hammer.Pan'], + hmPan: [Hammer.Pan, 'Hammer.Pan'], + hmPanUp: [Hammer.Pan, 'Hammer.Pan'], + hmPanDown: [Hammer.Pan, 'Hammer.Pan'], + hmPanLeft: [Hammer.Pan, 'Hammer.Pan'], + hmPanRight: [Hammer.Pan, 'Hammer.Pan'], + hmPanend: [Hammer.Pan, 'Hammer.Pan'], + hmHold: [Hammer.Press, 'Hammer.Press'], + hmPinch: [Hammer.Pinch, 'Hammer.Pinch'], + hmPinchIn: [Hammer.Pinch, 'Hammer.Pinch'], + hmPinchOut: [Hammer.Pinch, 'Hammer.Pinch'], + hmPress: [Hammer.Press, 'Hammer.Press'], + hmRotate: [Hammer.Rotate, 'Hammer.Rotate'], + hmSwipe: [Hammer.Swipe, 'Hammer.Swipe'], + hmSwipeUp: [Hammer.Swipe, 'Hammer.Swipe'], + hmSwipeDown: [Hammer.Swipe, 'Hammer.Swipe'], + hmSwipeLeft: [Hammer.Swipe, 'Hammer.Swipe'], + hmSwipeRight: [Hammer.Swipe, 'Hammer.Swipe'], + hmTap: [Hammer.Tap, 'Hammer.Tap'] + }; + +var VERBOSE = false; + +angular.forEach(HGESTURES, function(eventName, directiveName) { + angular.module('angular-gestures').directive(directiveName, ['$parse', '$log', '$timeout', 'hammerDefaultOpts', function($parse, $log, $timeout, hammerDefaultOpts) { + return function(scope, element, attr) { + var handler; + attr.$observe(directiveName, function(value) { + var callback = $parse(value); + var opts = $parse(attr[directiveName + 'Opts'])(scope, {}); + var defaultOpts = angular.copy(hammerDefaultOpts); + + angular.extend(defaultOpts, opts); + + if (angular.isUndefined(element.hammertime)) { + + // validate that needed recognizer is enabled + var recognizers = angular.isDefined(defaultOpts.recognizers) ? defaultOpts.recognizers : []; + var recognizer = HRECOGNIZERS[directiveName]; + if(angular.isDefined(recognizer)) { + var enabled = false; + angular.forEach(recognizers, function(r) { + if (recognizer[0] === r[0]) { + if (angular.isUndefined(r[1].enable) || r[1].enable === true) { + enabled = true; + } + } + }); + if (!enabled) { + throw new Error('Directive ' + directiveName + ' requires gesture recognizer [' + recognizer[1] + '] to be enabled'); + } + } + + element.hammer = new Hammer.Manager(element[0], defaultOpts); + scope.$on('$destroy', function() { + element.hammer.off(eventName); + element.hammer.destroy(); + }); + } + + handler = function(event) { + if (VERBOSE) { + $log.debug('angular-gestures: ', eventName, event); + } + var callbackHandler = function () { + var cb = callback(scope, { $event : event}); + if (typeof cb === 'function') { + cb.call(scope, event); + } + }; + + if (scope.$root.$$phase === '$apply' || + scope.$root.$$phase === '$digest') { + callbackHandler(); + } else { + scope.$apply(callbackHandler); + } + + }; + // register actual event + element.hammer.on(eventName, handler); + }); + }; + }]); + }); + +angular.module('angular-gestures').provider('hammerDefaultOpts', function HammerDefaultOptsProvider() { + var opts = {}; + + this.set = function(value) { + opts = value; + }; + + this.$get = function() { + return opts; + }; + }); diff --git a/ecomp-portal-FE/client/bower_components/angular-gestures/gestures.min.js b/ecomp-portal-FE/client/bower_components/angular-gestures/gestures.min.js new file mode 100644 index 00000000..3ad4b593 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-gestures/gestures.min.js @@ -0,0 +1 @@ +"use strict";angular.module("angular-gestures",[]);var HGESTURES={hmDoubleTap:"doubletap",hmDragstart:"panstart",hmDrag:"pan",hmDragUp:"panup",hmDragDown:"pandown",hmDragLeft:"panleft",hmDragRight:"panright",hmDragend:"panend",hmPanstart:"panstart",hmPan:"pan",hmPanUp:"panup",hmPanDown:"pandown",hmPanLeft:"panleft",hmPanRight:"panright",hmPanend:"panend",hmHold:"press",hmPinch:"pinch",hmPinchIn:"pinchin",hmPinchOut:"pinchout",hmPress:"press",hmRelease:"release",hmRotate:"rotate",hmSwipe:"swipe",hmSwipeUp:"swipeup",hmSwipeDown:"swipedown",hmSwipeLeft:"swipeleft",hmSwipeRight:"swiperight",hmTap:"tap",hmTouch:"touch",hmTransformstart:"transformstart",hmTransform:"transform",hmTransformend:"transformend"},HRECOGNIZERS={hmDoubleTap:[Hammer.Tap,"Hammer.Tap"],hmDragstart:[Hammer.Pan,"Hammer.Pan"],hmDrag:[Hammer.Pan,"Hammer.Pan"],hmDragUp:[Hammer.Pan,"Hammer.Pan"],hmDragDown:[Hammer.Pan,"Hammer.Pan"],hmDragLeft:[Hammer.Pan,"Hammer.Pan"],hmDragRight:[Hammer.Pan,"Hammer.Pan"],hmDragend:[Hammer.Pan,"Hammer.Pan"],hmPanstart:[Hammer.Pan,"Hammer.Pan"],hmPan:[Hammer.Pan,"Hammer.Pan"],hmPanUp:[Hammer.Pan,"Hammer.Pan"],hmPanDown:[Hammer.Pan,"Hammer.Pan"],hmPanLeft:[Hammer.Pan,"Hammer.Pan"],hmPanRight:[Hammer.Pan,"Hammer.Pan"],hmPanend:[Hammer.Pan,"Hammer.Pan"],hmHold:[Hammer.Press,"Hammer.Press"],hmPinch:[Hammer.Pinch,"Hammer.Pinch"],hmPinchIn:[Hammer.Pinch,"Hammer.Pinch"],hmPinchOut:[Hammer.Pinch,"Hammer.Pinch"],hmPress:[Hammer.Press,"Hammer.Press"],hmRotate:[Hammer.Rotate,"Hammer.Rotate"],hmSwipe:[Hammer.Swipe,"Hammer.Swipe"],hmSwipeUp:[Hammer.Swipe,"Hammer.Swipe"],hmSwipeDown:[Hammer.Swipe,"Hammer.Swipe"],hmSwipeLeft:[Hammer.Swipe,"Hammer.Swipe"],hmSwipeRight:[Hammer.Swipe,"Hammer.Swipe"],hmTap:[Hammer.Tap,"Hammer.Tap"]},VERBOSE=!1;angular.forEach(HGESTURES,function(a,b){angular.module("angular-gestures").directive(b,["$parse","$log","$timeout","hammerDefaultOpts",function(c,d,e,f){return function(e,g,h){var i;h.$observe(b,function(j){var k=c(j),l=c(h[b+"Opts"])(e,{}),m=angular.copy(f);if(angular.extend(m,l),angular.isUndefined(g.hammertime)){var n=angular.isDefined(m.recognizers)?m.recognizers:[],o=HRECOGNIZERS[b];if(angular.isDefined(o)){var p=!1;if(angular.forEach(n,function(a){o[0]===a[0]&&(angular.isUndefined(a[1].enable)||a[1].enable===!0)&&(p=!0)}),!p)throw new Error("Directive "+b+" requires gesture recognizer ["+o[1]+"] to be enabled")}g.hammer=new Hammer.Manager(g[0],m),e.$on("$destroy",function(){g.hammer.off(a),g.hammer.destroy()})}i=function(b){VERBOSE&&d.debug("angular-gestures: ",a,b);var c=function(){var a=k(e,{$event:b});"function"==typeof a&&a.call(e,b)};"$apply"===e.$root.$$phase||"$digest"===e.$root.$$phase?c():e.$apply(c)},g.hammer.on(a,i)})}}])}),angular.module("angular-gestures").provider("hammerDefaultOpts",function(){var a={};this.set=function(b){a=b},this.$get=function(){return a}}); \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-material/.bower.json b/ecomp-portal-FE/client/bower_components/angular-material/.bower.json new file mode 100644 index 00000000..24f568de --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-material/.bower.json @@ -0,0 +1,24 @@ +{ + "name": "angular-material", + "version": "0.9.8", + "ignore": [], + "dependencies": { + "angular": "^1.3.0 || >1.4.0-beta.0", + "angular-animate": "^1.3.0 || >1.4.0-beta.0", + "angular-aria": "^1.3.15 || >1.4.0-beta.0" + }, + "main": [ + "angular-material.js", + "angular-material.css" + ], + "homepage": "https://github.com/angular/bower-material", + "_release": "0.9.8", + "_resolution": { + "type": "version", + "tag": "v0.9.8", + "commit": "5f7de8ced18d4663d7a90caecf620fc1f8a66568" + }, + "_source": "https://github.com/angular/bower-material.git", + "_target": "0.9.8", + "_originalSource": "angular-material" +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-material/.gitignore b/ecomp-portal-FE/client/bower_components/angular-material/.gitignore new file mode 100644 index 00000000..f45564b6 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-material/.gitignore @@ -0,0 +1,5 @@ +*.log +*.sw* +.DS_STORE +/.idea/ +default-theme.css diff --git a/ecomp-portal-FE/client/bower_components/angular-material/CHANGELOG.md b/ecomp-portal-FE/client/bower_components/angular-material/CHANGELOG.md new file mode 100644 index 00000000..75a8cb43 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-material/CHANGELOG.md @@ -0,0 +1,1543 @@ + +### 0.9.8 (2015-06-08) + + +#### Bug Fixes + +* **tabs:** + * prevents tabs from shrinking when used with flexbox ([dd041927](https://github.com/angular/material/commit/dd0419277523a44f8d06e45dc3c31b2afb3c8101), closes [#3011](https://github.com/angular/material/issues/3011)) + * prevents select/deselect events from firing on destroy ([1ba0686e](https://github.com/angular/material/commit/1ba0686e7b42fef6072d011bf2f61fc3576a2017)) + + + +### 0.9.8-rc1 (2015-06-05) + + +#### Features + +* **autocomplete:** custom template demo ([7d2deb7e](https://github.com/angular/material/commit/7d2deb7ec702dac7999ae6b0fdba966c497e46ca), closes [#2505](https://github.com/angular/material/issues/2505)) +* **docs:** + * Use $scope instead of scope ([9683d66b](https://github.com/angular/material/commit/9683d66b9be37db753b5bca56d4531b35c99422f), closes [#2788](https://github.com/angular/material/issues/2788)) + * css customizations for buttons ([91c86c63](https://github.com/angular/material/commit/91c86c63e2aec579c9043fec1e548980eb8aa005)) +* **icons:** api changes for font-icons ([498048dc](https://github.com/angular/material/commit/498048dc9e340b98a001ea3223a25c42a556e747)) +* **tabs:** + * adds an opt-out option for disconnecting tab scopes ([4c1bf4b6](https://github.com/angular/material/commit/4c1bf4b6850d09ba27ffe744e7bdc50e435a6ccf)) + * makes swipe gestures on the content area opt-in ([03d01e96](https://github.com/angular/material/commit/03d01e96623de397ef171c063b7a8a56656fe0de), closes [#2331](https://github.com/angular/material/issues/2331)) +* **theming:** reduce specificity of default-theme selectors ([8468c600](https://github.com/angular/material/commit/8468c6008c10f8d218af057ab31f519efffad8d5)) + + +#### Breaking Changes + +* Tabs will now require the `md-swipe-content` attribute in order to enable swipe gestures to change tabs in the content area. + + ([03d01e96](https://github.com/angular/material/commit/03d01e96623de397ef171c063b7a8a56656fe0de)) + + +#### Bug Fixes + +* add missing alt attribute to logo in docs ([19ad66d1](https://github.com/angular/material/commit/19ad66d1a5954296de68d3f85d78a3522ff488e5)) +* **autocomplete:** + * selecting an item will hide the menu in IE11 ([0c073d10](https://github.com/angular/material/commit/0c073d10a18409d1d1f900951e42ba35df79d81e), closes [#2188](https://github.com/angular/material/issues/2188), [#3008](https://github.com/angular/material/issues/3008)) + * removes flicker of 'not found' message with async results ([08532b40](https://github.com/angular/material/commit/08532b40a336cd6fbd5e73356fa5142fbb305ca7)) + * adds cleanup step when autocomplete is destroyed ([e3a82c92](https://github.com/angular/material/commit/e3a82c92af2679e92e3b7f23deeffedf064e1a3c), closes [#2624](https://github.com/angular/material/issues/2624)) + * fixes positioning of progress bar ([85a75909](https://github.com/angular/material/commit/85a75909aad3cbac28defd3ec7a2ef45ea81ee0a), closes [#3115](https://github.com/angular/material/issues/3115)) + * updates docs, removes `clear` button when using a floating label ([86b4afab](https://github.com/angular/material/commit/86b4afabf3fbe210635426af29ac73d2e03b70a1), closes [#2727](https://github.com/angular/material/issues/2727)) + * selecting a value will now flag the form/input as `$dirty` ([217cae1f](https://github.com/angular/material/commit/217cae1f599c842687c1d2a19056df3820c06d96), closes [#2753](https://github.com/angular/material/issues/2753)) +* **list:** support empty list-item(s) ([e9ad5491](https://github.com/angular/material/commit/e9ad5491a3bfbfe5802b580ba7e570f03585db6e), closes [#2649](https://github.com/angular/material/issues/2649)) +* **slider:** + * fix for attributes being ignored ([07295879](https://github.com/angular/material/commit/072958796a64c2175de9e4cb0de10a8025f0dc7c)) + * increment the value properly when step is a decimal number ([476d068c](https://github.com/angular/material/commit/476d068c6f6e204d2c0797be08346076154a9029), closes [#2015](https://github.com/angular/material/issues/2015)) +* **styles:** fixes theming specificy regarding border colors ([264f043e](https://github.com/angular/material/commit/264f043e4220d13f74fef013081c68dcd878c12d)) +* **tabs:** fixes continuous `$digest` bug in Firefox when using `md-stretch-tabs` ([5372710a](https://github.com/angular/material/commit/5372710a7a4764a908f08deeb06481ae71e539d3), closes [#3101](https://github.com/angular/material/issues/3101)) +* **tooltip:** + * moves tooltip to the $rootElement rather than body or md-content ([32b0facc](https://github.com/angular/material/commit/32b0facc0b971f12f777f8f7b7392db1e5c5e091)) + * tooltip will attach to the body rather than $rootElement ([24ca2966](https://github.com/angular/material/commit/24ca2966cf08a56aad59f4f60b186664fcd7e7f3)) + + + +### 0.9.7 (2015-06-01) + + +#### Features + +* **autocomplete:** adds support for validation/ng-messages ([1f0a8450](https://github.com/angular/material/commit/1f0a845033df53244c54b47b42173fb37241586c), closes [#2808](https://github.com/angular/material/issues/2808), [#2664](https://github.com/angular/material/issues/2664)) +* **icons:** add support for font-icons with ligatures and Material-Icon demos ([a1074907](https://github.com/angular/material/commit/a10749074ee05b5f12685c546c52165bf7420eb9), closes [#3059](https://github.com/angular/material/issues/3059)) + + +#### Breaking Changes + +* **autocomplete:** `name` attribute has been changed to `input-name` to more clearly show its purpose + + +#### Bug Fixes + +* **autocomplete:** + * only 2 messages will be displayed at any given time for autocomplete ([6b4e95ce](https://github.com/angular/material/commit/6b4e95ce23dded8787329cc0f394fe9178d5bc06), closes [#2785](https://github.com/angular/material/issues/2785)) + * prevents text from going under the `clear` button ([73e03a70](https://github.com/angular/material/commit/73e03a704124475cadb2eec5de09403c9a447186), closes [#2193](https://github.com/angular/material/issues/2193), [#2578](https://github.com/angular/material/issues/2578)) + * prevents error on keydown when menu is hidden ([a7dcfb6d](https://github.com/angular/material/commit/a7dcfb6d9427d0645ed98f53f9575925177dcbe6), closes [#2774](https://github.com/angular/material/issues/2774)) + * autocomplete will no longer show the dropdown on value changes when not focused ([c3ec08d8](https://github.com/angular/material/commit/c3ec08d8aa5ccfa95fec4f41a5be09369cd19f73), closes [#2756](https://github.com/angular/material/issues/2756)) +* **input:** update disabled underline styles ([8f2b2c68](https://github.com/angular/material/commit/8f2b2c6835feb151e362d9e135c795055f9a5ccd), closes [#2870](https://github.com/angular/material/issues/2870)) +* **progressLinear:** rewrites animation for progress linear ([1d478ebd](https://github.com/angular/material/commit/1d478ebd6e5c6ddb6453cc1a708064459075a75e), closes [#2762](https://github.com/angular/material/issues/2762)) +* **tabs:** + * adds ui-sref/ng-href support to tabs ([6c53d112](https://github.com/angular/material/commit/6c53d1127fc1cb7d2a86802e5831642a40ff1e41), closes [#2344](https://github.com/angular/material/issues/2344)) + * updating dynamic tab labels will now be reflected ([8ac4fa1b](https://github.com/angular/material/commit/8ac4fa1b3f23f1f73125cc7e2425d349d7ad546e)) + * select/deselect events will now fire when a tab is removed but the index does no ([5e47b52b](https://github.com/angular/material/commit/5e47b52bd6a8817b2624ca6797b8267a162f84af), closes [#2837](https://github.com/angular/material/issues/2837)) + * initial state will now scroll to selected tab ([120c271b](https://github.com/angular/material/commit/120c271be0058ec96e676c34dd55baf53357ae6d), closes [#2838](https://github.com/angular/material/issues/2838)) +* **toolbar:** add h1 to toolbar tools headings ([c100ef10](https://github.com/angular/material/commit/c100ef10069f769f90d606792c68ca9042fe67a4)) +* **tooltip:** + * pointer-events 'none' used properly with activate events ([667e4c24](https://github.com/angular/material/commit/667e4c244ccd9a1dfef6894a64b6df0c5e2f6305)) + * hide tooltip after mouseleave if focus is achieved through mousedown ([e73d290c](https://github.com/angular/material/commit/e73d290cc84f07612d0faf08aec014714f18011a)) + + + +### 0.9.6 (2015-05-28) + + +#### Bug Fixes + +* **build:** fixes bug where versions in 0.9.5 comments were wrong. ([7a1ad41f](https://github.com/angular/material/commit/7a1ad41f3c1df6cb1dfa750cb817f166f02097ee)) +* **closure:** fixes issue in core.js where multiple `goog.provide` lines were included + + + +### 0.9.5 (2015-05-28) + + +#### Features + +* **accessibility:** additional Windows high-contrast styles ([37bc5b6f](https://github.com/angular/material/commit/37bc5b6f54bde618df8cfd9f85c0f860c811e451)) +* **button:** md-icon-button to use round ripple ([ab1e9e05](https://github.com/angular/material/commit/ab1e9e05908114fe5fb587f9b59aab4db749f9b3)) +* **listItem:** allow use of own md-buttons ([0ef4b79f](https://github.com/angular/material/commit/0ef4b79f53da91edc9f5591ceeb1950e73c50d3d)) + + +#### Breaking Changes + +* Removed attach(button|tab|checkbox|list)Behavior + in favor of composing an injectable ripple service specific to + your target. $mdInkRipple was too aware of how to configure + components. You now have access to $mdButtonInkRipple, + $mdTabInkRipple, $mdListInkRipple, $mdCheckboxInkRipple. + + Change your code from this: + ``` javascript + function MyService($mdInkRipple) { + //... Included for brevity + $mdInkRipple.attachButtonBehavior(scope, element, options); + } + ``` + + To this: + ``` javascript + function MyService($mdButtonInkRipple) { + //... Included for brevity + $mdButtonInkRipple.attach(scope, element, options); + } + ``` +* **icons:** Default size for `md-icon` has been changed from `28px` to `24px` +* **tabs:** Replaces pagination transition with `transform` rather than `left` for performance + + ([3b0f12e3](https://github.com/angular/material/commit/3b0f12e3b8e7c5b7ab78ea0b8672d1b1b54ef4b8)) + + +#### Bug Fixes + +* **autocomplete:** + * uses $attr to support `data-` prefixed attributes ([1dc56a6c](https://github.com/angular/material/commit/1dc56a6cfae0ace6c20848f65c3d6262bb9973bb), closes [#2798](https://github.com/angular/material/issues/2798)) + * resolves xss bug with autocomplete text highlighter ([1538ebe9](https://github.com/angular/material/commit/1538ebe9c2d8b9aec84d1f556a9b4cfe5a38dc04), closes [#2901](https://github.com/angular/material/issues/2901)) + * pulls in text content as HTML to prevent it from being un-escaped ([33ac259d](https://github.com/angular/material/commit/33ac259d66c90e96489d0512ac762f969458f5bd)) +* **build:** + * build will now update CHANGELOG in master ([859ceb18](https://github.com/angular/material/commit/859ceb1866094a85625de532ff578779712ff7d5)) + * fixes issue where release tag doesn't match release branch ([5f7b1e91](https://github.com/angular/material/commit/5f7b1e91d1575d89de8c228099b5213c7347cb48)) +* **button:** makes icon-buttons round for ripple and focus state ([98025aaf](https://github.com/angular/material/commit/98025aaf19796ca7c6c59ec37f37233a42787cc3)) +* **chips:** removes box-shadow from inline autocomplete ([bc407590](https://github.com/angular/material/commit/bc4075909baaa828b12d1327b7a57896e505c35a)) +* **demo:** use relative path to img src ([175fd54d](https://github.com/angular/material/commit/175fd54d16921e2263ac1f696be30ce58ea67a5d), closes [#2916](https://github.com/angular/material/issues/2916)) +* **docs:** + * use source path for github url ([0a1ed581](https://github.com/angular/material/commit/0a1ed5813e07ea9807ee88f9047c6970593dc399), closes [#2900](https://github.com/angular/material/issues/2900)) + * use source rather than combined js ([fd8fcf22](https://github.com/angular/material/commit/fd8fcf22fa85ae8c9af59d59e9dc73be93d21c2b)) +* **list:** correctly proxy ng-disabled attrs ([a25bc0ea](https://github.com/angular/material/commit/a25bc0ea42cbaedd569d385d0c722702ae0bf60d)) +* **listItem:** fix ng-enter having animation ([41953d5a](https://github.com/angular/material/commit/41953d5a0ad3492082b3fd99a82e212c59e75750)) +* **md-icon:** change icon size back to 24px ([3b305a3d](https://github.com/angular/material/commit/3b305a3d5bb46e366c9b7bfb7a069ff040392529), closes [#2992](https://github.com/angular/material/issues/2992)) +* **minify:** tabsDirective template function works when minified ([c7ea4a78](https://github.com/angular/material/commit/c7ea4a7865dd1b6b128c88bfa57ea92b9a617afa)) +* **select:** + * respect id attributes if assigned ([fc90fd31](https://github.com/angular/material/commit/fc90fd3173569f60acf53a2318fe453b42dd221a)) + * label not updating after objects resolved via promise ([74259976](https://github.com/angular/material/commit/742599769700e3a016db4454aa339bb3e35bf53f)) +* **subheader:** theme is applied to sticky clone ([e92686f0](https://github.com/angular/material/commit/e92686f0b400214ea0fd5209bff4923555a16bd4), closes [#2779](https://github.com/angular/material/issues/2779)) +* **tabs:** + * re-adds disconnected scopes for tab content ([e6f19d48](https://github.com/angular/material/commit/e6f19d4868059cd48e3dfd811273cff36c3ef9a4)) + * uses `ng-if` to remove inactive tabs once their animation is complete ([8dcaea8a](https://github.com/angular/material/commit/8dcaea8af19a6dcc2d9cafdd2ebb4277e66e89ae)) + * replaces pagination transition with `transform` rather than `left` for performan ([a8ff2ad1](https://github.com/angular/material/commit/a8ff2ad1779bdeabb2d960f69ebde23470353989)) + * fix missing dependency ([0b35c9b4](https://github.com/angular/material/commit/0b35c9b4c068fe13071a0a85da08125e316093e4), closes [#2460](https://github.com/angular/material/issues/2460)) +* **theming:** remove bogus hue value from grey palette ([0c28cee2](https://github.com/angular/material/commit/0c28cee2aad29b47a7f6b93ca5c27bca0d833dc1)) +* **tooltip:** use label instead of aria-describedby ([198199c1](https://github.com/angular/material/commit/198199c1eae39dbc7d139db42c5d4d3919aaeab2)) + + + +### 0.9.4 (2015-05-15) + +This interim release **fixes** an incorrect deployment of `angular-material.js`; which added the Closure Library **Namespace** features using `goog.provide()`. Only source in `/modules/closure/**.js` should use this namespacing. +#### Features + +* **build:** add script to snapshot docs site ([76e36722](https://github.com/angular/material/commit/76e36722e07846b518612e9073785a279b3027cd), closes [#2852](https://github.com/angular/material/issues/2852)) + + +#### Bug Fixes + +* **build:** fixes issue where JS files were being generated with Closure code ([ca940384](https://github.com/angular/material/commit/ca94038439982e81077020962042ec9f453dbf0e)), closes [#2852](https://github.com/angular/material/issues/2852)) + + + +### 0.9.3 (2015-05-14) + +This release is also using enhanced scripting to automate the build and release processes. Releases will now also deploy versioned docs to http://github.com/angular/code.material.angularjs.org. + +#### Bug Fixes + +* update rems to pixels ([08b89921](https://github.com/angular/material/commit/08b899210a963c87b1b70ccb88e2ee3191bc0647)) +* **autocomplete:** prevents `not found` message from displaying while results are loading ([3d5bd948](https://github.com/angular/material/commit/3d5bd94825688a6814266e7e1401b56e513f84c9)) +* **build:** enable build of component modules. ([8a886d79](https://github.com/angular/material/commit/8a886d7951e679174c7742d41a9a67c9f4462955)) +* **codepen:** + * escape ampersand in   ([0a5603f8](https://github.com/angular/material/commit/0a5603f8bc31c76012bf25e3657cb6f908f3bae1), closes [#2827](https://github.com/angular/material/issues/2827)) + * use https to avoid mixed content ([fbae0fcb](https://github.com/angular/material/commit/fbae0fcb697ecb959c3dc2922efe0fd8bb4a4124)) +* **dialog:** + * remove dialog wrapper from tab order ([dcb12a7c](https://github.com/angular/material/commit/dcb12a7c5ea8177de6f473bfe7bbf5c6d939bb75), closes [#2712](https://github.com/angular/material/issues/2712)) + * styling custom dialog wrapped in form ([666630ca](https://github.com/angular/material/commit/666630cab10fff2bb66bd0e6aeec552b27352db5), closes [#2637](https://github.com/angular/material/issues/2637)) + * style cleanup, button sizing ([9110d1e1](https://github.com/angular/material/commit/9110d1e1ace0b3d420d4ed329e61bde104ddea0d), closes [#2671](https://github.com/angular/material/issues/2671)) +* **gesture:** fix conflicts with Ionic ([05788d24](https://github.com/angular/material/commit/05788d242412f7b7d3babfab3931c0ee5a03aca2), closes [#1528](https://github.com/angular/material/issues/1528), [#1528](https://github.com/angular/material/issues/1528)) +* **list:** + * align icons to top ([a2b88bea](https://github.com/angular/material/commit/a2b88beae24127251c5844747b69f48ebeb120fa), closes [#2589](https://github.com/angular/material/issues/2589)) + * support aria-label for primary action ([e9324a9e](https://github.com/angular/material/commit/e9324a9ebf3ecabf0397a83a5275608600fa8115), closes [#2773](https://github.com/angular/material/issues/2773)) +* **tabs:** + * corrects css bug with tab-data positioning ([a8fd0f4d](https://github.com/angular/material/commit/a8fd0f4dc14154b1b41137773e157b7574661cb8)) + * reordering tabs should now work ([5bc3f232](https://github.com/angular/material/commit/5bc3f232f1850d80ec3dbf9bb7fbcf93b173f8fc)) +* **tests:** performance improvements and fixes to jasmine testing ([786b0ed3](https://github.com/angular/material/commit/786b0ed3652b7460c2c802efa1aa79972bd96f5d), closes [#2800](https://github.com/angular/material/issues/2800)) +* **toolbar:** align last icon button to spec ([f988255d](https://github.com/angular/material/commit/f988255d1a95e998e7e62b1e7b4d7c7687016ccb), closes [#2497](https://github.com/angular/material/issues/2497)) +* **tooltip:** update to spec, remove max-width ([8c60d9c7](https://github.com/angular/material/commit/8c60d9c7a63ab8598e7367bf7ef2b31afe1bdae6), closes [#656](https://github.com/angular/material/issues/656)) + + +#### Features + +* **docs:** edit code example in codepen ([5c37dc8c](https://github.com/angular/material/commit/5c37dc8c54ddd0e6ca3bd138665c4997c8189b52), closes [#2604](https://github.com/angular/material/issues/2604)) + + + +
      +
      + + +## 0.9.0 (2015-05-04) + + +#### Features + +* **autocomplete:** adds support for messages to be displayed when no results are found ([e057e271](https://github.com/angular/material/commit/e057e27171f15b1923d740a27447e7fafa66673a), closes [#2574](https://github.com/angular/material/issues/2574), [#1525](https://github.com/angular/material/issues/1525)) + +#### Breaking Changes + +* **styles:** removes global `line-height` and `font-size` from `html` and `body` ([666e3311](https://github.com/angular/material/commit/666e3311a8b66fb0910dc745192aaca23587bd29)) +* **icons:** namespaces built-in icons ([539ec5e3](https://github.com/angular/material/commit/539ec5e36281aa8a6f645376bcd4512911165fb9)) +* **gridlist:** `md-grid-tiles` must be direct children of `md-grid-list` ([5d9142ea](https://github.com/angular/material/commit/5d9142ea8d77e60350e8a7ddd02be6642218c0fa)) + + +#### Bug Fixes + +* **autocomplete:** + * improves logic behind scrolling items into view on keyboard navigation ([211a31ea](https://github.com/angular/material/commit/211a31ea363a721240d23b9f591e7652ab5f313e), closes [#2615](https://github.com/angular/material/issues/2615)) + * addresses issue where dropdown shows if there is an initial value ([85846922](https://github.com/angular/material/commit/85846922934226d2d62646df06d96f644e332378), closes [#2517](https://github.com/angular/material/issues/2517)) + * fixes positioning issue with clear button ([1fcb79a6](https://github.com/angular/material/commit/1fcb79a6e56e2c8d337c2875eca3740a8aafb235), closes [#2593](https://github.com/angular/material/issues/2593)) +* **button:** disable warn and accent buttons ([973c4d75](https://github.com/angular/material/commit/973c4d75847479d670dff620e1cc3104435bbcfc), closes [#2586](https://github.com/angular/material/issues/2586)) +* **compiler:** make bindToController vars available to instantiation fn ([e414091a](https://github.com/angular/material/commit/e414091ac0272652217010ca64ad96389f657e46)) +* **dialog:** makes sure that body is not more than 100% height when dialog is showing ([6806f554](https://github.com/angular/material/commit/6806f5546bd51929793b77d86675725670fa7bea)) +* **gridlist:** noops layoutDelegate ([2b6dd4dc](https://github.com/angular/material/commit/2b6dd4dced54e62721a1339143cf7b8996ba9e88), closes [#2613](https://github.com/angular/material/issues/2613)) +* **icons:** namespaces built-in icons ([539ec5e3](https://github.com/angular/material/commit/539ec5e36281aa8a6f645376bcd4512911165fb9)) +* **input:** + * minimize overlapping on iOS ([ea817874](https://github.com/angular/material/commit/ea817874b0b46fbae883e5191b234d1d4e38179c)) + * remove overflow-x causing drop on iOS ([f1df6dc0](https://github.com/angular/material/commit/f1df6dc09c20635d42ffaada32ae2f036666a5ba), closes [#2539](https://github.com/angular/material/issues/2539)) +* **list:** Do not ignore spaces in descendant textareas. ([f737fb04](https://github.com/angular/material/commit/f737fb04d4e93033365bb2a178c5ecbafaab42f0)) +* **select:** + * fix theming ([7e0a2aaa](https://github.com/angular/material/commit/7e0a2aaaf79e87da6b7536fcf4bf66f4213227e9)) + * update rendering on option changes ([4e855c7f](https://github.com/angular/material/commit/4e855c7fb6dda3a24811597fdb10d2f62048920e)) + * expose and preserve aria-label ([bd3d8fba](https://github.com/angular/material/commit/bd3d8fba5045bd4cfda5a46463ea15ecd23a30fc), closes [#1893](https://github.com/angular/material/issues/1893)) + * rendering in input group ([aa9058fe](https://github.com/angular/material/commit/aa9058fe737f69d3d36fbffaebd7fb1774e9ee39)) +* **slider:** adds box-sizing to fix slider ripple positioning ([e982547b](https://github.com/angular/material/commit/e982547b0e8a0502c0180099e5a7b3f4c3431ffb)) +* **styles:** fixes rem syntax for negative values ([2fef6cad](https://github.com/angular/material/commit/2fef6cada79dc6a3b42f1ab14f8a143f7044ae48)) +* **tabs:** + * fixes `md-center-tabs` css bug ([44e6984a](https://github.com/angular/material/commit/44e6984ad36544326cccbbf34aeeb08337b41e2c), closes [#2638](https://github.com/angular/material/issues/2638)) + * cleans up scope tree for tabs ([294e0664](https://github.com/angular/material/commit/294e06645c3a7b9323a95db10879844c0bbc9150), closes [#2600](https://github.com/angular/material/issues/2600)) +* **toolbar:** + * prevents transition lag on ng-if/ng-hide/ng-show ([544cb270](https://github.com/angular/material/commit/544cb270c65f86b44d9f882460ca784d43546b30), closes [#2663](https://github.com/angular/material/issues/2663)) + * adds shadow when content moves under 'scroll shrink' toolbar ([92ed4657](https://github.com/angular/material/commit/92ed46578001eeb54c009bfc9f302be783f79137)) + * sets icon color to primary contrast color within toolbars ([8ea0dc1d](https://github.com/angular/material/commit/8ea0dc1da8f75d8fdb1262e955156e8a62119a90), closes [#2622](https://github.com/angular/material/issues/2622)) + + + +### 0.9.0-rc3 (2015-04-28) + + +#### Features + +* **autocomplete:** allows tab or enter to select an item ([59015b09](https://github.com/angular/material/commit/59015b0991f06993b3eb70457716be97c7b6369d)) +* **button:** support angular-new-router ng-link ([4b9dcab5](https://github.com/angular/material/commit/4b9dcab5baf1e6794b8382ad997dc9047c58f3bd), closes [#2478](https://github.com/angular/material/issues/2478)) +* **tooltip:** Support hide-* show-* and user defined css using "display: none;" ([08132848](https://github.com/angular/material/commit/08132848b9702481a6dc46b0490ffa91646acdd2), closes [#2386](https://github.com/angular/material/issues/2386)) + + +#### Breaking Changes + +* content containers for `md-dialog` now require `md-dialog-content` to be more flexible with child content containers. This is more consistent with `md-card`, which uses `md-card-content`. ([9dc62403](https://github.com/angular/material/commit/9dc624033aac3714e97a8356b34508ffdcf02cf6)) +* md-input-containers no longer allow both label and placeholder to be used simultaneously. Now the placeholder value (if present) is transcluded as a label or ignored. When the placeholder value is ignored, a warning will be logged. ([d931c0d2](https://github.com/angular/material/commit/d931c0d237777c0197464277a5d8096bc3cbd698)) + + +#### Bug Fixes + +* **autocomplete:** + * adds support for the new `$animate.pin()` in `ng-animate@1.4.0-rc.1` ([790ccca6](https://github.com/angular/material/commit/790ccca695233e80f2720e3d4b2b67d2736b39fd)) + * adds watcher for ngDisabled rather than 2-way binding ([973a2fca](https://github.com/angular/material/commit/973a2fcadfc0fb786635cabf7c6674e5027117c4), closes [#2160](https://github.com/angular/material/issues/2160)) + * allow undefined `searchText` and `selectedItem` ([8fb60c34](https://github.com/angular/material/commit/8fb60c343a720a240ba8661f606ffe4479d05195), closes [#2515](https://github.com/angular/material/issues/2515)) + * fixes issue where autocomplete suggestions were not hiding ([da0a4f06](https://github.com/angular/material/commit/da0a4f06c54248c6d73eba7767cdf06bc0225f57)) + * fixes bug that prevented autocomplete results from displaying on focus if min-le ([5e72bb3d](https://github.com/angular/material/commit/5e72bb3db0b965fce0065cb12d56bcc6b122d8c5)) +* **bottomsheet:** fix button styles ([7bd97acc](https://github.com/angular/material/commit/7bd97acc60a895eea75978a95f0c43e8d6d3ebd2)) +* **build:** + * adds missing quotes for closure build ([041ffe94](https://github.com/angular/material/commit/041ffe943651de276c0558370e59f4a8ffde4863)) + * adds method that was missing from previous update ([77420942](https://github.com/angular/material/commit/774209429e1232fd975610123beef7e9f6dc5fd9)) +* **button:** + * icon inherits colors ([92aff331](https://github.com/angular/material/commit/92aff331bd9397cc5469a70ca502dfc239239652), closes [#2434](https://github.com/angular/material/issues/2434), [#2551](https://github.com/angular/material/issues/2551), [#2586](https://github.com/angular/material/issues/2586)) + * make buttons less opinionated ([482a916a](https://github.com/angular/material/commit/482a916ae72f01b6ed1383d36bcec120c02b256a), closes [#2580](https://github.com/angular/material/issues/2580), [#2438](https://github.com/angular/material/issues/2438)) + * fixes typo in button scss file ([b742dfda](https://github.com/angular/material/commit/b742dfda81500852dcffbc3a19a9ec40d5f314b4), closes [#2432](https://github.com/angular/material/issues/2432)) +* **card:** md-action-bar renamed to .md-actions ([5610dc98](https://github.com/angular/material/commit/5610dc98798c4148ca284e3f61208cb494da75dc), closes [#2472](https://github.com/angular/material/issues/2472)) +* **chips:** + * fix $apply already in progress ([daf680f0](https://github.com/angular/material/commit/daf680f0bc3a6e6460e5ae6894893970e684c3dc), closes [#2458](https://github.com/angular/material/issues/2458)) + * removes flicker when switching selected chip ([55fa76a0](https://github.com/angular/material/commit/55fa76a0a98e53ec91addaba5fdcba77cea17ef4)) +* **demo:** fix list demo secondary action. ([7e5d5e34](https://github.com/angular/material/commit/7e5d5e342fddc3d9f0ec1d5e1217b6eb8e6a6ec4), closes [#2471](https://github.com/angular/material/issues/2471)) +* **dialog:** + * use .md-primary color for actions ([4a648d55](https://github.com/angular/material/commit/4a648d55578a2d54e64bfc0b3f1dec5fa73bef57), closes [#2448](https://github.com/angular/material/issues/2448)) + * Rename md-content to md-dialog-content ([9dc62403](https://github.com/angular/material/commit/9dc624033aac3714e97a8356b34508ffdcf02cf6), closes [#2514](https://github.com/angular/material/issues/2514)) +* **docs:** fix doc app toolbar breadcrumb when using back button ([58f2c481](https://github.com/angular/material/commit/58f2c481175a0bd27a6ea8754f4ba79fda8445ec), closes [#2464](https://github.com/angular/material/issues/2464)) +* **gridlist:** Tile ordering improved ([5d9142ea](https://github.com/angular/material/commit/5d9142ea8d77e60350e8a7ddd02be6642218c0fa), closes [#2553](https://github.com/angular/material/issues/2553), [#NaN](https://github.com/angular/material/issues/NaN)) +* **highlight:** adds missing characters to sanitize method ([ef0dce07](https://github.com/angular/material/commit/ef0dce076716669d1d9a6ca50a6d341fad774632), closes [#2292](https://github.com/angular/material/issues/2292)) +* **icon:** rem sizes, line-height for font icons ([860d1f67](https://github.com/angular/material/commit/860d1f67db2be427aff149cbd0c13f0f3d39e4ca), closes [#2597](https://github.com/angular/material/issues/2597)) +* **input:** improve layout when md-input-container has nested md-icon. ([2dbe6a97](https://github.com/angular/material/commit/2dbe6a973b546cc9cdf79ca5e8262188a2570bfc), closes [#2452](https://github.com/angular/material/issues/2452)) +* **interimElement:** fix default parent grabbing svg body ([952d5f50](https://github.com/angular/material/commit/952d5f508a618fccd002ac28b211bc02e8f76c8d)) +* **list:** + * prevents error when there are no child elements ([f66e4718](https://github.com/angular/material/commit/f66e4718451b90cd27f393a2bbc46e3e6891a61e)) + * fixes line-height issues with list items and checkboxes/icons/switches ([4b045b58](https://github.com/angular/material/commit/4b045b5860cc5bebe6e7e17ca4c723a71a6abbfa)) + * fixes checkbox styles for list items ([6a0b7015](https://github.com/angular/material/commit/6a0b70159fc6fc6eebbb08defc6ee57d62124cd3)) +* **mdUtil:** + * move comment nodes as well when reenabling scroll ([6a5a6a77](https://github.com/angular/material/commit/6a5a6a77a3c8eaa88a9ca234ab5f10e5e8b82da4), closes [#2592](https://github.com/angular/material/issues/2592)) + * move comment nodes as well when disabling scroll ([160df4cf](https://github.com/angular/material/commit/160df4cf436ff22b487e4691dfb3b8034cc0da19), closes [#2456](https://github.com/angular/material/issues/2456)) +* **select:** + * add support for non-scrolling md-content ([88612d65](https://github.com/angular/material/commit/88612d6503ad3921a186ae3de4bb8422f6e76bce)) + * fix window resize listeners breaking after select shown ([7c74050f](https://github.com/angular/material/commit/7c74050fd83311880de5d11eae9ac199f56f54b2)) + * fix error when removing node on route change ([9d761878](https://github.com/angular/material/commit/9d7618781929f9983cb1550fe015b75182abbe26)) +* **tabs:** + * select/deselect methods will now be called in the proper scope ([02a4af56](https://github.com/angular/material/commit/02a4af565862d3da5caebbc059f388c942f11e1f), closes [#2489](https://github.com/angular/material/issues/2489)) + * removes top border radius when tabs immediately follows md-toolbar ([6372111c](https://github.com/angular/material/commit/6372111c8048d0f13dc6d47609e777b8c26940ee), closes [#2394](https://github.com/angular/material/issues/2394)) + * fixes styles for Firefox ([6996edd3](https://github.com/angular/material/commit/6996edd39378f366416c59dc8e781fecddad19b5), closes [#2543](https://github.com/angular/material/issues/2543)) +* **tests:** support for Angular 1.4 and improved use of angular-mocks. ([48ee9867](https://github.com/angular/material/commit/48ee9867cd44f7a387e78d4a323f5a9e44382550)) + + + +### 0.9.0-rc2 (2015-04-20) + +This RC2 release contains more polish and bug fixes to stabilize performance, layout, and improve the UX for Chips, List, Select, and Tabs. + +#### Features + +* **chips:** + * allows user to require a matched item - used with autocomplete ([736cbdb0](https://github.com/angular/material/commit/736cbdb096d4e064e1176e92eabddfe63288090c)) + * adds keyboard shortcuts to chips component ([c62d4dfd](https://github.com/angular/material/commit/c62d4dfd0f8741d90a2c69873154f55c91c3e7e7)) +* **list:** + * extend typography styles ([70fc6290](https://github.com/angular/material/commit/70fc62907442cb5e1349dcc9f3c3bafc601245c6), closes [#2366](https://github.com/angular/material/issues/2366)) + * add ripple to secondary to md-list actions ([927d8e56](https://github.com/angular/material/commit/927d8e5612c60afb7c18e4c697dc0e343f28e963)) +* **mdUtil:** add md-overflow-wrapper-shown class to DOM ([5a028092](https://github.com/angular/material/commit/5a028092c1236dc50ef428c4f3292b4641b831bd)) + + +#### Breaking Changes + +* List + * `md-item` has been renamed to `md-list-item` + * `md-item-content` element is now a `.md-list-item-text` class + * List styles of `.md-tile-left` changed to `.md-avatar` for avatar-like images + * `md-list-item` uses `.md-no-proxy` and `.md-no-style` +* Tabs + * When using `md-tab-label`, the content of your tab **must** be wrapped in `md-tab-body` +* Typography changes have significantly impacted the `

      `...`

      ` types. + + ([f1db7534](https://github.com/angular/material/commit/f1db7534ae73de3820f0f84b6b2b40f4011770e0)) + +#### Bug Fixes + +* **autocomplete:** fixes positioning logic to fire at the correct time ([8946322b](https://github.com/angular/material/commit/8946322b956ae6795e6ea0a700423469982bcdd5), closes [#2298](https://github.com/angular/material/issues/2298)) +* **button:** + * limit flat/raised button height ([9fc2c28b](https://github.com/angular/material/commit/9fc2c28b9084e6f5651a41540216045e176c93ef), closes [#2380](https://github.com/angular/material/issues/2380)) + * address flat hover state ([1e7b9823](https://github.com/angular/material/commit/1e7b98235c004ae384946d71a8aa9ac71e434883), closes [#2382](https://github.com/angular/material/issues/2382)) +* **card:** action bar spacing, remove order attrs ([50ce4e8b](https://github.com/angular/material/commit/50ce4e8b053b3387a81d1a4d533af06be9ef1e5c), closes [#2403](https://github.com/angular/material/issues/2403)) +* **checkbox:** style checkbox without visual label ([ab264807](https://github.com/angular/material/commit/ab26480780677fae4dd9b5a6238fa531b50bed6e), closes [#2175](https://github.com/angular/material/issues/2175)) +* **chips:** + * adds better focus handling for chips ([def6d3a4](https://github.com/angular/material/commit/def6d3a41441481f7a85503a5eb666332e055e27)) + * fixes issue where arrow keys switch active chip while editing input field ([1f261440](https://github.com/angular/material/commit/1f2614404a8d6cce45437f027662066f11ae35ef)) + * fixes issue with deletion logic ([6d4ecbee](https://github.com/angular/material/commit/6d4ecbee951ad87a73277517bc15c3c64264c149)) + * fixes shift+tab interaction with `md-chips` ([314aae1c](https://github.com/angular/material/commit/314aae1c345635f3ec1f67c842a38e19ee49b01e)) + * adds aria-hidden to remove buttons ([79b07399](https://github.com/angular/material/commit/79b073992a2e59f449d93321d74912d7cbe2eed6), closes [#2345](https://github.com/angular/material/issues/2345)) + * adds slightly better support for input types other than `text` ([d885d638](https://github.com/angular/material/commit/d885d638ac8f8d1c0660d30e6dfdca0fe780d0e8)) + * prevents item deletion when text is selected ([b65236b7](https://github.com/angular/material/commit/b65236b70ae3d6eb86d3d78631144fe65848d4ca), closes [#2322](https://github.com/angular/material/issues/2322)) +* **dialog:** action spacing matches spec ([d7b23763](https://github.com/angular/material/commit/d7b23763c7f17581b15c4b049faa8b306b9aed2a), closes [#2389](https://github.com/angular/material/issues/2389)) +* **input:** expect input container to use placeholder for label fallback ([8410c0d6](https://github.com/angular/material/commit/8410c0d6c46def09674a09a052e8503d85577085), closes [#2397](https://github.com/angular/material/issues/2397)) +* **inputContainer:** style ng-messages as both element and attr ([a5d09af5](https://github.com/angular/material/commit/a5d09af5493531f48a6c3a59036579cfa974c048)) +* **interimElement:** add fallback to document.body if root node removed ([3b3d0205](https://github.com/angular/material/commit/3b3d020581593eba3eea7632d68acdeb15dc56ad)) +* **list:** + * check child element length before detecting proxies ([c964609e](https://github.com/angular/material/commit/c964609e1f8842418d8494fe6bb8f276885aafa6)) + * add ripple, inherit from .md-button ([25cc5e8a](https://github.com/angular/material/commit/25cc5e8a9657f1d0731e6f11b4e44ad621bb3486), closes [#2395](https://github.com/angular/material/issues/2395)) + * fix key hijacking ([e1ca13fd](https://github.com/angular/material/commit/e1ca13fdea90867e3ecb841caf20860accfd764c)) +* **mdList:** remove focus state on blur ([588e58cf](https://github.com/angular/material/commit/588e58cf6de9be13f77c820a1582f2d1a2c36cb0), closes [#2339](https://github.com/angular/material/issues/2339)) +* **select:** + * fix scrollbars not respecting parent overflow hidden ([b9ee6121](https://github.com/angular/material/commit/b9ee6121412d59895b43286f4eefd4418e9081aa)) + * fix select backdrop closing when parent is destroyed ([2d66368c](https://github.com/angular/material/commit/2d66368cc31daf476bf786083a256d47a0e338ce)) + * fix infinite loop condition ([6e3b43cc](https://github.com/angular/material/commit/6e3b43cc91df2fa84a8c3983763e432b906a5740)) + * fix 1px error to match md-input-container style ([bd566a52](https://github.com/angular/material/commit/bd566a5217fedfb0731098795c6f45563c68716c)) + * add tabindex, aria-disabled support ([40924003](https://github.com/angular/material/commit/409240037aee989926179e4e5d3137dd9704b008), closes [#2308](https://github.com/angular/material/issues/2308)) + * fix restore focus on select close ([f55eeba0](https://github.com/angular/material/commit/f55eeba04150d0aecd2afe03c88009cf62293b9e)) + * remove broken ARIA attributes ([aaa6e5aa](https://github.com/angular/material/commit/aaa6e5aaef73d8667339521aed08f42abee53b01)) + * improve keyboard filtering ([f3c113c9](https://github.com/angular/material/commit/f3c113c9083dfd079c06e26bbcc8da5c52ba8586)) + * improve disable scroll layout ([68395a25](https://github.com/angular/material/commit/68395a254e70a8e57ccd2e03019228bf158435a4)) +* **tabs:** + * tabs will no longer assume that all tabs have no content if the first one doesn't ([1b789fed](https://github.com/angular/material/commit/1b789fed4cf55443e2d106537e6912fa1df605e2), closes [#2398](https://github.com/angular/material/issues/2398)) + * adds a queue for delayed function calls ([0e2ccd23](https://github.com/angular/material/commit/0e2ccd23dc220392c0082f9cb05ccfddf42d22a1), closes [#2402](https://github.com/angular/material/issues/2402)) + * resolves issue where `md-tabs` is used within `ui-view` by removing hard require ([43508032](https://github.com/angular/material/commit/4350803267a8e19a15a240ccf657cd2ba098e0ae), closes [#2414](https://github.com/angular/material/issues/2414)) + * apply ARIA only to dummy tabs ([5ad44728](https://github.com/angular/material/commit/5ad447284c7cda2261da1440819e3e0101a7b1ae)) + * cleans up label/template fetch logic ([17aecd2d](https://github.com/angular/material/commit/17aecd2dbc3acfe48006ff5adb12cad5e15aa70b)) + * id's should no longer cause issues when used within tab labels or contents ([ea185eab](https://github.com/angular/material/commit/ea185eabd13bbcce3e07628891836b0626f609e5), closes [#2326](https://github.com/angular/material/issues/2326)) +* **tests:** mock .flush() added for Angular 1.4.x ([1e3f1a59](https://github.com/angular/material/commit/1e3f1a591f8f2f7c6bd54153df080367970b8a2d)) +* **textfield:** removed unstable, deprecated textfield component Replaced with md-input-container ([b5c4390b](https://github.com/angular/material/commit/b5c4390bf7cb81809fea0df93c7d01e410b5585a)) + + + + +### 0.9.0-rc1 (2015-04-14) + +The release of 0.9 is primarily focused on bug fixes and polish efforts. Also included in this upcoming release will be added support for List items and controls, Chips, re-architected Tabs, and improvements to Gesture support and documentation. + +#### Features + +* **autocomplete:** + * adds `md-menu-class` to allow users to add a class to the dropdown menu for cust ([860897d9](https://github.com/angular/material/commit/860897d9c6f2db034ecb2e30dddd005bc174e00d)) + * dropdown will now stretch to accomodate longer text ([b1343704](https://github.com/angular/material/commit/b13437044147cecf6835850c95fc764a67d466d0)) + * adds support for `md-autocomplete-snap` to specify parent element for autocomple ([15d1db73](https://github.com/angular/material/commit/15d1db731eab44151543c2ec27323e0bc92f0878)) + * `md-highlight-text` now supports custom flags ([1ac0c93c](https://github.com/angular/material/commit/1ac0c93c369b1e6652741abd640e0539090bb083), closes [#1799](https://github.com/angular/material/issues/1799)) + * adds support for `name` attribute ([ebca76da](https://github.com/angular/material/commit/ebca76da4831c359547cce59d6378c9f0a93e913)) + * adds support for floating labels ([f487248a](https://github.com/angular/material/commit/f487248a70c9bfd749953e98e3412240112f39a3)) +* **button:** support for link icon buttons ([1b9bafb5](https://github.com/angular/material/commit/1b9bafb5abd470e4e1bfd784484407588430083b), closes [#2265](https://github.com/angular/material/issues/2265)) +* **card:** add default background color ([3bc8b27b](https://github.com/angular/material/commit/3bc8b27be39d33a07a58c562e9efafa184fdd12d), closes [#1846](https://github.com/angular/material/issues/1846)) +* **chips:** initial commit Supports basic list mutation (add, remove items). ([713f7b67](https://github.com/angular/material/commit/713f7b67cd195d5abfde1540f26679d58f738bb5), closes [#2030](https://github.com/angular/material/issues/2030)) +* **gestures:** add provider to all config and optional skipClickHijack() ([f28393df](https://github.com/angular/material/commit/f28393df73a4ef85c18f54bd99041a74382375fb)) +* **layout:** re-add offset attribute ([fecceeb5](https://github.com/angular/material/commit/fecceeb5158bbd78e848a449dd3840ccef76e1f6)) +* **list:** BREAKING: add list controls ([abb807d0](https://github.com/angular/material/commit/abb807d0dcb77b92132b5ab73d61f97aa953f461), closes [#2021](https://github.com/angular/material/issues/2021)) +* **mdButton:** add icon button for toolbars, etc. ([6ee9a190](https://github.com/angular/material/commit/6ee9a1901beaeed49055437f64506b42b3f33bc3), closes [#2091](https://github.com/angular/material/issues/2091), [#NaN](https://github.com/angular/material/issues/NaN)) +* **mdCard:** support md-card-footer ([ff794eca](https://github.com/angular/material/commit/ff794ecad8299f343d79501b470a914a75cdb6bc), closes [#1474](https://github.com/angular/material/issues/1474)) +* **radioButton:** allow theming on radio group ([30d0cc44](https://github.com/angular/material/commit/30d0cc4425c68571ec67bcca0ec4ba0925b18ed7), closes [#1746](https://github.com/angular/material/issues/1746)) +* **script:** Set ngModule to Closure namespace. ([ade76f95](https://github.com/angular/material/commit/ade76f95f38410a92ec07c4b02bff0c1df580efb), closes [#1709](https://github.com/angular/material/issues/1709)) +* **select:** + * truncate string to fit in single line ([1a81f0c6](https://github.com/angular/material/commit/1a81f0c6c98a2fe217f089c3457af5ee99ba61f5)) + * add auto-inference of option value ([342af6a8](https://github.com/angular/material/commit/342af6a808c962ce02da26461b403aa4465f05cc)) + * add psuedo-element to make auto complete work ([aa440efc](https://github.com/angular/material/commit/aa440efcb381a61abb49ea846d1fe0b458cc3a50)) + * add support to select/focus by typing options ([f5d905a2](https://github.com/angular/material/commit/f5d905a203808c067195330675d6bdbad6f2be0c)) + * add ngMultiple support to select ([9956f62d](https://github.com/angular/material/commit/9956f62d8652a4d335bfe2d759b12634ef8691c8)) + * add styles for invalid ([ddf1198f](https://github.com/angular/material/commit/ddf1198fe7c7a5f2c344dced368da4b5f4e13d20)) +* **sidenav:** + * add focus override option and demo ([3ca2a637](https://github.com/angular/material/commit/3ca2a637f730b49dfdb89489cfc368372fb73c4e), closes [#1891](https://github.com/angular/material/issues/1891)) + * add promise-based lookup .then(callbackFn) for deferred instances ([076a97db](https://github.com/angular/material/commit/076a97db299a42c35f315b032729da5500a24157), closes [#1311](https://github.com/angular/material/issues/1311)) +* **tabs:** + * adds support for `md-no-pagination` ([d0ddad1f](https://github.com/angular/material/commit/d0ddad1f467b9f001233e1dff708f3d7785f9bfc), closes [#1189](https://github.com/angular/material/issues/1189)) + * adds support for `md-center-tabs` ([2de0cf26](https://github.com/angular/material/commit/2de0cf26a4e7fc985b6247039537311a7e7861c1), closes [#1992](https://github.com/angular/material/issues/1992)) + * adds support for `label` attribute without tab content ([faab8883](https://github.com/angular/material/commit/faab88837c2f38b87adaa3bfe5de77f563f7899f), closes [#2068](https://github.com/angular/material/issues/2068)) + * adds support for dynamic height based on tab contents ([efde63c4](https://github.com/angular/material/commit/efde63c4673ca60085013f9d4caa6121c8110c30), closes [#2088](https://github.com/angular/material/issues/2088)) + * refactors tabs to function closer to spec ([f34eb800](https://github.com/angular/material/commit/f34eb800212b6daa11d3a23c14a5a173d9691cf3), closes [#1087](https://github.com/angular/material/issues/1087), [#1107](https://github.com/angular/material/issues/1107), [#1140](https://github.com/angular/material/issues/1140), [#1247](https://github.com/angular/material/issues/1247), [#1261](https://github.com/angular/material/issues/1261), [#1380](https://github.com/angular/material/issues/1380), [#1387](https://github.com/angular/material/issues/1387), [#1403](https://github.com/angular/material/issues/1403), [#1443](https://github.com/angular/material/issues/1443), [#1505](https://github.com/angular/material/issues/1505), [#1506](https://github.com/angular/material/issues/1506), [#1516](https://github.com/angular/material/issues/1516), [#1518](https://github.com/angular/material/issues/1518), [#1564](https://github.com/angular/material/issues/1564), [#1570](https://github.com/angular/material/issues/1570), [#1620](https://github.com/angular/material/issues/1620), [#1626](https://github.com/angular/material/issues/1626), [#1698](https://github.com/angular/material/issues/1698), [#1777](https://github.com/angular/material/issues/1777), [#1788](https://github.com/angular/material/issues/1788), [#1850](https://github.com/angular/material/issues/1850), [#1959](https://github.com/angular/material/issues/1959), [#1986](https://github.com/angular/material/issues/1986), [#2020](https://github.com/angular/material/issues/2020), [#1944](https://github.com/angular/material/issues/1944)) +* **tooltip:** adds `md-autohide` functionality ([b682e36a](https://github.com/angular/material/commit/b682e36a55c37f41cf9004645916cba07b6ef805), closes [#1689](https://github.com/angular/material/issues/1689)) + + +#### Breaking Changes + +Generated HTML and style improvements: + +###### CSS +* CSS Focus outlines not prevented by default +* Box-sizing removed from body, applied to components +* `textRendering: optimizeLegibility` removed from body, applied to a subset of elements +* Font sizes, margins, padding in `rem` units +* Roboto webfont included in SCSS +* Typography: classes introduced for headings and body copy +* High-contrast mode supported on Windows + +###### HTML +* Docs: Pinch and zoom re-enabled in viewport + +Bugs fixed: + +* [#1087](https://github.com/angular/material/issues/1087), [#1107](https://github.com/angular/material/issues/1107), [#1140](https://github.com/angular/material/issues/1140), [#1247](https://github.com/angular/material/issues/1247), [#1261](https://github.com/angular/material/issues/1261) +* [#1380](https://github.com/angular/material/issues/1380), [#1387](https://github.com/angular/material/issues/1387), [#1403](https://github.com/angular/material/issues/1403), [#1443](https://github.com/angular/material/issues/1443), [#1505](https://github.com/angular/material/issues/1505) +* [#1506](https://github.com/angular/material/issues/1506), [#1516](https://github.com/angular/material/issues/1516), [#1518](https://github.com/angular/material/issues/1518), [#1564](https://github.com/angular/material/issues/1564), [#1570](https://github.com/angular/material/issues/1570) +* [#1620](https://github.com/angular/material/issues/1620), [#1626](https://github.com/angular/material/issues/1626), [#1698](https://github.com/angular/material/issues/1698), [#1777](https://github.com/angular/material/issues/1777), [#1788](https://github.com/angular/material/issues/1788) +* [#1850](https://github.com/angular/material/issues/1850), [#1959](https://github.com/angular/material/issues/1959), [#1986](https://github.com/angular/material/issues/1986), [#2020](https://github.com/angular/material/issues/2020) + + + ([f34eb800](https://github.com/angular/material/commit/f34eb800212b6daa11d3a23c14a5a173d9691cf3)) + +#### Bug Fixes + +* include Roboto as non-blocking ([5936f7a0](https://github.com/angular/material/commit/5936f7a080faef5d6bfb53463815a0a6d989f739), closes [#2247](https://github.com/angular/material/issues/2247), [#NaN](https://github.com/angular/material/issues/NaN)) +* switch focus on keyboard, use .md-focused ([0e916bfc](https://github.com/angular/material/commit/0e916bfccbd2abd05508c6bde61eb513530c3331), closes [#1336](https://github.com/angular/material/issues/1336)) +* limit global list styles ([fde08cc1](https://github.com/angular/material/commit/fde08cc174a1516fcba97cf626861751d80cebfc)) +* refactor global CSS styles ([6af1546d](https://github.com/angular/material/commit/6af1546da48aa335ca65ff32f09e2d3b69d0a2d9), closes [#1442](https://github.com/angular/material/issues/1442), [#NaN](https://github.com/angular/material/issues/NaN)) +* **autocomplete:** + * fixes menu flicker ([9b2dc2c4](https://github.com/angular/material/commit/9b2dc2c472e3a8893114d8153a0734c80fe7fa6a)) + * updates the z-index to account for use within sidenav or dialog ([3cc914d7](https://github.com/angular/material/commit/3cc914d7f31a230c500fb460e7bb0ee7fe9003b1), closes [#2202](https://github.com/angular/material/issues/2202)) + * hitting enter with an item selected no longer resets the selected item ([7e666ab4](https://github.com/angular/material/commit/7e666ab4df3e866acae45bc50030042ad31a0b75), closes [#2183](https://github.com/angular/material/issues/2183)) + * dropdown will shift if there is not enough space ([0b15c976](https://github.com/angular/material/commit/0b15c97629be75924653738687ffeff827fcdc22), closes [#2004](https://github.com/angular/material/issues/2004)) + * moves autocomplete dropdown to nearest content container ([7f355f6d](https://github.com/angular/material/commit/7f355f6d787520cc6a047f46067971b25d79ee73), closes [#2013](https://github.com/angular/material/issues/2013), [#1961](https://github.com/angular/material/issues/1961), [#1670](https://github.com/angular/material/issues/1670)) + * `md-min-length` now supports 0 as a value and will show dropdown on focus ([dcf92682](https://github.com/angular/material/commit/dcf92682ef199cc400af013a426fd2e952cd182e)) + * `md-search-text-change` now fires when content is deleted from text ([f308779a](https://github.com/angular/material/commit/f308779a3553af6b46419aaeaa5e3bad9b27e832), closes [#2061](https://github.com/angular/material/issues/2061)) + * item templates now handle child bindings in the correct scope ([2e157d2b](https://github.com/angular/material/commit/2e157d2bf89336ee404cfaea15e16cc5aeb6c8cf), closes [#2065](https://github.com/angular/material/issues/2065)) + * adds a minimum width ([66fe5757](https://github.com/angular/material/commit/66fe5757fa946414a16b4a3058ecf22701315f2c), closes [#1853](https://github.com/angular/material/issues/1853)) + * hides cancel button when field is disabled ([936b8816](https://github.com/angular/material/commit/936b88166fb4cc6d90ebdab78127926fc7cc89e6), closes [#1710](https://github.com/angular/material/issues/1710)) + * adds support for `md-autofocus` ([c6374d9b](https://github.com/angular/material/commit/c6374d9bb17beca805d36c905eb3c8917a2084e8), closes [#1764](https://github.com/angular/material/issues/1764)) + * accepts pre-selected values ([c3fcd0d8](https://github.com/angular/material/commit/c3fcd0d84e375ba8d77d6bf532904769eb433601), closes [#1779](https://github.com/angular/material/issues/1779)) + * will no longer query when item is selected ([5f141269](https://github.com/angular/material/commit/5f14126954fa10911e00a8c18bf8a070133ae5de), closes [#1835](https://github.com/angular/material/issues/1835), [#1732](https://github.com/angular/material/issues/1732)) + * disables browser autocomplete ([1a8b5658](https://github.com/angular/material/commit/1a8b5658a7e6cc37d307c725f9bb24d8e5dbd2e7), closes [#1849](https://github.com/angular/material/issues/1849)) + * autocomplete now selects the first item by default and no longer hides results w ([4c2b086a](https://github.com/angular/material/commit/4c2b086a6bd38f1d6d4096b73a96d7523b0f09b9), closes [#1858](https://github.com/angular/material/issues/1858)) + * adds support for `$http` promises ([de423ae4](https://github.com/angular/material/commit/de423ae48d593c08f0277376b7e3e80571a3ba48), closes [#1798](https://github.com/angular/material/issues/1798)) +* **bottomSheet:** make bottom sheet work with new md-list ([bc32eb4c](https://github.com/angular/material/commit/bc32eb4c3bd795ad9be0ba6412bb7bfcde6d681b)) +* **bower:** fixes bower warning ([c4979d68](https://github.com/angular/material/commit/c4979d680aaca4136dd0e9408ac76c3cb8351529), closes [#2165](https://github.com/angular/material/issues/2165)) +* **build:** + * do not bower install in github hook script ([859ecb56](https://github.com/angular/material/commit/859ecb568a7318b1f334f6e210680c4d03e4d110)) + * remove bower dependency, update npm to install angular, update Jasmine 2 ([9398a7a9](https://github.com/angular/material/commit/9398a7a9d0c97eff5e35d24348d89bec85bebd34), closes [#1962](https://github.com/angular/material/issues/1962)) +* **button:** + * update to use .md-focused ([7d1608e6](https://github.com/angular/material/commit/7d1608e6b776cc695a5426b35fd0e0abff8f0970)) + * safari background FAB colors ([0178b895](https://github.com/angular/material/commit/0178b8955f6bf2120a3a32fa8c51398557c9c059), closes [#2011](https://github.com/angular/material/issues/2011)) + * hover, disabled, alignment, docs ([a936e1ed](https://github.com/angular/material/commit/a936e1edc964b69ffe5bf96905e348fc6f4c6b4d), closes [#1607](https://github.com/angular/material/issues/1607), [#NaN](https://github.com/angular/material/issues/NaN)) + * prevents transition from firing on ng-leave ([9aedd741](https://github.com/angular/material/commit/9aedd7413835b9333154e3b3f082ee94c4f89d49), closes [#936](https://github.com/angular/material/issues/936)) + * Support ui-sref attribute natively. ([7b743ed4](https://github.com/angular/material/commit/7b743ed4c62e2680c350d56daf57b202f15e63bf)) + * fixes ripple on fab buttons in safari ([a1c011ef](https://github.com/angular/material/commit/a1c011ef729c091d82f070b87b84f98fd685690c), closes [#1856](https://github.com/angular/material/issues/1856)) +* **card:** add correct themed background color ([9af45d37](https://github.com/angular/material/commit/9af45d37d635b31ae9fa1117db3edfd2e07f15ab)) +* **checkbox:** + * update to grid units, demo cleanup ([bc1c4e0f](https://github.com/angular/material/commit/bc1c4e0f290739088b7da7d51744c603ea107079)) + * disable checkboxes with tabindex=-1 ([3c0fed99](https://github.com/angular/material/commit/3c0fed997bacc58f9bad49aa6a4f96f06db08402), closes [#2087](https://github.com/angular/material/issues/2087)) + * support for ng-checked ([2680cf15](https://github.com/angular/material/commit/2680cf1565afb4a8858737f0201933a400b9059e), closes [#1550](https://github.com/angular/material/issues/1550)) + * ngModel intial value used correctly with events ([942d0b9a](https://github.com/angular/material/commit/942d0b9a87b1478917325ccac011c2f4f5a5b3e1), closes [#1550](https://github.com/angular/material/issues/1550)) + * make enter toggle checkboxes ([57610eae](https://github.com/angular/material/commit/57610eaea680871b2751f078a7132e983c6f24e8)) +* **chips:** + * sets `md-autoselect` for contact chips ([eb9f5646](https://github.com/angular/material/commit/eb9f56466a02e44321c8fa50c09f5689836a5be3), closes [#2294](https://github.com/angular/material/issues/2294)) + * fixes issue when removing chips via button ([59d359ce](https://github.com/angular/material/commit/59d359ce1d2ca9629876bf3c07b4801d1c253d1b), closes [#74](https://github.com/angular/material/issues/74)) + * when adding items, duplicates will now be skipped ([1ba926bc](https://github.com/angular/material/commit/1ba926bc347f93ce9ff2bff77bd492a39f393fa6)) + * only add chip item if it is truthy. fixes #2116 ([d154a8e4](https://github.com/angular/material/commit/d154a8e4b465a94e6dda18333e797216e8a9a558), closes [#2120](https://github.com/angular/material/issues/2120)) +* **dialog:** + * fix backdrop position with parent scrolled ([74601d03](https://github.com/angular/material/commit/74601d0341609d072c5350b63da515b9af54a095)) + * cross-browser layout, a11y issues ([0b0ed233](https://github.com/angular/material/commit/0b0ed2339e1ec32e34d20daa18cadd4ef89d5f86), closes [#1340](https://github.com/angular/material/issues/1340), [#NaN](https://github.com/angular/material/issues/NaN)) +* **divider:** make divider work with new md-list ([ebcd7f04](https://github.com/angular/material/commit/ebcd7f043457c189f731bf3d40a8d6004e6e4af1)) +* **docs:** use node_modules/angularytics ([b8cc062a](https://github.com/angular/material/commit/b8cc062a36d0e957e626e630dd4a8734ba29785f)) +* **gesture:** don't stop hijacking clicks on ios & android when jquery is loaded ([81bcf7fd](https://github.com/angular/material/commit/81bcf7fd15fd76f054814b34d8a877168fc6225c), closes [#1869](https://github.com/angular/material/issues/1869), [#1842](https://github.com/angular/material/issues/1842)) +* **gestures:** + * attach event listeners once when multiple ng-apps are running ([6d566762](https://github.com/angular/material/commit/6d566762f22049fbfe10dd9187e04f305a7f9ae7), closes [#2173](https://github.com/angular/material/issues/2173)) + * disable click hijack on mobile if jQuery is present ([2c57a828](https://github.com/angular/material/commit/2c57a8289f73265ca41fc8b2fbbe173a80073caa), closes [#1915](https://github.com/angular/material/issues/1915)) + * gestures initialize only on use instead of load ([63c87096](https://github.com/angular/material/commit/63c87096f2e1d18c38fe0895434b087bfd517a3b), closes [#2074](https://github.com/angular/material/issues/2074)) +* **gridlist:** + * The gridlist will now lay out everytime a tile is added ([aca5944d](https://github.com/angular/material/commit/aca5944d9d12f92b75e4bcf3de8865475b3105e8), closes [#2227](https://github.com/angular/material/issues/2227)) + * Now supports custom interpolation symbols ([f037b8cb](https://github.com/angular/material/commit/f037b8cb9e4db8b0ba0d1830fe872f9537e91779), closes [#1874](https://github.com/angular/material/issues/1874), [#NaN](https://github.com/angular/material/issues/NaN)) + * update calcs for safari ([33049cff](https://github.com/angular/material/commit/33049cff96cd643e76159fc2d72cbd9fb911245c), closes [#2066](https://github.com/angular/material/issues/2066)) + * typo in gridlist demo ([8849fe19](https://github.com/angular/material/commit/8849fe19595dbaf2515ad590fe2f9029c70b54e4)) +* **icon:** + * remove invalid danger style ([360e2b60](https://github.com/angular/material/commit/360e2b6097f97189e41b9f1da2c95ba4043193e7)) + * Fix issue with resizing SVGs when jQuery included. ([c4e8c1c4](https://github.com/angular/material/commit/c4e8c1c429e1333b8b8ecee0033fe4c695c60c3d), closes [#1803](https://github.com/angular/material/issues/1803)) +* **icons:** + * disable pointer events on SVG(s). ([a7d9fa32](https://github.com/angular/material/commit/a7d9fa32209378b92ebddcbd2c11a85cea612a3b), closes [#2048](https://github.com/angular/material/issues/2048)) + * fix default icon color to match spec ([6fc26fab](https://github.com/angular/material/commit/6fc26fabfc97ea1d677eefec912ed1b5a8bbaca0)) +* **input:** + * float labels support dir=rtl ([b88b744f](https://github.com/angular/material/commit/b88b744f325c79db728df83652db08865ac29b7d), closes [#964](https://github.com/angular/material/issues/964)) + * Only make invalid input label red when it's floating ([6d368505](https://github.com/angular/material/commit/6d3685059f0e5c47e1051e2c8accc53bf62a5b3d), closes [#1928](https://github.com/angular/material/issues/1928)) + * delegate clicks in IE10 ([d0b427d6](https://github.com/angular/material/commit/d0b427d63e5fac31d0cec18c5df8158e2c083c9a), closes [#1394](https://github.com/angular/material/issues/1394)) + * improve textarea appearance on firefox and IE ([a693dabd](https://github.com/angular/material/commit/a693dabdafa72a07e6422b94e5f927328cee94ef), closes [#1157](https://github.com/angular/material/issues/1157)) + * input validation and visual error indicators fixed ([c818da75](https://github.com/angular/material/commit/c818da75c89043a81de6234120315d7d48abc782), closes [#1606](https://github.com/angular/material/issues/1606)) +* **list:** + * add missing interpolation character ([07e82015](https://github.com/angular/material/commit/07e82015bbe4d5b40eb36a1077c509a6f7597b33)) + * focus covers whole row, IE11 layout fix ([b47a8787](https://github.com/angular/material/commit/b47a8787fba6d9f7bda6f695ee0c8a9904df65c1), closes [#2222](https://github.com/angular/material/issues/2222)) + * adds role attributes in compile rather than link for performance improvements ([38f04230](https://github.com/angular/material/commit/38f04230a3064f84701a1c7eca862fd6bb2c256f)) +* **md-select:** Track disabled state ([dc93bffe](https://github.com/angular/material/commit/dc93bffe25cd5b2c28e6eac7adfe954c69c672c6)) +* **mdButton:** focus styles only on keyboard ([dfb4d1ab](https://github.com/angular/material/commit/dfb4d1abd9ed82cb2b68980ae580f3a434ff3f1c), closes [#1423](https://github.com/angular/material/issues/1423)) +* **progressCircular:** fixes animation in IE11 ([d5b77bdc](https://github.com/angular/material/commit/d5b77bdcfe21cb898ea21461e180da2daad89886), closes [#387](https://github.com/angular/material/issues/387)) +* **radioButton:** fix googly eyed radio-buttons ([bffa15ab](https://github.com/angular/material/commit/bffa15abc37698a9e8b663db1a9ce4a8250b27a4)) +* **ripple:** + * fixes ripple positioning ([087c3dcf](https://github.com/angular/material/commit/087c3dcf2443ee49bf86a2c87fcaab014e47751f)) + * fixes issues with ripples on items below the fold ([e0f9fe98](https://github.com/angular/material/commit/e0f9fe98b24e028f80acf662b8a69e2f582529f2), closes [#2028](https://github.com/angular/material/issues/2028), [#1526](https://github.com/angular/material/issues/1526)) +* **select:** + * greatly improve scroll disable compatability ([614630d7](https://github.com/angular/material/commit/614630d730f24df9ad3e9ee91f061049d615738a)) + * fix invalid css ([2f9eef70](https://github.com/angular/material/commit/2f9eef70b5503b4fcb5c7cbe09712700403cc5a3)) + * make select focus states match ui-spec ([42db19dd](https://github.com/angular/material/commit/42db19dd5571eb88ba8d2a9667d183d9f3d61da7)) + * bug using select attr with inferred value ([e20b1906](https://github.com/angular/material/commit/e20b1906c4c732b5b9baf708eaebd25de93dc9cb)) + * fix scrollbar margin of select parent ([f3cd5b9c](https://github.com/angular/material/commit/f3cd5b9c21e75794b689f8f4ba33d5dfec3d2fd9)) + * make select positioning factor in scroll bar width ([e18450fb](https://github.com/angular/material/commit/e18450fb53d4566e19b847e1606a2c9d00fe18cf)) + * reposition select on resize events ([0fe35cc8](https://github.com/angular/material/commit/0fe35cc8a896ac8db734e8f06766774cbe337211)) + * fix md-on-show not hiding spinner for non-async ([13b9c697](https://github.com/angular/material/commit/13b9c6978426dd82bfc669ca653e84d35373fb67)) + * make constrained select fit to top/left first ([f6f2187c](https://github.com/angular/material/commit/f6f2187cc92055cafbb440ff021093bfccaa72c3)) + * BREAKING: make select more inline with md-input-container styles ([a67a6a2e](https://github.com/angular/material/commit/a67a6a2e7f7d6ff88ae48e405828b542481d2468)) + * fix alignment of select dropdown arrow ([5c853e66](https://github.com/angular/material/commit/5c853e6654d1e2279833edccdbc142a577e3a338)) + * fix firefox select positioning, page moving on open ([a15347cd](https://github.com/angular/material/commit/a15347cd24255492ed5a36e9bbd2920e45c629e1)) +* **sidenav:** improve API usage for $animate enter/leave ([4245bcf7](https://github.com/angular/material/commit/4245bcf7f1000ab8960dda27d47cb34a8ddb7583), closes [#2261](https://github.com/angular/material/issues/2261)) +* **subheader:** fix width styling while scrolling ([2f335732](https://github.com/angular/material/commit/2f33573258466be877c3399b1033ad71dc612283)) +* **tabs:** + * resets offset if the user resizes causing pagination to no longer be necessary ([bd1c973a](https://github.com/angular/material/commit/bd1c973a1d13490dfc8ad38aa8cd7ffc633aa6fc)) + * fixes styles for `md-align-tabs="bottom"` ([0dabfc5c](https://github.com/angular/material/commit/0dabfc5cd1bccd51c221e8af8ea4ddda35f21df1)) + * fixes Safari issue regarding dynamic tabs transitions ([4ac7dc03](https://github.com/angular/material/commit/4ac7dc037aecce6721d6656e9a2fb773b852ae15)) + * fixes flicker issue with dynamic height ([48eeb627](https://github.com/angular/material/commit/48eeb627ddf497a241ec5e4686f333d5b56769fe)) + * fixes styles for `md-border-bottom` and `md-align-tabs` ([9affd121](https://github.com/angular/material/commit/9affd1216174d31e92be54e0d0c1ec1698ed483f), closes [#2119](https://github.com/angular/material/issues/2119)) + * adds support for height changes in between tab switches ([64c4585b](https://github.com/angular/material/commit/64c4585b0bc3bcb16a2f196f26ceaa8cb420b679), closes [#2088](https://github.com/angular/material/issues/2088), [#2177](https://github.com/angular/material/issues/2177)) + * resolves issue where code was attempting to fire after the tabs array had been r ([f15fd050](https://github.com/angular/material/commit/f15fd0502463ffcd436d4768771ea4ccdc378fcf)) + * re-added `md-on-select` and `md-on-deselect` events to `md-tab` directive ([c84899df](https://github.com/angular/material/commit/c84899df1036804db1b435ca4c6f92e01c7e12e6)) + * applies correct styles for tabs living inside md-toolbar ([4c59c5c5](https://github.com/angular/material/commit/4c59c5c58906e3b7cb6da36f25426767b3949c4e), closes [#2067](https://github.com/angular/material/issues/2067)) + * updates CSS so that tabs support scrolling when necessary ([9f0d428c](https://github.com/angular/material/commit/9f0d428c9de64ddcb08bbe269044b88d0dae0d23), closes [#2088](https://github.com/angular/material/issues/2088)) +* **theming:** + * fix theming in safari ([1ebc42ec](https://github.com/angular/material/commit/1ebc42ec4ad457602a634f8fb0562128dc3e701c)) + * add support for background hues ([0df4b16e](https://github.com/angular/material/commit/0df4b16eca2c8af9805615da646d38db04a33d7f)) + * change default background palette, document theming ([57deba10](https://github.com/angular/material/commit/57deba108b5075fff2fb869d252b923569091398)) +* **toast:** style tweaks for desktop rendering, fix opacity ([860a55ec](https://github.com/angular/material/commit/860a55ec7d5aaf337f671645d226da566b23b596)) +* **toolbar:** md-button style cleanup ([1c19272a](https://github.com/angular/material/commit/1c19272a70e7690a5689bbd5f33a338c078a795c), closes [#2267](https://github.com/angular/material/issues/2267), [#2146](https://github.com/angular/material/issues/2146)) +* **tooltip:** + * fixes positioning bug in Safari ([f62fd480](https://github.com/angular/material/commit/f62fd480fdb5aa4f044f69691aedee31932af638), closes [#1883](https://github.com/angular/material/issues/1883)) + * tooltip will no longer be pushed up if there is room for it within its `offsetPa ([6e576c02](https://github.com/angular/material/commit/6e576c02dfebab5474c43d6069247ed32942e3e3)) + * fixes tooltip position when body has a margin ([00f4cc6d](https://github.com/angular/material/commit/00f4cc6d1329f618d595f887c0a13371bcff2d9e), closes [#1883](https://github.com/angular/material/issues/1883)) + * + + +### 0.8.3 (2015-03-03) + + +#### Features + +* **ripple:** smooths out ripple animations ([ac47ca6c](https://github.com/angular/material/commit/ac47ca6c1df6a873615445d8c1f0cbb3eb3764f7), closes [#1750](https://github.com/angular/material/issues/1750)) + + +#### Bug Fixes + +* **autocomplete:** clicking on the scrollbar in the dropdown will no longer cause the list to hide ([2731f107](https://github.com/angular/material/commit/2731f1078efdd3c546629376ed71c7068501daf3), closes [#1739](https://github.com/angular/material/issues/1739)) +* **gestures:** resolves jQuery conflict with $mdGesture ([79505888](https://github.com/angular/material/commit/7950588882557a6d1670e2029ddda76d12027c45)) +* **select:** + * support rtl direction ([59e30a39](https://github.com/angular/material/commit/59e30a392ed7f0dc43ec71db22b60fcaa800ba7e)) + * make md-option work with ng-selected ([425a76a3](https://github.com/angular/material/commit/425a76a3a5b4bce767e0f8ad766d7a8773e5463f)) + * make select work with ng-disabled ([60514054](https://github.com/angular/material/commit/60514054984c3d1c5577c5150e80e7ca9e3a4083)) + * fix select not setting initial value ([6dc46d52](https://github.com/angular/material/commit/6dc46d52dea21c4de3dc7984fb56d99076fdd1e6)) + * fix closing select a second time on firefox ([e49a20e8](https://github.com/angular/material/commit/e49a20e85d808ca0533afc40ee11f8337535c315)) + * fix positioning / sizing on firefox ([67618dc8](https://github.com/angular/material/commit/67618dc8fb5a81b23ed33879b062a6e316477a4a)) + * fix resetting value for selects without placeholder ([2a0ea163](https://github.com/angular/material/commit/2a0ea1630fb286099a8c27e5035cadc5cc1ed0b8)) + * fix empty select breaking page ([6e7b36cf](https://github.com/angular/material/commit/6e7b36cfa388227e3879120d3c332fc76e095e32)) + * make select consistent with md-input ([6aa1c8a7](https://github.com/angular/material/commit/6aa1c8a782b67aee3a31f9ed38c99aebfdb3b1a9)) + * positioning when to close to bottom or right ([cf78ba9f](https://github.com/angular/material/commit/cf78ba9f1fce7fdc3e613b3c83a4056ed8fb2e40)) + + + +### 0.8.2 (2015-02-27) + + +#### Features + +* **autocomplete:** adds support for `md-delay` to wait until the user stops typing to poll for resu ([70a884a1](https://github.com/angular/material/commit/70a884a164f20b7cfde0c08f66712f86c4789f13)) + + +#### Bug Fixes + +* **autocomplete:** + * cleans up watchers when elements are removed ([7fc0d02c](https://github.com/angular/material/commit/7fc0d02c6009c0c5f688d84cbd76afb9f4262541), closes [#1692](https://github.com/angular/material/issues/1692)) + * prevents aria message on selection ([e2148f13](https://github.com/angular/material/commit/e2148f13aa802196505106e776a959a648a38010)) + * addresses accessibility issues ([0bd7afb8](https://github.com/angular/material/commit/0bd7afb81755ea2815c8af646d692e045509f014), closes [#1473](https://github.com/angular/material/issues/1473)) + * change events will no longer be called on load ([c58d930e](https://github.com/angular/material/commit/c58d930e293ec4105e49391f67d8b31218d474ad)) +* **button:** allow attribute syntax for md-button. ([fc223b0c](https://github.com/angular/material/commit/fc223b0c9f92f03e4a1f2f9d53ac14d699ff02a2), closes [#1661](https://github.com/angular/material/issues/1661)) +* **core:** Remove redeclared variable declaration. ([3454db3c](https://github.com/angular/material/commit/3454db3c3feb8f5f35bad7815a84a26c67c4dd58), closes [#1697](https://github.com/angular/material/issues/1697)) +* **input:** + * resolves on-focus validation checks ([2f17c8f4](https://github.com/angular/material/commit/2f17c8f44a628daf4e81bc576a85f27697dd54ab)) + * error states and improved for input fields ([747eb9c3](https://github.com/angular/material/commit/747eb9c3f493dfc338901f66108042ca78b5936e), closes [#1485](https://github.com/angular/material/issues/1485), [#1633](https://github.com/angular/material/issues/1633), [#1629](https://github.com/angular/material/issues/1629)) +* **select:** + * when nothing selected, focus first option ([50b5d923](https://github.com/angular/material/commit/50b5d923ef92d7fb664bde605c821e643aa1f152)) + * make space not scroll select ([b8da17a0](https://github.com/angular/material/commit/b8da17a07c0b532527193ed0626cd1e9cd8b319e)) + * make sure arrow keys always focus next/prev option ([e441abaf](https://github.com/angular/material/commit/e441abaf91aeb706c8fb773b67bde40fb225601d)) + * render label on external model updates ([6baed64c](https://github.com/angular/material/commit/6baed64c6c20eeabacf8e737e16e5b39de5f39ea)) + * fix updating of values on change and init ([0e21b3bc](https://github.com/angular/material/commit/0e21b3bc0bb96c9e9982ebd74a4b810e02a2935f)) + * change placeholder computation to be handled internally ([b4c0a86e](https://github.com/angular/material/commit/b4c0a86eb3c094c5c897895a4bec392b37923f5f)) + * fix duplicate options when using ng-repeat ([9e0ca443](https://github.com/angular/material/commit/9e0ca4430f9f3add077a21ccb5a963e48a7f7d83)) + * fix chrome double scroll bars ([4d722ecf](https://github.com/angular/material/commit/4d722ecf9f8240f47e6e8989afcc5388c99669b4)) +* **tooltip:** fixes `md-direction` attribute functionality ([93080cae](https://github.com/angular/material/commit/93080cae36e1bea653b39c85d9345afe798de59f), closes [#1673](https://github.com/angular/material/issues/1673)) + + + +### 0.8.1 (2015-02-24) + + +#### Features + +* **select:** + * set label value based on md-option text ([ee4c7c18](https://github.com/angular/material/commit/ee4c7c1809558a51a58928e9fbe06ab16686742a)) + * add disabled support ([0c0f25ce](https://github.com/angular/material/commit/0c0f25ce5bb4e1a4467617541cffdb24d36f8ec7)) + * add ng-change support ([f4ce10ee](https://github.com/angular/material/commit/f4ce10eea6590ac431b7b15c44242b76219e1f7f)) + + +#### Bug Fixes + +* **select:** + * keyboard controls in IE ([69053a30](https://github.com/angular/material/commit/69053a30c869c6ccdf8bcfcaaba451900f38e2ba)) + * fix overflow scroll on IE ([c5c5f860](https://github.com/angular/material/commit/c5c5f8603e631665d93c018bc6f94df2c4125eab)) + * prevent select from closing a dialog on click away ([c573c8cd](https://github.com/angular/material/commit/c573c8cd28c66b3c74f6f6ad482ff0c0b3844ff6)) + * stop position from going past bottom of screen ([805ed1b4](https://github.com/angular/material/commit/805ed1b49369269c0f9606d9f9c812cc8658d954)) + * fix select with 0 options positioning ([5a82426e](https://github.com/angular/material/commit/5a82426ed23724a1860bb7b5efa3e28326512716)) + * support custom interpolate symbols ([20b66111](https://github.com/angular/material/commit/20b6611107332b7cd36b93fc1398263c3ad328ae)) + * remove placeholder for falsey, but defined values ([9497f063](https://github.com/angular/material/commit/9497f063a2f5b118077aaba0cdc3df851754629c)) + * close md-select-menu if md-select is removed ([5e02ad94](https://github.com/angular/material/commit/5e02ad948fc4e68d81b18234102e47e643917b97)) + + + + +### 0.8.0 (2015-02-23) + + +#### Features + +* **select:** + * add ng-change support ([f4ce10ee](https://github.com/angular/material/commit/f4ce10eea6590ac431b7b15c44242b76219e1f7f)) + * add select component and functionality ([786cb3b1](https://github.com/angular/material/commit/786cb3b1642be623b21551e4c8aff9c11d53ca13), closes [#1562](https://github.com/angular/material/issues/1562)) +* **autocomplete:** added initial files for autocomplete ([0bd8cf1c](https://github.com/angular/material/commit/0bd8cf1c31bc3a00513b91d2a200e9cc6818f2d0), closes [#1418](https://github.com/angular/material/issues/1418)) +* **dialog:** add ability to specify theme on alert and confirm presets ([c97f48b7](https://github.com/angular/material/commit/c97f48b7ad6515fe211cb1528ba9c2df14c98b18)) +* **gridlist:** Initial gridList implementation ([ef4aff00](https://github.com/angular/material/commit/ef4aff00f05136cfdeb149b151c85c4cae7a0228)) +* **icon:** implemented md-icon for font-faces and svgs ([b7d09d7e](https://github.com/angular/material/commit/b7d09d7e247d3055e53f438b5528ce9e36ecbc66), closes [#1438](https://github.com/angular/material/issues/1438)) +* **input:** adds `no-float` class to disable floating label animations ([33f677e5](https://github.com/angular/material/commit/33f677e53f97a8dacfae173120dbda369bd734ee), closes [#201](https://github.com/angular/material/issues/201), [#1392](https://github.com/angular/material/issues/1392)) +* **tabs:** changes default state of tabs to be transparent. ([732cbc9c](https://github.com/angular/material/commit/732cbc9c3abc1b001e0c425272ab49aa4f4e2d44), closes [#1250](https://github.com/angular/material/issues/1250), [#1393](https://github.com/angular/material/issues/1393)) +* **toast:** + * add ability to specify theme for simple preset ([2fef2207](https://github.com/angular/material/commit/2fef22078497d6f444511032bcef1e9900a5103a)) + * proper toast queing behavior ([74fe8706](https://github.com/angular/material/commit/74fe87068212e233a8b8ed2a3029d0cf491cd53e)) + * update toast content dynamicly using $mdToast.updateContent ([0e161cb7](https://github.com/angular/material/commit/0e161cb7f9ab02c3774c7071f45bba4a8f97e49b)) +* **tooltip:** adds `md-direction` so that users can specify tooltip direction ([9c69c5cd](https://github.com/angular/material/commit/9c69c5cd4f5ada823cc12bc93246f3c847ecb23d), closes [#1220](https://github.com/angular/material/issues/1220), [#1410](https://github.com/angular/material/issues/1410)) + + +#### Bug Fixes + +* **autocomplete:** + * hitting Enter will now trigger the submit method on parent form tags ([da084fc5](https://github.com/angular/material/commit/da084fc55fe67fa9c5094b73187953423317f5aa), closes [#1530](https://github.com/angular/material/issues/1530)) + * fixes issue with click events not firing ([e088f6ac](https://github.com/angular/material/commit/e088f6aceb108449b7e6786ef3f1329d805a8001), closes [#1513](https://github.com/angular/material/issues/1513)) +* **dialog:** correct opening position for showing a dialog ([150efe62](https://github.com/angular/material/commit/150efe620f98059f18f6088551d10e3a97984fca)) +* **docs:** toolbar button overlap on mobile ([7391cad4](https://github.com/angular/material/commit/7391cad4a040ca674af4135d7336d852125c2d59)) +* **gridlist:** + * Tile removal now supports ngAnimate ([1d8e7832](https://github.com/angular/material/commit/1d8e7832dc6f0c7b20aefd704d8eeaba90cc763c), closes [#1559](https://github.com/angular/material/issues/1559), [#1560](https://github.com/angular/material/issues/1560)) + * add ngInject annotation support for GridLayoutFactory - $mdGridLayout ([c045f542](https://github.com/angular/material/commit/c045f5425fd4c2dc45a366a2dad66bd675ee1cf1)) + * Throws error on invalid or missing colCount to avoid infinite loops ([39f4f26a](https://github.com/angular/material/commit/39f4f26ad1fb8ec6b96254620a1b9dcc1525694a)) + * Adds grid height calculation ([0196014d](https://github.com/angular/material/commit/0196014db76eef8931b5d5b32f94a4fa7d3db675)) + * Prevents media from being unwatched immediately ([a4104215](https://github.com/angular/material/commit/a4104215be8c3aa902095dcb182d28b05ff3b79e)) +* **icon:** + * improve error recovery and item caching ([603e5d68](https://github.com/angular/material/commit/603e5d68623dda4003917989e752fda4e603f36a), closes [#1477](https://github.com/angular/material/issues/1477)) + * add support for themes with md-primary, etc. ([cdea9a2d](https://github.com/angular/material/commit/cdea9a2d586f82a1048eff9c0b834e7428049a81)) +* **input:** + * fix hidden textarea height issue ([efbd414a](https://github.com/angular/material/commit/efbd414a4d5af7b5144f1d08522e46cc043b627d), closes [#1356](https://github.com/angular/material/issues/1356)) + * improve use of placeholder and floating label ([f704dda6](https://github.com/angular/material/commit/f704dda627c2a030e0bdda44f6cb12ac59e951e0), closes [#1409](https://github.com/angular/material/issues/1409)) + * hide input text character overflow ([e290b536](https://github.com/angular/material/commit/e290b536a0f7daecbc095a59d3d641f9105e1f15), closes [#1461](https://github.com/angular/material/issues/1461)) + * improve error checking UX for required inputs ([c1d59aba](https://github.com/angular/material/commit/c1d59aba9f7aa1b30d4664e90ba44235510c9acc), closes [#1491](https://github.com/angular/material/issues/1491), [#1485](https://github.com/angular/material/issues/1485)) +* **layout:** fix responsive breakpoint dead-zones ([ecf6edef](https://github.com/angular/material/commit/ecf6edef9e1ff9a931a77a4665b075faf1988759)) +* **mdButton:** add default color, update docs ([a80804b5](https://github.com/angular/material/commit/a80804b5c1f3ccf554c76e3cad221b750c939a6f), closes [#1486](https://github.com/angular/material/issues/1486)) +* **select:** fixes scrollbar and padding issues ([5d7b63b0](https://github.com/angular/material/commit/5d7b63b0ff286ef0fbeffac3cf61283f4f782e13)) +* **tooltip:** content text was semi-opaque ([42cff135](https://github.com/angular/material/commit/42cff135320727b3615c3cd00300c923112e142d), closes [#1480](https://github.com/angular/material/issues/1480), [#1492](https://github.com/angular/material/issues/1492)) +* **autocomplete:** selected item now properly updates ([1307b945](https://github.com/angular/material/commit/1307b94592c128b31aee7dc8012fa74d2526768f), closes [#1468](https://github.com/angular/material/issues/1468)) +* **button:** remove underline on href button hover ([c19cd433](https://github.com/angular/material/commit/c19cd433eb75fb3b6a9507f8eb36e6e9916f50d3)) +* **card:** fixes selector to be more specific ([2f840b2a](https://github.com/angular/material/commit/2f840b2a221959a9101471ccb86da7a216ab80fd)) +* **gesture:** fix gesture event dispatching with jQuery ([88813b78](https://github.com/angular/material/commit/88813b78cae831b28e2e8d0eb37c32269034bfad), closes [#1367](https://github.com/angular/material/issues/1367)) +* **input:** check invalid model immediately and setInvalid() if needed ([e0f53ddd](https://github.com/angular/material/commit/e0f53ddda7d080e80c08d5106b5586b697ea8e87), closes [#372](https://github.com/angular/material/issues/372)) +* **mdIcon:** + * support aria-label on mdRadioButton ([bbbec18e](https://github.com/angular/material/commit/bbbec18e5a9d79dd2957ddaad36993f80b879ce4)) + * label docs, support from parent el ([f764c049](https://github.com/angular/material/commit/f764c049c9128909fc86a26ffddcb9b6db9dd8f6), closes [#1458](https://github.com/angular/material/issues/1458), [#1460](https://github.com/angular/material/issues/1460)) +* **mdInput:** css cascades from disabled fieldset ([66fa1e3e](https://github.com/angular/material/commit/66fa1e3e276ddf33d669da0ef4d13c73c668e654), closes [#895](https://github.com/angular/material/issues/895)) +* **mdMedia:** fix media listeners not firing on non-chrome browsers ([0dfcaf55](https://github.com/angular/material/commit/0dfcaf553c94935945c7b74b584abd8b73abd40c)) +* **mdTabs:** use md-icon for pagination arrows ([517623e7](https://github.com/angular/material/commit/517623e721cd1d9a104aec2bee22a23889944b3a), closes [#1464](https://github.com/angular/material/issues/1464), [#1466](https://github.com/angular/material/issues/1466)) +* **sidenav:** properly sets width of sidenav ([0318ca44](https://github.com/angular/material/commit/0318ca44ca1d256d50cd1de675c92c8bf4b2bcb1), closes [#957](https://github.com/angular/material/issues/957)) +* **slider:** + * updates positioning method to prevent overflow issues ([fb3623a1](https://github.com/angular/material/commit/fb3623a1da832a43fdccb7402ecfd206248639c9), closes [#1343](https://github.com/angular/material/issues/1343), [#1431](https://github.com/angular/material/issues/1431), [#1391](https://github.com/angular/material/issues/1391)) + * BREAKING: change default color to accent ([3ea349fc](https://github.com/angular/material/commit/3ea349fc7aef2fb22109b69c5c4fb466a4607989)) +* **subheader:** fix subheaders within dialogs ([55084143](https://github.com/angular/material/commit/550841433586d1bbc0a94ef5c0c2ef50e45e28c1)) +* **theming:** fix typo in warning message ([8a6eb7e8](https://github.com/angular/material/commit/8a6eb7e88c7317f755789ba3f2bfe3f88a288b81)) +* **toast:** fix minified toast controller injections ([5c5106e4](https://github.com/angular/material/commit/5c5106e48725cbc7b46269339a33be084ce4aeff)) +* **tooltip:** now works inside elements without pointer events ([3d010cd8](https://github.com/angular/material/commit/3d010cd831c7377e2ebef0df0d897788130cab9f)) + + + +### 0.7.1 (2015-01-30) + +#### Features + +* **bottomSheet:** disable scroll on parent when bottom sheet is shown ([8273126c](https://github.com/angular/material/commit/8273126c99304b315632c377ff22717acb45f03b)) +* **button:** adds attribute override for ripple size ([b7c43a10](https://github.com/angular/material/commit/b7c43a10071455e9024fe403d6b696b664c36df4), closes [#1088](https://github.com/angular/material/issues/1088)) +* **gestures:** add built in gesture system, remove HammerJS ([8364fb57](https://github.com/angular/material/commit/8364fb57a9ac1b211c09ff564fea6ad0dea94e61)) +* **input:** make input placeholder attr work ([f1d7f830](https://github.com/angular/material/commit/f1d7f830bf2f12dab288c46a5fc2919d5d608110), closes [#1279](https://github.com/angular/material/issues/1279)) +* **mdInputContainer:** add mdIsError expr, defaults to $touched && $invalid ([c3cad666](https://github.com/angular/material/commit/c3cad666368cc238644b6c9b1aaf1260cd763187), closes [#1267](https://github.com/angular/material/issues/1267)) +* **sidenav:** + * make it thinner on <360px wide screens ([6ee3346e](https://github.com/angular/material/commit/6ee3346e301e979e89bf6f43449b6c4a51d78670)) + * expose isLockedOpen for sidenav instances ([ba71a598](https://github.com/angular/material/commit/ba71a5987d4600128d6a1c14a479cac37a308d28)) +* **theming:** + * add warnings when using unregistered themes ([f6f56c98](https://github.com/angular/material/commit/f6f56c989bab062462a4dbf4ece59fd744c6ec3b)) + * warn for same palette as primary and accent ([1c973330](https://github.com/angular/material/commit/1c973330c68e0c653a19f4408a373741107eb0e3)) + * fix strong light colors vs normal light colors, as per spec ([dd5b9549](https://github.com/angular/material/commit/dd5b9549f4241eadacb4fe92db574b1bcd9771d5)) + * rename palette methods, change default palettes ([0e0846fe](https://github.com/angular/material/commit/0e0846feb562d32079eff427abbc045f2681c24e), closes [#1252](https://github.com/angular/material/issues/1252)) + * change default color of components that should be accent ([f2996b73](https://github.com/angular/material/commit/f2996b734fd7574ad484f258f1fd674de62d64b5)) +* **toolbar:** add shadow to toolbar ([4e47a174](https://github.com/angular/material/commit/4e47a174659e768e0b506d7ea937aadb67818d56)) + + +#### Breaking Changes + +* As per the +[spec](http://www.google.com/design/spec/style/color.html#color-ui-color-application) +this commit changes the default color palette of FABs, checkboxes, radio +buttons, sliders and switches to the accent palette. + +closes #1255 + + ([f2996b73](https://github.com/angular/material/commit/f2996b734fd7574ad484f258f1fd674de62d64b5)) + + +#### Bug Fixes + +* **card:** fixes selector to be more specific ([2f840b2a](https://github.com/angular/material/commit/2f840b2a221959a9101471ccb86da7a216ab80fd)) +* **gesture:** + * make sure clicks properly support keys ([c6d24eb2](https://github.com/angular/material/commit/c6d24eb2dfb7c3c23f08e09ae7f22812fe395516)) + * fix firefox keyboard events ([79196c3d](https://github.com/angular/material/commit/79196c3df13906c0221a9f0b4e2bab9c0c25825e)) + * only hijack click events on mobile devices ([ade65b60](https://github.com/angular/material/commit/ade65b6023021350a9a6d50d5d3245104766fe82)) +* **theming:** + * fix warning for `changeTheme` being wrong ([f44bf604](https://github.com/angular/material/commit/f44bf6040dea4c1816922f595ec7e38213f914c6)) +* **checkbox:** + * make `mdAria` check linked element ([3346532c](https://github.com/angular/material/commit/3346532ca1deba489fb79bccc0047f4f9f10e8da)) +* **build:** + * add annotations to `rAfDecorator`, remove unused args ([c4927f9e](https://github.com/angular/material/commit/c4927f9e4ca1ffd21550e5eefb08b34c8840d02b)) + * add annotation to `swipe.js` ([22040c77](https://github.com/angular/material/commit/22040c77b4f82dd9845ad6bcfab9ab62a534170c)) +* **button:** + * change default style of fab to white instead of transparent ([04feeb83](https://github.com/angular/material/commit/04feeb836ae5508d4c0349f125d13b75dd63b7b3)) + * default background-color on fab buttons on toolbar ([08ebff44](https://github.com/angular/material/commit/08ebff4405f75d305c24eca3549668bbc84d7ce8)) +* **card:** + * allow img to have a wrapper ([349b521e](https://github.com/angular/material/commit/349b521e550c48a55713659d8d6fc2f4e1719a74)) +* **dialog:** fix overlay not covering, dialog position in overlay ([1d5ef95d](https://github.com/angular/material/commit/1d5ef95d2a1daa91bcad98d460eec49923ea5233)) +* **input:** + * dont add focus/blur class if readonly ([6333b72c](https://github.com/angular/material/commit/6333b72c2cd50d848924e694237772371fefa759), closes [#1203](https://github.com/angular/material/issues/1203)) + * fix input padding & border on iOS ([7dab2060](https://github.com/angular/material/commit/7dab2060dd6f1c07dcb7186a1de360c20d3014fd), closes [#1164](https://github.com/angular/material/issues/1164)) + * remove default Firefox invalid input styling ([ba65437b](https://github.com/angular/material/commit/ba65437b452835c96bba9a7681710aec253264de), closes [#1165](https://github.com/angular/material/issues/1165)) + * add check for input value on blur ([ec53d1a1](https://github.com/angular/material/commit/ec53d1a1d02a92e3c8d71c25d354784709124fee), closes [#1201](https://github.com/angular/material/issues/1201)) +* **layout:** fix IE11 layout ([74fe3eb1](https://github.com/angular/material/commit/74fe3eb19b097611ed17f2f1459a5682b043387a), closes [#1227](https://github.com/angular/material/issues/1227)) +* **mdSwitch:** add missing styles to switch ([54338d7d](https://github.com/angular/material/commit/54338d7d4220fd0bb88af3e3b584c70fe5ac37ab), closes [#912](https://github.com/angular/material/issues/912)) +* **ripple:** fixes size issue with ripple on switches ([c435409b](https://github.com/angular/material/commit/c435409bfdcda51c5ba164c9013a3da1e5a03ce6)) +* **slider:** + * don't run touchend listener if disabled ([5bbd23d6](https://github.com/angular/material/commit/5bbd23d6ad6d944806943786a748329428620e79), closes [#1308](https://github.com/angular/material/issues/1308)) + * make modelValue be set on pressdown ([7028a750](https://github.com/angular/material/commit/7028a75058338533696d75d532e7f13f6d6f1fff), closes [#1296](https://github.com/angular/material/issues/1296)) + * fix thumb positioning so that it works when not visible ([41c2d65d](https://github.com/angular/material/commit/41c2d65d2d4344687959c0d13c2cf48b0c90a880), closes [#1210](https://github.com/angular/material/issues/1210)) +* **styles:** fix subheader z-index, button md-mini class, md-no-bar. ([dde9ab79](https://github.com/angular/material/commit/dde9ab7987c8df787ff72c3ce46b9247ffdf7aad), closes [#1182](https://github.com/angular/material/issues/1182), [#1034](https://github.com/angular/material/issues/1034), [#1173](https://github.com/angular/material/issues/1173), [#1194](https://github.com/angular/material/issues/1194)) +* **switch:** set tabindex to -1 while disabled ([19f47b5d](https://github.com/angular/material/commit/19f47b5dcbf3006fbc14a08d909bc0265058dfe0)) +* **tabs:** + * adds fix for css transition on theme change ([312dcc6c](https://github.com/angular/material/commit/312dcc6c51f81de8284f43959c30d51e286bca29), closes [#1033](https://github.com/angular/material/issues/1033)) + * remove bad opacity on focus state ([72ced4b5](https://github.com/angular/material/commit/72ced4b5b93fd82dc3e7382850f964baffbda32c)) + * prevents multiple pagination clicks during animation ([299e1556](https://github.com/angular/material/commit/299e15569783d4f666863ac3e9f6ceed237b6cf0), closes [#1207](https://github.com/angular/material/issues/1207)) +* **toast:** + * fix highlighting of action buttons ([53cffe29](https://github.com/angular/material/commit/53cffe2945006ea9f5e2171fa2fbaf73b7ac6d27)) + * fix excess padding on md-action ([0f40a843](https://github.com/angular/material/commit/0f40a8431f5b807d43c2054c64d40008213d4cf5)) + + + +### 0.7.0 (2015-01-24) + + +#### Bug Fixes + +* **input:** + * fix bug regarding `md-maxlength` value changes ([b432277d](https://github.com/angular/material/commit/b432277d59614d2d23e4f651a1b3c46d76ec50ae)) + + +### 0.7.0-rc3 (2015-01-14) + + +#### Bug Fixes + +* allow user selection when swipe listener is enabled ([520faa72](https://github.com/angular/material/commit/520faa72e8a1ebf9112d615097e939349997fc51), closes [#838](https://github.com/angular/material/issues/838)) +* **button:** + * fixes vertical alignment issue with `md-fab` button ([f71eb32a](https://github.com/angular/material/commit/f71eb32a0070bdbf6ea5613d7dce32a8fa22a02c), closes [#914](https://github.com/angular/material/issues/914)) + * adds a safe disabled-check for ripples ([9091741f](https://github.com/angular/material/commit/9091741f80002352ef16901d7abdd860631dce68), closes [#1029](https://github.com/angular/material/issues/1029)) + * fix usages with ngDisabled ([416079b7](https://github.com/angular/material/commit/416079b787becfe584d0633ae8e7946e4309f438), closes [#1074](https://github.com/angular/material/issues/1074)) +* **dialog:** + * fix dialog alignment in IE11 ([240c03aa](https://github.com/angular/material/commit/240c03aa188520a20e0416095c20ace8a685fca3), closes [#790](https://github.com/angular/material/issues/790)) + * fix margin-top when layout is row ([191df15a](https://github.com/angular/material/commit/191df15abf13cae397f7e9c3c73db956842dfee3)) +* **input:** fix label inputs with specified types ([747c6acb](https://github.com/angular/material/commit/747c6acb1835ce388215d8ecc0794ec4da67a43b)) +* **layout:** make sure hide-gt-* and show-gt-* work together properly ([d149f36b](https://github.com/angular/material/commit/d149f36b6ab2a24d22e0246d4db8c030dcb84f96), closes [#772](https://github.com/angular/material/issues/772)) +* **radioGroup:** fix render call happing before radioGroup init ([68e350d1](https://github.com/angular/material/commit/68e350d11dcd15ae07c495e6859ba32f47d79836)) +* **subheader:** make content clickable ([7178b6d6](https://github.com/angular/material/commit/7178b6d674336a8d9ee718b58fb2f1aece85c80b), closes [#554](https://github.com/angular/material/issues/554)) +* **tabs:** + * fix overflow leaking out tab-content ([dec2ac42](https://github.com/angular/material/commit/dec2ac42ebec04070e81fe1a664e7be906f0b4a4)) + * factors `me-active` attribute into selection logic to prevent unnecessary `md-on ([6a087a01](https://github.com/angular/material/commit/6a087a01656b3e8f6ba2e87b40b0611519b75c2b), closes [#868](https://github.com/angular/material/issues/868)) + * adds a delayed call to update the ink bar after a tab is removed ([1a1095b0](https://github.com/angular/material/commit/1a1095b0fae19e4b6df80027f57870e7aff7b97f), closes [#573](https://github.com/angular/material/issues/573)) +* **theming:** make switch, checkbox, radio button default to primary color for consistency ([8cbfeadf](https://github.com/angular/material/commit/8cbfeadfb32e19e855b6280983784fe0a8a516cb)) + + +#### Features + +* **input:** add error states, md-maxlength ([a2bc3c68](https://github.com/angular/material/commit/a2bc3c68551b4915c40a4eca9ec48fa9ec61f6b7)) +* **layout:** add flex-order-{sm,gt-sm,md,gt-md,lg,gt-lg} attributes ([3e453078](https://github.com/angular/material/commit/3e4530785c29650ff46cf7688f0b154adb9a7042)) +* **tooltip:** add configurable md-delay attr, default 400ms. ([e4ed530d](https://github.com/angular/material/commit/e4ed530d8000b6e31c9e4e7d52e402b9b76debd2), closes [#713](https://github.com/angular/material/issues/713)) + + + +### 0.7.0-rc2 (2015-01-08) + + +#### Bug Fixes + +* **$mdUtil:** fix bugs in iterator's `next()`/`previous()` methods Refactor for DRY-ness `next ([124466e7](https://github.com/angular/material/commit/124466e71945a4515a7b5742310594e8753c4314)) +* **$mdComponentRegistry:** gracefully recover if no md-component-id is specified ([bf2266f1](https://github.com/angular/material/commit/bf2266f15a6d2c8cc299f083544955b1d1f0dc69)) +* **demos:** tab dynamic demo removes use of on-select expressions ([4db16c17](https://github.com/angular/material/commit/4db16c17fa617c53fd8436de00386826e08b602b)) +* **mdDialog:** + * fix dialog border radius visual overflow glitch ([9b162202](https://github.com/angular/material/commit/9b162202721a2a60884e8edf4e02f754e9bef447), closes [#1015](https://github.com/angular/material/issues/1015)) + * prevent null-pointer error with popInTarget ([b36282a5](https://github.com/angular/material/commit/b36282a586d700831006c750d1df743bb16c6194), closes [#1061](https://github.com/angular/material/issues/1061)) +* **input:** fix height on IE11 ([dc31ee53](https://github.com/angular/material/commit/dc31ee53bb88dcc782b7aaa9c9ade31085ab3e69)) +* **layout:** + * fix 'layout-padding' rule not having comma ([b35be936](https://github.com/angular/material/commit/b35be936cab8118c7de483c5065b6db56018e855), closes [#952](https://github.com/angular/material/issues/952)) + * layout-fill in firefox ([31d3c123](https://github.com/angular/material/commit/31d3c123185c6fe3e0db95674cccefb2b8884bca)) + * layout-wrap fixed and documented ([8f937bd2](https://github.com/angular/material/commit/8f937bd2df7e43d0343f5e89e154f6b0a3c89ecc)) +* **mdButton:** + * fix fab vertical positioning ([641e2272](https://github.com/angular/material/commit/641e2272ce1cad731a59f015bede4a97fa2fca53), closes [#1031](https://github.com/angular/material/issues/1031)) + * fix error when md-button is disabled anchor ([48e5a8bc](https://github.com/angular/material/commit/48e5a8bc365e89f1d0446758a7211f5773956443), closes [#1074](https://github.com/angular/material/issues/1074)) +* **mdCard:** add an md-card-content container inside md-card for content ([28a4f8ff](https://github.com/angular/material/commit/28a4f8ff4d3b1d7b123152a01ef71e767fc315ff), closes [#265](https://github.com/angular/material/issues/265)) +* **mdMedia:** avoid unnecessary digest and make changes apply quicker ([98247bcf](https://github.com/angular/material/commit/98247bcf22df9ef96e4dd0197d61e6b9b69e1b6d), closes [#978](https://github.com/angular/material/issues/978)) +* **mdRadioButton:** Loosen equality check ([ca3e4c30](https://github.com/angular/material/commit/ca3e4c306af3e4a49670a379d759f0448b42ca95), closes [#1112](https://github.com/angular/material/issues/1112)) +* **mdToolbar:** Toolbar flow on medium screens ([bfc947f6](https://github.com/angular/material/commit/bfc947f66b2a2568dc76ca3278eb9b5f83424a2f)) +* **mdUtil:** + * fix `throttle()` delay check ([fdb923e4](https://github.com/angular/material/commit/fdb923e40f98422ff75cbcaf137ead2233c64c68), closes [#977](https://github.com/angular/material/issues/977)) + * remove/delete cacheFactory keys when clearing/destroying cache ([8736c7cc](https://github.com/angular/material/commit/8736c7ccf019df417e6b7834b55a1cc157a6ac64)) +* **mdRadioButton:** arrowkey navigation with disabled buttons ([f520d507](https://github.com/angular/material/commit/f520d50710e6e93686736c4f5a97e54bb9bb7518), closes [#1040](https://github.com/angular/material/issues/1040)) +* **mdSidenav:** Fix tests and typo ([c0e2b0fb](https://github.com/angular/material/commit/c0e2b0fbba0006ab2ea8930544c380d207dfea1a)) +* **mdTabs:** + * make md-tab-label visible on IE11 ([b85ad629](https://github.com/angular/material/commit/b85ad6296f49be7fa5ce95cbbbec49d650912e46), closes [#1057](https://github.com/angular/material/issues/1057)) + * pagination only call watcher() when it's a function ([e952ab41](https://github.com/angular/material/commit/e952ab4100826a5ff2e36efe71d5d6b8d49df2b2), closes [#1073](https://github.com/angular/material/issues/1073)) + * delays disconnect until after the digest is finished ([78ba497e](https://github.com/angular/material/commit/78ba497e443ca31e8a8c97f11db281f743f6aca0), closes [#1048](https://github.com/angular/material/issues/1048)) +* **theming:** + * switch accent palette to use accent hues ([002d8bfd](https://github.com/angular/material/commit/002d8bfde5aa0c240ebd054297227e499f9c3bf4)) + * allow hex values with uppercase letters ([9b45af50](https://github.com/angular/material/commit/9b45af50fd894d9e9451b833bb9c2edb1ff2e750), closes [#1014](https://github.com/angular/material/issues/1014)) + + +#### Features + +* **mdDialog:** disable scrolling on parent while showing dialog ([993fa2bc](https://github.com/angular/material/commit/993fa2bc00598dd18227b12bb197f2d9c667ea75)) +* **input:** + * add parent for inputs/textareas, deprecate md-text-float ([60fcd6f4](https://github.com/angular/material/commit/60fcd6f4d8b895162b37a940c3f33164d8044382), closes [#993](https://github.com/angular/material/issues/993), [#553](https://github.com/angular/material/issues/553), [#654](https://github.com/angular/material/issues/654), [#993](https://github.com/angular/material/issues/993)) + * support md-warn for theming ([6acacc53](https://github.com/angular/material/commit/6acacc5382940a7ce1b393d0f4cdda6a0ffa615c), closes [#1137](https://github.com/angular/material/issues/1137)) +* **textarea:** make textarea autogrow with size of content ([1c653696](https://github.com/angular/material/commit/1c65369629080ddb6b2c4a981ae00533f5c303b1), closes [#565](https://github.com/angular/material/issues/565)) +* **layout:** add layout-align-{sm,gt-sm,md,gt-md,lg,gt-lg} attrs ([8550bd6c](https://github.com/angular/material/commit/8550bd6c9353914083bf75328c0160027202d237), closes [#631](https://github.com/angular/material/issues/631)) +* **mdRadioGroup:** Radio submits on keydown/enter ([03c75927](https://github.com/angular/material/commit/03c7592798f904ac7a59b4a1c580672ca7c4789f), closes [#577](https://github.com/angular/material/issues/577)) +* **mdSlider:** make discrete track ticks themable ([91bc598f](https://github.com/angular/material/commit/91bc598fab00150e26b11a2c7a0e7c9b3b364bec), closes [#621](https://github.com/angular/material/issues/621)) +* **mdSwitch:** add grab/grabbing cursor during drag ([c60640bf](https://github.com/angular/material/commit/c60640bf4305cbd42d899db5b2adfe8601096d1b), closes [#983](https://github.com/angular/material/issues/983)) +* **mdTabs:** adds default transitions for tab content ([3ee83a64](https://github.com/angular/material/commit/3ee83a645b9e4da8f4c0f2e6cbf772f504d8e9a9), closes [#1044](https://github.com/angular/material/issues/1044), [#717](https://github.com/angular/material/issues/717), [#811](https://github.com/angular/material/issues/811)) +* **$mdToast:** add mdToast#showSimple shortcut method ([dd960c6f](https://github.com/angular/material/commit/dd960c6fce3dfc041ab2ee6c27f6574cfae75185)) + + +#### Breaking Changes + +* md-text-float has been deprecated due to flaws (explanation in [#547](https://github.com/angular/material/issues/547)). + +To create an input, you now must use the native `` and ` + * + * + * + */ +function mdInputContainerDirective($mdTheming, $parse) { + ContainerCtrl.$inject = ["$scope", "$element", "$attrs"]; + return { + restrict: 'E', + link: postLink, + controller: ContainerCtrl + }; + + function postLink(scope, element, attr) { + $mdTheming(element); + } + function ContainerCtrl($scope, $element, $attrs) { + var self = this; + + self.isErrorGetter = $attrs.mdIsError && $parse($attrs.mdIsError); + + self.delegateClick = function() { + self.input.focus(); + }; + self.element = $element; + self.setFocused = function(isFocused) { + $element.toggleClass('md-input-focused', !!isFocused); + }; + self.setHasValue = function(hasValue) { + $element.toggleClass('md-input-has-value', !!hasValue); + }; + self.setInvalid = function(isInvalid) { + $element.toggleClass('md-input-invalid', !!isInvalid); + }; + $scope.$watch(function() { + return self.label && self.input; + }, function(hasLabelAndInput) { + if (hasLabelAndInput && !self.label.attr('for')) { + self.label.attr('for', self.input.attr('id')); + } + }); + } +} +mdInputContainerDirective.$inject = ["$mdTheming", "$parse"]; + +function labelDirective() { + return { + restrict: 'E', + require: '^?mdInputContainer', + link: function(scope, element, attr, containerCtrl) { + if (!containerCtrl || attr.mdNoFloat) return; + + containerCtrl.label = element; + scope.$on('$destroy', function() { + containerCtrl.label = null; + }); + } + }; +} + +/** + * @ngdoc directive + * @name mdInput + * @restrict E + * @module material.components.input + * + * @description + * Use the `` or the ` + *
      + *
      This is required!
      + *
      That's too long!
      + *
      + * + * + * + * + * + * + * + * + * + * + * Requires [ngMessages](https://docs.angularjs.org/api/ngMessages). + * Behaves like the [AngularJS input directive](https://docs.angularjs.org/api/ng/directive/input). + * + */ + +function inputTextareaDirective($mdUtil, $window, $mdAria) { + return { + restrict: 'E', + require: ['^?mdInputContainer', '?ngModel'], + link: postLink + }; + + function postLink(scope, element, attr, ctrls) { + + var containerCtrl = ctrls[0]; + var ngModelCtrl = ctrls[1] || $mdUtil.fakeNgModel(); + var isReadonly = angular.isDefined(attr.readonly); + + if ( !containerCtrl ) return; + if (containerCtrl.input) { + throw new Error(" can only have *one* or + * + * + * + */ +function mdInputContainerDirective($mdTheming, $parse) { + ContainerCtrl.$inject = ["$scope", "$element", "$attrs"]; + return { + restrict: 'E', + link: postLink, + controller: ContainerCtrl + }; + + function postLink(scope, element, attr) { + $mdTheming(element); + } + function ContainerCtrl($scope, $element, $attrs) { + var self = this; + + self.isErrorGetter = $attrs.mdIsError && $parse($attrs.mdIsError); + + self.delegateClick = function() { + self.input.focus(); + }; + self.element = $element; + self.setFocused = function(isFocused) { + $element.toggleClass('md-input-focused', !!isFocused); + }; + self.setHasValue = function(hasValue) { + $element.toggleClass('md-input-has-value', !!hasValue); + }; + self.setInvalid = function(isInvalid) { + $element.toggleClass('md-input-invalid', !!isInvalid); + }; + $scope.$watch(function() { + return self.label && self.input; + }, function(hasLabelAndInput) { + if (hasLabelAndInput && !self.label.attr('for')) { + self.label.attr('for', self.input.attr('id')); + } + }); + } +} +mdInputContainerDirective.$inject = ["$mdTheming", "$parse"]; + +function labelDirective() { + return { + restrict: 'E', + require: '^?mdInputContainer', + link: function(scope, element, attr, containerCtrl) { + if (!containerCtrl || attr.mdNoFloat) return; + + containerCtrl.label = element; + scope.$on('$destroy', function() { + containerCtrl.label = null; + }); + } + }; +} + +/** + * @ngdoc directive + * @name mdInput + * @restrict E + * @module material.components.input + * + * @description + * Use the `` or the ` + *
      + *
      This is required!
      + *
      That's too long!
      + *
      + * + * + * + * + * + * + * + * + * + * + * Requires [ngMessages](https://docs.angularjs.org/api/ngMessages). + * Behaves like the [AngularJS input directive](https://docs.angularjs.org/api/ng/directive/input). + * + */ + +function inputTextareaDirective($mdUtil, $window, $mdAria) { + return { + restrict: 'E', + require: ['^?mdInputContainer', '?ngModel'], + link: postLink + }; + + function postLink(scope, element, attr, ctrls) { + + var containerCtrl = ctrls[0]; + var ngModelCtrl = ctrls[1] || $mdUtil.fakeNgModel(); + var isReadonly = angular.isDefined(attr.readonly); + + if ( !containerCtrl ) return; + if (containerCtrl.input) { + throw new Error(" can only have *one* or + * + * + * + */ +function mdInputContainerDirective($mdTheming, $parse) { + ContainerCtrl.$inject = ["$scope", "$element", "$attrs"]; + return { + restrict: 'E', + link: postLink, + controller: ContainerCtrl + }; + + function postLink(scope, element, attr) { + $mdTheming(element); + } + function ContainerCtrl($scope, $element, $attrs) { + var self = this; + + self.isErrorGetter = $attrs.mdIsError && $parse($attrs.mdIsError); + + self.delegateClick = function() { + self.input.focus(); + }; + self.element = $element; + self.setFocused = function(isFocused) { + $element.toggleClass('md-input-focused', !!isFocused); + }; + self.setHasValue = function(hasValue) { + $element.toggleClass('md-input-has-value', !!hasValue); + }; + self.setInvalid = function(isInvalid) { + $element.toggleClass('md-input-invalid', !!isInvalid); + }; + $scope.$watch(function() { + return self.label && self.input; + }, function(hasLabelAndInput) { + if (hasLabelAndInput && !self.label.attr('for')) { + self.label.attr('for', self.input.attr('id')); + } + }); + } +} +mdInputContainerDirective.$inject = ["$mdTheming", "$parse"]; + +function labelDirective() { + return { + restrict: 'E', + require: '^?mdInputContainer', + link: function(scope, element, attr, containerCtrl) { + if (!containerCtrl || attr.mdNoFloat) return; + + containerCtrl.label = element; + scope.$on('$destroy', function() { + containerCtrl.label = null; + }); + } + }; +} + +/** + * @ngdoc directive + * @name mdInput + * @restrict E + * @module material.components.input + * + * @description + * Use the `` or the ` + *
      + *
      This is required!
      + *
      That's too long!
      + *
      + * + * + * + * + * + * + * + * + * + * + * Requires [ngMessages](https://docs.angularjs.org/api/ngMessages). + * Behaves like the [AngularJS input directive](https://docs.angularjs.org/api/ng/directive/input). + * + */ + +function inputTextareaDirective($mdUtil, $window, $mdAria) { + return { + restrict: 'E', + require: ['^?mdInputContainer', '?ngModel'], + link: postLink + }; + + function postLink(scope, element, attr, ctrls) { + + var containerCtrl = ctrls[0]; + var ngModelCtrl = ctrls[1] || $mdUtil.fakeNgModel(); + var isReadonly = angular.isDefined(attr.readonly); + + if ( !containerCtrl ) return; + if (containerCtrl.input) { + throw new Error(" can only have *one* or + + + + + + + + + + + + + + + + + + + + + + + + + +
      DirectiveHowSourceRendered
      ng-bind-htmlAutomatically uses $sanitize
      <div ng-bind-html="snippet">
      </div>
      ng-bind-htmlBypass $sanitize by explicitly trusting the dangerous value +
      <div ng-bind-html="deliberatelyTrustDangerousSnippet()">
      +</div>
      +
      ng-bindAutomatically escapes
      <div ng-bind="snippet">
      </div>
      + + + + it('should sanitize the html snippet by default', function() { + expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()). + toBe('

      an html\nclick here\nsnippet

      '); + }); + + it('should inline raw snippet if bound to a trusted value', function() { + expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()). + toBe("

      an html\n" + + "click here\n" + + "snippet

      "); + }); + + it('should escape snippet without any filter', function() { + expect(element(by.css('#bind-default div')).getInnerHtml()). + toBe("<p style=\"color:blue\">an html\n" + + "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + + "snippet</p>"); + }); + + it('should update', function() { + element(by.model('snippet')).clear(); + element(by.model('snippet')).sendKeys('new text'); + expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()). + toBe('new text'); + expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe( + 'new text'); + expect(element(by.css('#bind-default div')).getInnerHtml()).toBe( + "new <b onclick=\"alert(1)\">text</b>"); + }); +
      + + */ + + +/** + * @ngdoc provider + * @name $sanitizeProvider + * + * @description + * Creates and configures {@link $sanitize} instance. + */ +function $SanitizeProvider() { + var svgEnabled = false; + + this.$get = ['$$sanitizeUri', function($$sanitizeUri) { + if (svgEnabled) { + angular.extend(validElements, svgElements); + } + return function(html) { + var buf = []; + htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) { + return !/^unsafe:/.test($$sanitizeUri(uri, isImage)); + })); + return buf.join(''); + }; + }]; + + + /** + * @ngdoc method + * @name $sanitizeProvider#enableSvg + * @kind function + * + * @description + * Enables a subset of svg to be supported by the sanitizer. + * + *
      + *

      By enabling this setting without taking other precautions, you might expose your + * application to click-hijacking attacks. In these attacks, sanitized svg elements could be positioned + * outside of the containing element and be rendered over other elements on the page (e.g. a login + * link). Such behavior can then result in phishing incidents.

      + * + *

      To protect against these, explicitly setup `overflow: hidden` css rule for all potential svg + * tags within the sanitized content:

      + * + *
      + * + *
      
      +   *   .rootOfTheIncludedContent svg {
      +   *     overflow: hidden !important;
      +   *   }
      +   *   
      + *
      + * + * @param {boolean=} regexp New regexp to whitelist urls with. + * @returns {boolean|ng.$sanitizeProvider} Returns the currently configured value if called + * without an argument or self for chaining otherwise. + */ + this.enableSvg = function(enableSvg) { + if (angular.isDefined(enableSvg)) { + svgEnabled = enableSvg; + return this; + } else { + return svgEnabled; + } + }; +} + +function sanitizeText(chars) { + var buf = []; + var writer = htmlSanitizeWriter(buf, angular.noop); + writer.chars(chars); + return buf.join(''); +} + + +// Regular Expressions for parsing tags and attributes +var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g, + // Match everything outside of normal chars and " (quote character) + NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g; + + +// Good source of info about elements and attributes +// http://dev.w3.org/html5/spec/Overview.html#semantics +// http://simon.html5.org/html-elements + +// Safe Void Elements - HTML5 +// http://dev.w3.org/html5/spec/Overview.html#void-elements +var voidElements = toMap("area,br,col,hr,img,wbr"); + +// Elements that you can, intentionally, leave open (and which close themselves) +// http://dev.w3.org/html5/spec/Overview.html#optional-tags +var optionalEndTagBlockElements = toMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"), + optionalEndTagInlineElements = toMap("rp,rt"), + optionalEndTagElements = angular.extend({}, + optionalEndTagInlineElements, + optionalEndTagBlockElements); + +// Safe Block Elements - HTML5 +var blockElements = angular.extend({}, optionalEndTagBlockElements, toMap("address,article," + + "aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," + + "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul")); + +// Inline Elements - HTML5 +var inlineElements = angular.extend({}, optionalEndTagInlineElements, toMap("a,abbr,acronym,b," + + "bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," + + "samp,small,span,strike,strong,sub,sup,time,tt,u,var")); + +// SVG Elements +// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements +// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted. +// They can potentially allow for arbitrary javascript to be executed. See #11290 +var svgElements = toMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," + + "hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," + + "radialGradient,rect,stop,svg,switch,text,title,tspan"); + +// Blocked Elements (will be stripped) +var blockedElements = toMap("script,style"); + +var validElements = angular.extend({}, + voidElements, + blockElements, + inlineElements, + optionalEndTagElements); + +//Attributes that have href and hence need to be sanitized +var uriAttrs = toMap("background,cite,href,longdesc,src,xlink:href"); + +var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' + + 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' + + 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' + + 'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' + + 'valign,value,vspace,width'); + +// SVG attributes (without "id" and "name" attributes) +// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes +var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' + + 'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' + + 'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' + + 'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' + + 'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' + + 'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' + + 'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' + + 'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' + + 'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' + + 'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' + + 'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' + + 'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' + + 'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' + + 'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' + + 'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true); + +var validAttrs = angular.extend({}, + uriAttrs, + svgAttrs, + htmlAttrs); + +function toMap(str, lowercaseKeys) { + var obj = {}, items = str.split(','), i; + for (i = 0; i < items.length; i++) { + obj[lowercaseKeys ? angular.lowercase(items[i]) : items[i]] = true; + } + return obj; +} + +var inertBodyElement; +(function(window) { + var doc; + if (window.document && window.document.implementation) { + doc = window.document.implementation.createHTMLDocument("inert"); + } else { + throw $sanitizeMinErr('noinert', "Can't create an inert html document"); + } + var docElement = doc.documentElement || doc.getDocumentElement(); + var bodyElements = docElement.getElementsByTagName('body'); + + // usually there should be only one body element in the document, but IE doesn't have any, so we need to create one + if (bodyElements.length === 1) { + inertBodyElement = bodyElements[0]; + } else { + var html = doc.createElement('html'); + inertBodyElement = doc.createElement('body'); + html.appendChild(inertBodyElement); + doc.appendChild(html); + } +})(window); + +/** + * @example + * htmlParser(htmlString, { + * start: function(tag, attrs) {}, + * end: function(tag) {}, + * chars: function(text) {}, + * comment: function(text) {} + * }); + * + * @param {string} html string + * @param {object} handler + */ +function htmlParser(html, handler) { + if (html === null || html === undefined) { + html = ''; + } else if (typeof html !== 'string') { + html = '' + html; + } + inertBodyElement.innerHTML = html; + + //mXSS protection + var mXSSAttempts = 5; + do { + if (mXSSAttempts === 0) { + throw $sanitizeMinErr('uinput', "Failed to sanitize html because the input is unstable"); + } + mXSSAttempts--; + + // strip custom-namespaced attributes on IE<=11 + if (document.documentMode <= 11) { + stripCustomNsAttrs(inertBodyElement); + } + html = inertBodyElement.innerHTML; //trigger mXSS + inertBodyElement.innerHTML = html; + } while (html !== inertBodyElement.innerHTML); + + var node = inertBodyElement.firstChild; + while (node) { + switch (node.nodeType) { + case 1: // ELEMENT_NODE + handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes)); + break; + case 3: // TEXT NODE + handler.chars(node.textContent); + break; + } + + var nextNode; + if (!(nextNode = node.firstChild)) { + if (node.nodeType == 1) { + handler.end(node.nodeName.toLowerCase()); + } + nextNode = node.nextSibling; + if (!nextNode) { + while (nextNode == null) { + node = node.parentNode; + if (node === inertBodyElement) break; + nextNode = node.nextSibling; + if (node.nodeType == 1) { + handler.end(node.nodeName.toLowerCase()); + } + } + } + } + node = nextNode; + } + + while (node = inertBodyElement.firstChild) { + inertBodyElement.removeChild(node); + } +} + +function attrToMap(attrs) { + var map = {}; + for (var i = 0, ii = attrs.length; i < ii; i++) { + var attr = attrs[i]; + map[attr.name] = attr.value; + } + return map; +} + + +/** + * Escapes all potentially dangerous characters, so that the + * resulting string can be safely inserted into attribute or + * element text. + * @param value + * @returns {string} escaped text + */ +function encodeEntities(value) { + return value. + replace(/&/g, '&'). + replace(SURROGATE_PAIR_REGEXP, function(value) { + var hi = value.charCodeAt(0); + var low = value.charCodeAt(1); + return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';'; + }). + replace(NON_ALPHANUMERIC_REGEXP, function(value) { + return '&#' + value.charCodeAt(0) + ';'; + }). + replace(//g, '>'); +} + +/** + * create an HTML/XML writer which writes to buffer + * @param {Array} buf use buf.join('') to get out sanitized html string + * @returns {object} in the form of { + * start: function(tag, attrs) {}, + * end: function(tag) {}, + * chars: function(text) {}, + * comment: function(text) {} + * } + */ +function htmlSanitizeWriter(buf, uriValidator) { + var ignoreCurrentElement = false; + var out = angular.bind(buf, buf.push); + return { + start: function(tag, attrs) { + tag = angular.lowercase(tag); + if (!ignoreCurrentElement && blockedElements[tag]) { + ignoreCurrentElement = tag; + } + if (!ignoreCurrentElement && validElements[tag] === true) { + out('<'); + out(tag); + angular.forEach(attrs, function(value, key) { + var lkey=angular.lowercase(key); + var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background'); + if (validAttrs[lkey] === true && + (uriAttrs[lkey] !== true || uriValidator(value, isImage))) { + out(' '); + out(key); + out('="'); + out(encodeEntities(value)); + out('"'); + } + }); + out('>'); + } + }, + end: function(tag) { + tag = angular.lowercase(tag); + if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) { + out(''); + } + if (tag == ignoreCurrentElement) { + ignoreCurrentElement = false; + } + }, + chars: function(chars) { + if (!ignoreCurrentElement) { + out(encodeEntities(chars)); + } + } + }; +} + + +/** + * When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare + * ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want + * to allow any of these custom attributes. This method strips them all. + * + * @param node Root element to process + */ +function stripCustomNsAttrs(node) { + if (node.nodeType === Node.ELEMENT_NODE) { + var attrs = node.attributes; + for (var i = 0, l = attrs.length; i < l; i++) { + var attrNode = attrs[i]; + var attrName = attrNode.name.toLowerCase(); + if (attrName === 'xmlns:ns1' || attrName.indexOf('ns1:') === 0) { + node.removeAttributeNode(attrNode); + i--; + l--; + } + } + } + + var nextNode = node.firstChild; + if (nextNode) { + stripCustomNsAttrs(nextNode); + } + + nextNode = node.nextSibling; + if (nextNode) { + stripCustomNsAttrs(nextNode); + } +} + + + +// define ngSanitize module and register $sanitize service +angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider); + +/* global sanitizeText: false */ + +/** + * @ngdoc filter + * @name linky + * @kind function + * + * @description + * Finds links in text input and turns them into html links. Supports `http/https/ftp/mailto` and + * plain email address links. + * + * Requires the {@link ngSanitize `ngSanitize`} module to be installed. + * + * @param {string} text Input text. + * @param {string} target Window (`_blank|_self|_parent|_top`) or named frame to open links in. + * @param {object|function(url)} [attributes] Add custom attributes to the link element. + * + * Can be one of: + * + * - `object`: A map of attributes + * - `function`: Takes the url as a parameter and returns a map of attributes + * + * If the map of attributes contains a value for `target`, it overrides the value of + * the target parameter. + * + * + * @returns {string} Html-linkified and {@link $sanitize sanitized} text. + * + * @usage + + * + * @example + + +
      + Snippet: + + + + + + + + + + + + + + + + + + + + + + + + + + +
      FilterSourceRendered
      linky filter +
      <div ng-bind-html="snippet | linky">
      </div>
      +
      +
      +
      linky target +
      <div ng-bind-html="snippetWithSingleURL | linky:'_blank'">
      </div>
      +
      +
      +
      linky custom attributes +
      <div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}">
      </div>
      +
      +
      +
      no filter
      <div ng-bind="snippet">
      </div>
      + + + angular.module('linkyExample', ['ngSanitize']) + .controller('ExampleController', ['$scope', function($scope) { + $scope.snippet = + 'Pretty text with some links:\n'+ + 'http://angularjs.org/,\n'+ + 'mailto:us@somewhere.org,\n'+ + 'another@somewhere.org,\n'+ + 'and one more: ftp://127.0.0.1/.'; + $scope.snippetWithSingleURL = 'http://angularjs.org/'; + }]); + + + it('should linkify the snippet with urls', function() { + expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). + toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' + + 'another@somewhere.org, and one more: ftp://127.0.0.1/.'); + expect(element.all(by.css('#linky-filter a')).count()).toEqual(4); + }); + + it('should not linkify snippet without the linky filter', function() { + expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()). + toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' + + 'another@somewhere.org, and one more: ftp://127.0.0.1/.'); + expect(element.all(by.css('#escaped-html a')).count()).toEqual(0); + }); + + it('should update', function() { + element(by.model('snippet')).clear(); + element(by.model('snippet')).sendKeys('new http://link.'); + expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). + toBe('new http://link.'); + expect(element.all(by.css('#linky-filter a')).count()).toEqual(1); + expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()) + .toBe('new http://link.'); + }); + + it('should work with the target property', function() { + expect(element(by.id('linky-target')). + element(by.binding("snippetWithSingleURL | linky:'_blank'")).getText()). + toBe('http://angularjs.org/'); + expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank'); + }); + + it('should optionally add custom attributes', function() { + expect(element(by.id('linky-custom-attributes')). + element(by.binding("snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}")).getText()). + toBe('http://angularjs.org/'); + expect(element(by.css('#linky-custom-attributes a')).getAttribute('rel')).toEqual('nofollow'); + }); + + + */ +angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) { + var LINKY_URL_REGEXP = + /((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i, + MAILTO_REGEXP = /^mailto:/i; + + var linkyMinErr = angular.$$minErr('linky'); + var isString = angular.isString; + + return function(text, target, attributes) { + if (text == null || text === '') return text; + if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text); + + var match; + var raw = text; + var html = []; + var url; + var i; + while ((match = raw.match(LINKY_URL_REGEXP))) { + // We can not end in these as they are sometimes found at the end of the sentence + url = match[0]; + // if we did not match ftp/http/www/mailto then assume mailto + if (!match[2] && !match[4]) { + url = (match[3] ? 'http://' : 'mailto:') + url; + } + i = match.index; + addText(raw.substr(0, i)); + addLink(url, match[0].replace(MAILTO_REGEXP, '')); + raw = raw.substring(i + match[0].length); + } + addText(raw); + return $sanitize(html.join('')); + + function addText(text) { + if (!text) { + return; + } + html.push(sanitizeText(text)); + } + + function addLink(url, text) { + var key; + html.push(''); + addText(text); + html.push(''); + } + }; +}]); + + +})(window, window.angular); diff --git a/ecomp-portal-FE/client/bower_components/angular-sanitize/angular-sanitize.min.js b/ecomp-portal-FE/client/bower_components/angular-sanitize/angular-sanitize.min.js new file mode 100644 index 00000000..135d5a0e --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-sanitize/angular-sanitize.min.js @@ -0,0 +1,15 @@ +/* + AngularJS v1.5.0 + (c) 2010-2016 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(A,e,B){'use strict';function C(a){var c=[];v(c,e.noop).chars(a);return c.join("")}function h(a,c){var b={},d=a.split(","),l;for(l=0;l=document.documentMode&&n(g);a=g.innerHTML;g.innerHTML=a}while(a!==g.innerHTML);for(b=g.firstChild;b;){switch(b.nodeType){case 1:c.start(b.nodeName.toLowerCase(),E(b.attributes)); +break;case 3:c.chars(b.textContent)}var d;if(!(d=b.firstChild)&&(1==b.nodeType&&c.end(b.nodeName.toLowerCase()),d=b.nextSibling,!d))for(;null==d;){b=b.parentNode;if(b===g)break;d=b.nextSibling;1==b.nodeType&&c.end(b.nodeName.toLowerCase())}b=d}for(;b=g.firstChild;)g.removeChild(b)}function E(a){for(var c={},b=0,d=a.length;b/g,">")}function v(a,c){var b=!1,d=e.bind(a,a.push);return{start:function(a,f){a=e.lowercase(a);!b&&H[a]&&(b=a);b||!0!==t[a]||(d("<"),d(a),e.forEach(f,function(b,f){var g=e.lowercase(f),h="img"===a&&"src"===g||"background"===g;!0!==I[g]||!0===y[g]&&!c(b,h)||(d(" "),d(f),d('="'),d(x(b)),d('"'))}),d(">"))},end:function(a){a=e.lowercase(a);b||!0!==t[a]||!0===z[a]||(d(""));a== +b&&(b=!1)},chars:function(a){b||d(x(a))}}}function n(a){if(a.nodeType===Node.ELEMENT_NODE)for(var c=a.attributes,b=0,d=c.length;b"\u201d\u2019]/i,b=/^mailto:/i,d=e.$$minErr("linky"),g=e.isString;return function(f,h,m){function k(a){a&&p.push(C(a))}function q(a,b){var c;p.push("');k(b);p.push("")}if(null==f||""===f)return f;if(!g(f))throw d("notstring",f);for(var r=f,p=[],s,n;f=r.match(c);)s=f[0],f[2]||f[4]||(s=(f[3]?"http://":"mailto:")+s),n=f.index,k(r.substr(0,n)),q(s,f[0].replace(b,"")),r=r.substring(n+f[0].length);k(r);return a(p.join(""))}}])})(window,window.angular); +//# sourceMappingURL=angular-sanitize.min.js.map diff --git a/ecomp-portal-FE/client/bower_components/angular-sanitize/angular-sanitize.min.js.map b/ecomp-portal-FE/client/bower_components/angular-sanitize/angular-sanitize.min.js.map new file mode 100644 index 00000000..7276abd2 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-sanitize/angular-sanitize.min.js.map @@ -0,0 +1,8 @@ +{ +"version":3, +"file":"angular-sanitize.min.js", +"lineCount":14, +"mappings":"A;;;;;aAKC,SAAQ,CAACA,CAAD,CAASC,CAAT,CAAkBC,CAAlB,CAA6B,CAsMtCC,QAASA,EAAY,CAACC,CAAD,CAAQ,CAC3B,IAAIC,EAAM,EACGC,EAAAC,CAAmBF,CAAnBE,CAAwBN,CAAAO,KAAxBD,CACbH,MAAA,CAAaA,CAAb,CACA,OAAOC,EAAAI,KAAA,CAAS,EAAT,CAJoB,CAyF7BC,QAASA,EAAK,CAACC,CAAD,CAAMC,CAAN,CAAqB,CAAA,IAC7BC,EAAM,EADuB,CACnBC,EAAQH,CAAAI,MAAA,CAAU,GAAV,CADW,CACKC,CACtC,KAAKA,CAAL,CAAS,CAAT,CAAYA,CAAZ,CAAgBF,CAAAG,OAAhB,CAA8BD,CAAA,EAA9B,CACEH,CAAA,CAAID,CAAA,CAAgBX,CAAAiB,UAAA,CAAkBJ,CAAA,CAAME,CAAN,CAAlB,CAAhB,CAA8CF,CAAA,CAAME,CAAN,CAAlD,CAAA,CAA8D,CAAA,CAEhE,OAAOH,EAL0B,CA0CnCM,QAASA,EAAU,CAACC,CAAD,CAAOC,CAAP,CAAgB,CACpB,IAAb,GAAID,CAAJ,EAAqBA,CAArB,GAA8BlB,CAA9B,CACEkB,CADF,CACS,EADT,CAE2B,QAF3B,GAEW,MAAOA,EAFlB,GAGEA,CAHF,CAGS,EAHT,CAGcA,CAHd,CAKAE,EAAAC,UAAA,CAA6BH,CAG7B,KAAII,EAAe,CACnB,GAAG,CACD,GAAqB,CAArB,GAAIA,CAAJ,CACE,KAAMC,EAAA,CAAgB,QAAhB,CAAN,CAEFD,CAAA,EAG6B,GAA7B,EAAIE,QAAAC,aAAJ,EACEC,CAAA,CAAmBN,CAAnB,CAEFF,EAAA,CAAOE,CAAAC,UACPD,EAAAC,UAAA,CAA6BH,CAX5B,CAAH,MAYSA,CAZT,GAYkBE,CAAAC,UAZlB,CAeA,KADIM,CACJ,CADWP,CAAAQ,WACX,CAAOD,CAAP,CAAA,CAAa,CACX,OAAQA,CAAAE,SAAR,EACE,KAAK,CAAL,CACEV,CAAAW,MAAA,CAAcH,CAAAI,SAAAC,YAAA,EAAd,CAA2CC,CAAA,CAAUN,CAAAO,WAAV,CAA3C,CACA;KACF,MAAK,CAAL,CACEf,CAAAjB,MAAA,CAAcyB,CAAAQ,YAAd,CALJ,CASA,IAAIC,CACJ,IAAM,EAAAA,CAAA,CAAWT,CAAAC,WAAX,CAAN,GACuB,CAIhBQ,EAJDT,CAAAE,SAICO,EAHHjB,CAAAkB,IAAA,CAAYV,CAAAI,SAAAC,YAAA,EAAZ,CAGGI,CADLA,CACKA,CADMT,CAAAW,YACNF,CAAAA,CAAAA,CALP,EAMI,IAAA,CAAmB,IAAnB,EAAOA,CAAP,CAAA,CAAyB,CACvBT,CAAA,CAAOA,CAAAY,WACP,IAAIZ,CAAJ,GAAaP,CAAb,CAA+B,KAC/BgB,EAAA,CAAWT,CAAAW,YACU,EAArB,EAAIX,CAAAE,SAAJ,EACEV,CAAAkB,IAAA,CAAYV,CAAAI,SAAAC,YAAA,EAAZ,CALqB,CAU7BL,CAAA,CAAOS,CA3BI,CA8Bb,IAAA,CAAOT,CAAP,CAAcP,CAAAQ,WAAd,CAAA,CACER,CAAAoB,YAAA,CAA6Bb,CAA7B,CAxD+B,CA4DnCM,QAASA,EAAS,CAACQ,CAAD,CAAQ,CAExB,IADA,IAAIC,EAAM,EAAV,CACS5B,EAAI,CADb,CACgB6B,EAAKF,CAAA1B,OAArB,CAAmCD,CAAnC,CAAuC6B,CAAvC,CAA2C7B,CAAA,EAA3C,CAAgD,CAC9C,IAAI8B,EAAOH,CAAA,CAAM3B,CAAN,CACX4B,EAAA,CAAIE,CAAAC,KAAJ,CAAA,CAAiBD,CAAAE,MAF6B,CAIhD,MAAOJ,EANiB,CAiB1BK,QAASA,EAAc,CAACD,CAAD,CAAQ,CAC7B,MAAOA,EAAAE,QAAA,CACG,IADH,CACS,OADT,CAAAA,QAAA,CAEGC,CAFH,CAE0B,QAAQ,CAACH,CAAD,CAAQ,CAC7C,IAAII,EAAKJ,CAAAK,WAAA,CAAiB,CAAjB,CACLC,EAAAA,CAAMN,CAAAK,WAAA,CAAiB,CAAjB,CACV,OAAO,IAAP,EAAgC,IAAhC,EAAiBD,CAAjB,CAAsB,KAAtB;CAA0CE,CAA1C,CAAgD,KAAhD,EAA0D,KAA1D,EAAqE,GAHxB,CAF1C,CAAAJ,QAAA,CAOGK,CAPH,CAO4B,QAAQ,CAACP,CAAD,CAAQ,CAC/C,MAAO,IAAP,CAAcA,CAAAK,WAAA,CAAiB,CAAjB,CAAd,CAAoC,GADW,CAP5C,CAAAH,QAAA,CAUG,IAVH,CAUS,MAVT,CAAAA,QAAA,CAWG,IAXH,CAWS,MAXT,CADsB,CAyB/B5C,QAASA,EAAkB,CAACD,CAAD,CAAMmD,CAAN,CAAoB,CAC7C,IAAIC,EAAuB,CAAA,CAA3B,CACIC,EAAMzD,CAAA0D,KAAA,CAAatD,CAAb,CAAkBA,CAAAuD,KAAlB,CACV,OAAO,CACL5B,MAAOA,QAAQ,CAAC6B,CAAD,CAAMlB,CAAN,CAAa,CAC1BkB,CAAA,CAAM5D,CAAAiB,UAAA,CAAkB2C,CAAlB,CACDJ,EAAAA,CAAL,EAA6BK,CAAA,CAAgBD,CAAhB,CAA7B,GACEJ,CADF,CACyBI,CADzB,CAGKJ,EAAL,EAAoD,CAAA,CAApD,GAA6BM,CAAA,CAAcF,CAAd,CAA7B,GACEH,CAAA,CAAI,GAAJ,CAcA,CAbAA,CAAA,CAAIG,CAAJ,CAaA,CAZA5D,CAAA+D,QAAA,CAAgBrB,CAAhB,CAAuB,QAAQ,CAACK,CAAD,CAAQiB,CAAR,CAAa,CAC1C,IAAIC,EAAKjE,CAAAiB,UAAA,CAAkB+C,CAAlB,CAAT,CACIE,EAAmB,KAAnBA,GAAWN,CAAXM,EAAqC,KAArCA,GAA4BD,CAA5BC,EAAyD,YAAzDA,GAAgDD,CAC3B,EAAA,CAAzB,GAAIE,CAAA,CAAWF,CAAX,CAAJ,EACsB,CAAA,CADtB,GACGG,CAAA,CAASH,CAAT,CADH,EAC8B,CAAAV,CAAA,CAAaR,CAAb,CAAoBmB,CAApB,CAD9B,GAEET,CAAA,CAAI,GAAJ,CAIA,CAHAA,CAAA,CAAIO,CAAJ,CAGA,CAFAP,CAAA,CAAI,IAAJ,CAEA,CADAA,CAAA,CAAIT,CAAA,CAAeD,CAAf,CAAJ,CACA,CAAAU,CAAA,CAAI,GAAJ,CANF,CAH0C,CAA5C,CAYA,CAAAA,CAAA,CAAI,GAAJ,CAfF,CAL0B,CADvB,CAwBLnB,IAAKA,QAAQ,CAACsB,CAAD,CAAM,CACjBA,CAAA,CAAM5D,CAAAiB,UAAA,CAAkB2C,CAAlB,CACDJ,EAAL,EAAoD,CAAA,CAApD,GAA6BM,CAAA,CAAcF,CAAd,CAA7B,EAAkF,CAAA,CAAlF,GAA4DS,CAAA,CAAaT,CAAb,CAA5D,GACEH,CAAA,CAAI,IAAJ,CAEA,CADAA,CAAA,CAAIG,CAAJ,CACA,CAAAH,CAAA,CAAI,GAAJ,CAHF,CAKIG,EAAJ;AAAWJ,CAAX,GACEA,CADF,CACyB,CAAA,CADzB,CAPiB,CAxBd,CAmCLrD,MAAOA,QAAQ,CAACA,CAAD,CAAQ,CAChBqD,CAAL,EACEC,CAAA,CAAIT,CAAA,CAAe7C,CAAf,CAAJ,CAFmB,CAnClB,CAHsC,CAsD/CwB,QAASA,EAAkB,CAACC,CAAD,CAAO,CAChC,GAAIA,CAAAE,SAAJ,GAAsBwC,IAAAC,aAAtB,CAEE,IADA,IAAI7B,EAAQd,CAAAO,WAAZ,CACSpB,EAAI,CADb,CACgByD,EAAI9B,CAAA1B,OAApB,CAAkCD,CAAlC,CAAsCyD,CAAtC,CAAyCzD,CAAA,EAAzC,CAA8C,CAC5C,IAAI0D,EAAW/B,CAAA,CAAM3B,CAAN,CAAf,CACI2D,EAAWD,CAAA3B,KAAAb,YAAA,EACf,IAAiB,WAAjB,GAAIyC,CAAJ,EAA6D,CAA7D,GAAgCA,CAAAC,QAAA,CAAiB,MAAjB,CAAhC,CACE/C,CAAAgD,oBAAA,CAAyBH,CAAzB,CAEA,CADA1D,CAAA,EACA,CAAAyD,CAAA,EAN0C,CAYhD,CADInC,CACJ,CADeT,CAAAC,WACf,GACEF,CAAA,CAAmBU,CAAnB,CAIF,EADAA,CACA,CADWT,CAAAW,YACX,GACEZ,CAAA,CAAmBU,CAAnB,CArB8B,CAxdlC,IAAIb,EAAkBxB,CAAA6E,SAAA,CAAiB,WAAjB,CAAtB,CAkMI3B,EAAwB,iCAlM5B,CAoMEI,EAA0B,eApM5B,CA6MIe,EAAe5D,CAAA,CAAM,wBAAN,CA7MnB,CAiNIqE,EAA8BrE,CAAA,CAAM,gDAAN,CAjNlC,CAkNIsE,EAA+BtE,CAAA,CAAM,OAAN,CAlNnC,CAmNIuE,EAAyBhF,CAAAiF,OAAA,CAAe,EAAf,CACeF,CADf,CAEeD,CAFf,CAnN7B,CAwNII,EAAgBlF,CAAAiF,OAAA,CAAe,EAAf;AAAmBH,CAAnB,CAAgDrE,CAAA,CAAM,qKAAN,CAAhD,CAxNpB,CA6NI0E,EAAiBnF,CAAAiF,OAAA,CAAe,EAAf,CAAmBF,CAAnB,CAAiDtE,CAAA,CAAM,2JAAN,CAAjD,CA7NrB,CAqOI2E,EAAc3E,CAAA,CAAM,wNAAN,CArOlB;AA0OIoD,EAAkBpD,CAAA,CAAM,cAAN,CA1OtB,CA4OIqD,EAAgB9D,CAAAiF,OAAA,CAAe,EAAf,CACeZ,CADf,CAEea,CAFf,CAGeC,CAHf,CAIeH,CAJf,CA5OpB,CAmPIZ,EAAW3D,CAAA,CAAM,8CAAN,CAnPf,CAqPI4E,EAAY5E,CAAA,CAAM,kTAAN,CArPhB,CA6PI6E,EAAW7E,CAAA,CAAM,guCAAN;AAcoE,CAAA,CAdpE,CA7Pf,CA6QI0D,EAAanE,CAAAiF,OAAA,CAAe,EAAf,CACeb,CADf,CAEekB,CAFf,CAGeD,CAHf,CA7QjB,CA0RIhE,CACH,UAAQ,CAACtB,CAAD,CAAS,CAEhB,GAAIA,CAAA0B,SAAJ,EAAuB1B,CAAA0B,SAAA8D,eAAvB,CACEC,CAAA,CAAMzF,CAAA0B,SAAA8D,eAAAE,mBAAA,CAAkD,OAAlD,CADR,KAGE,MAAMjE,EAAA,CAAgB,SAAhB,CAAN,CAGF,IAAIkE,EAAeC,CADFH,CAAAI,gBACED,EADqBH,CAAAK,mBAAA,EACrBF,sBAAA,CAAgC,MAAhC,CAGS,EAA5B,GAAID,CAAA1E,OAAJ,CACEK,CADF,CACqBqE,CAAA,CAAa,CAAb,CADrB,EAGMvE,CAGJ,CAHWqE,CAAAM,cAAA,CAAkB,MAAlB,CAGX,CAFAzE,CAEA,CAFmBmE,CAAAM,cAAA,CAAkB,MAAlB,CAEnB,CADA3E,CAAA4E,YAAA,CAAiB1E,CAAjB,CACA,CAAAmE,CAAAO,YAAA,CAAgB5E,CAAhB,CANF,CAXgB,CAAjB,CAAD,CAmBGpB,CAnBH,CAyNAC,EAAAgG,OAAA,CAAe,YAAf,CAA6B,EAA7B,CAAAC,SAAA,CAA0C,WAA1C,CApXAC,QAA0B,EAAG,CAC3B,IAAIC,EAAa,CAAA,CAEjB,KAAAC,KAAA,CAAY,CAAC,eAAD,CAAkB,QAAQ,CAACC,CAAD,CAAgB,CAChDF,CAAJ,EACEnG,CAAAiF,OAAA,CAAenB,CAAf,CAA8BsB,CAA9B,CAEF,OAAO,SAAQ,CAACjE,CAAD,CAAO,CACpB,IAAIf;AAAM,EACVc,EAAA,CAAWC,CAAX,CAAiBd,CAAA,CAAmBD,CAAnB,CAAwB,QAAQ,CAACkG,CAAD,CAAMpC,CAAN,CAAe,CAC9D,MAAO,CAAC,UAAAqC,KAAA,CAAgBF,CAAA,CAAcC,CAAd,CAAmBpC,CAAnB,CAAhB,CADsD,CAA/C,CAAjB,CAGA,OAAO9D,EAAAI,KAAA,CAAS,EAAT,CALa,CAJ8B,CAA1C,CA4CZ,KAAAgG,UAAA,CAAiBC,QAAQ,CAACD,CAAD,CAAY,CACnC,MAAIxG,EAAA0G,UAAA,CAAkBF,CAAlB,CAAJ,EACEL,CACO,CADMK,CACN,CAAA,IAFT,EAISL,CAL0B,CA/CV,CAoX7B,CAmIAnG,EAAAgG,OAAA,CAAe,YAAf,CAAAW,OAAA,CAAoC,OAApC,CAA6C,CAAC,WAAD,CAAc,QAAQ,CAACC,CAAD,CAAY,CAAA,IACzEC,EACE,yFAFuE,CAGzEC,EAAgB,WAHyD,CAKzEC,EAAc/G,CAAA6E,SAAA,CAAiB,OAAjB,CAL2D,CAMzEmC,EAAWhH,CAAAgH,SAEf,OAAO,SAAQ,CAACC,CAAD,CAAOC,CAAP,CAAe/E,CAAf,CAA2B,CAwBxCgF,QAASA,EAAO,CAACF,CAAD,CAAO,CAChBA,CAAL,EAGA9F,CAAAwC,KAAA,CAAUzD,CAAA,CAAa+G,CAAb,CAAV,CAJqB,CAOvBG,QAASA,EAAO,CAACC,CAAD,CAAMJ,CAAN,CAAY,CAC1B,IAAIjD,CACJ7C,EAAAwC,KAAA,CAAU,KAAV,CACI3D,EAAAsH,WAAA,CAAmBnF,CAAnB,CAAJ,GACEA,CADF,CACeA,CAAA,CAAWkF,CAAX,CADf,CAGA,IAAIrH,CAAAuH,SAAA,CAAiBpF,CAAjB,CAAJ,CACE,IAAK6B,CAAL,GAAY7B,EAAZ,CACEhB,CAAAwC,KAAA,CAAUK,CAAV;AAAgB,IAAhB,CAAuB7B,CAAA,CAAW6B,CAAX,CAAvB,CAAyC,IAAzC,CAFJ,KAKE7B,EAAA,CAAa,EAEX,EAAAnC,CAAA0G,UAAA,CAAkBQ,CAAlB,CAAJ,EAAmC,QAAnC,EAA+C/E,EAA/C,EACEhB,CAAAwC,KAAA,CAAU,UAAV,CACUuD,CADV,CAEU,IAFV,CAIF/F,EAAAwC,KAAA,CAAU,QAAV,CACU0D,CAAApE,QAAA,CAAY,IAAZ,CAAkB,QAAlB,CADV,CAEU,IAFV,CAGAkE,EAAA,CAAQF,CAAR,CACA9F,EAAAwC,KAAA,CAAU,MAAV,CAtB0B,CA9B5B,GAAY,IAAZ,EAAIsD,CAAJ,EAA6B,EAA7B,GAAoBA,CAApB,CAAiC,MAAOA,EACxC,IAAK,CAAAD,CAAA,CAASC,CAAT,CAAL,CAAqB,KAAMF,EAAA,CAAY,WAAZ,CAA8DE,CAA9D,CAAN,CAOrB,IAJA,IAAIO,EAAMP,CAAV,CACI9F,EAAO,EADX,CAEIkG,CAFJ,CAGItG,CACJ,CAAQ0G,CAAR,CAAgBD,CAAAC,MAAA,CAAUZ,CAAV,CAAhB,CAAA,CAEEQ,CAQA,CARMI,CAAA,CAAM,CAAN,CAQN,CANKA,CAAA,CAAM,CAAN,CAML,EANkBA,CAAA,CAAM,CAAN,CAMlB,GALEJ,CAKF,EALSI,CAAA,CAAM,CAAN,CAAA,CAAW,SAAX,CAAuB,SAKhC,EAL6CJ,CAK7C,EAHAtG,CAGA,CAHI0G,CAAAC,MAGJ,CAFAP,CAAA,CAAQK,CAAAG,OAAA,CAAW,CAAX,CAAc5G,CAAd,CAAR,CAEA,CADAqG,CAAA,CAAQC,CAAR,CAAaI,CAAA,CAAM,CAAN,CAAAxE,QAAA,CAAiB6D,CAAjB,CAAgC,EAAhC,CAAb,CACA,CAAAU,CAAA,CAAMA,CAAAI,UAAA,CAAc7G,CAAd,CAAkB0G,CAAA,CAAM,CAAN,CAAAzG,OAAlB,CAERmG,EAAA,CAAQK,CAAR,CACA,OAAOZ,EAAA,CAAUzF,CAAAX,KAAA,CAAU,EAAV,CAAV,CAtBiC,CARmC,CAAlC,CAA7C,CApoBsC,CAArC,CAAD,CAusBGT,MAvsBH,CAusBWA,MAAAC,QAvsBX;", +"sources":["angular-sanitize.js"], +"names":["window","angular","undefined","sanitizeText","chars","buf","htmlSanitizeWriter","writer","noop","join","toMap","str","lowercaseKeys","obj","items","split","i","length","lowercase","htmlParser","html","handler","inertBodyElement","innerHTML","mXSSAttempts","$sanitizeMinErr","document","documentMode","stripCustomNsAttrs","node","firstChild","nodeType","start","nodeName","toLowerCase","attrToMap","attributes","textContent","nextNode","end","nextSibling","parentNode","removeChild","attrs","map","ii","attr","name","value","encodeEntities","replace","SURROGATE_PAIR_REGEXP","hi","charCodeAt","low","NON_ALPHANUMERIC_REGEXP","uriValidator","ignoreCurrentElement","out","bind","push","tag","blockedElements","validElements","forEach","key","lkey","isImage","validAttrs","uriAttrs","voidElements","Node","ELEMENT_NODE","l","attrNode","attrName","indexOf","removeAttributeNode","$$minErr","optionalEndTagBlockElements","optionalEndTagInlineElements","optionalEndTagElements","extend","blockElements","inlineElements","svgElements","htmlAttrs","svgAttrs","implementation","doc","createHTMLDocument","bodyElements","getElementsByTagName","documentElement","getDocumentElement","createElement","appendChild","module","provider","$SanitizeProvider","svgEnabled","$get","$$sanitizeUri","uri","test","enableSvg","this.enableSvg","isDefined","filter","$sanitize","LINKY_URL_REGEXP","MAILTO_REGEXP","linkyMinErr","isString","text","target","addText","addLink","url","isFunction","isObject","raw","match","index","substr","substring"] +} diff --git a/ecomp-portal-FE/client/bower_components/angular-sanitize/bower.json b/ecomp-portal-FE/client/bower_components/angular-sanitize/bower.json new file mode 100644 index 00000000..3f31319a --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-sanitize/bower.json @@ -0,0 +1,10 @@ +{ + "name": "angular-sanitize", + "version": "1.5.0", + "license": "MIT", + "main": "./angular-sanitize.js", + "ignore": [], + "dependencies": { + "angular": "1.5.0" + } +} diff --git a/ecomp-portal-FE/client/bower_components/angular-sanitize/index.js b/ecomp-portal-FE/client/bower_components/angular-sanitize/index.js new file mode 100644 index 00000000..dd5d22e4 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-sanitize/index.js @@ -0,0 +1,2 @@ +require('./angular-sanitize'); +module.exports = 'ngSanitize'; diff --git a/ecomp-portal-FE/client/bower_components/angular-sanitize/package.json b/ecomp-portal-FE/client/bower_components/angular-sanitize/package.json new file mode 100644 index 00000000..3f706558 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-sanitize/package.json @@ -0,0 +1,26 @@ +{ + "name": "angular-sanitize", + "version": "1.5.0", + "description": "AngularJS module for sanitizing HTML", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/angular/angular.js.git" + }, + "keywords": [ + "angular", + "framework", + "browser", + "html", + "client-side" + ], + "author": "Angular Core Team ", + "license": "MIT", + "bugs": { + "url": "https://github.com/angular/angular.js/issues" + }, + "homepage": "http://angularjs.org" +} diff --git a/ecomp-portal-FE/client/bower_components/angular-scenario/.bower.json b/ecomp-portal-FE/client/bower_components/angular-scenario/.bower.json new file mode 100644 index 00000000..daa21e1c --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-scenario/.bower.json @@ -0,0 +1,20 @@ +{ + "name": "angular-scenario", + "version": "1.5.11", + "license": "MIT", + "main": "./angular-scenario.js", + "ignore": [], + "dependencies": { + "angular": "1.5.11" + }, + "homepage": "https://github.com/angular/bower-angular-scenario", + "_release": "1.5.11", + "_resolution": { + "type": "version", + "tag": "v1.5.11", + "commit": "477d9520de0f6eeadd0d97b7fa820b7c111ba572" + }, + "_source": "https://github.com/angular/bower-angular-scenario.git", + "_target": "~1.5.0", + "_originalSource": "angular-scenario" +} \ No newline at end of file diff --git a/ecomp-portal-FE/client/bower_components/angular-scenario/LICENSE.md b/ecomp-portal-FE/client/bower_components/angular-scenario/LICENSE.md new file mode 100644 index 00000000..2c395eef --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-scenario/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Angular + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ecomp-portal-FE/client/bower_components/angular-scenario/README.md b/ecomp-portal-FE/client/bower_components/angular-scenario/README.md new file mode 100644 index 00000000..a4a00534 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-scenario/README.md @@ -0,0 +1,61 @@ +# packaged angular-scenario + +This tool is now in maintenance mode. If you are starting a new project, please use +[Protractor](https://github.com/angular/protractor). Existing projects using scenario runner are +advised to migrate to protractor, as this tool is unlikely to receive updates. + +This repo is for distribution on `npm` and `bower`. The source for this module is in the +[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngScenario). +Please file issues and pull requests against that repo. + +## Install + +You can install this package either with `npm` or with `bower`. + +### npm + +```shell +npm install angular-scenario +``` + +The files are then available at `node_modules/angular-scenario/`. + +Note that this package is not in CommonJS format, so doing `require('angular-scenario')` will +return `undefined`. + +### bower + +```shell +bower install angular-scenario +``` + +The files are then available at `bower_components/angular-scenario/`. + +## Documentation + +Documentation is available on the +[AngularJS docs site](http://docs.angularjs.org/). + +## License + +The MIT License + +Copyright (c) 2010-2015 Google, Inc. http://angularjs.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ecomp-portal-FE/client/bower_components/angular-scenario/angular-scenario.js b/ecomp-portal-FE/client/bower_components/angular-scenario/angular-scenario.js new file mode 100644 index 00000000..d9fca0b1 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/angular-scenario/angular-scenario.js @@ -0,0 +1,44994 @@ +/*eslint-disable no-unused-vars*/ +/*! + * jQuery JavaScript Library v3.1.0 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2016-07-07T21:44Z + */ +( function( global, factory ) { + +if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. + +var arr = []; + +var document = window.document; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + + + + function DOMEval( code, doc ) { + doc = doc || document; + + var script = doc.createElement( "script" ); + + script.text = code; + doc.head.appendChild( script ).parentNode.removeChild( script ); + } +/* global Symbol */ +// Defining this global in .eslintrc would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.1.0", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android <=4.0 only + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = jQuery.isArray( copy ) ) ) ) { + + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray( src ) ? src : []; + + } else { + clone = src && jQuery.isPlainObject( src ) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isFunction: function( obj ) { + return jQuery.type( obj ) === "function"; + }, + + isArray: Array.isArray, + + isWindow: function( obj ) { + return obj != null && obj === obj.window; + }, + + isNumeric: function( obj ) { + + // As of jQuery 3.0, isNumeric is limited to + // strings and numbers (primitives or objects) + // that can be coerced to finite numbers (gh-2662) + var type = jQuery.type( obj ); + return ( type === "number" || type === "string" ) && + + // parseFloat NaNs numeric-cast false positives ("") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + !isNaN( obj - parseFloat( obj ) ); + }, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + + /* eslint-disable no-unused-vars */ + // See https://github.com/eslint/eslint/issues/6125 + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + DOMEval( code ); + }, + + // Convert dashed to camelCase; used by the css and data modules + // Support: IE <=9 - 11, Edge 12 - 13 + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android <=4.0 only + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: Date.now, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.0 + * https://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2016-01-04 + */ +(function( window ) {'use strict'; + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + disabledAncestor = addCombinator( + function( elem ) { + return elem.disabled === true; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !compilerCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + + if ( nodeType !== 1 ) { + newContext = context; + newSelector = selector; + + // qSA looks outside Element context, which is not what we want + // Thanks to Andrew Dupont for this workaround technique + // Support: IE <=8 + // Exclude object elements + } else if ( context.nodeName.toLowerCase() !== "object" ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[i] = "#" + nid + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement("fieldset"); + + try { + return !!fn( el ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + // Known :disabled false positives: + // IE: *[disabled]:not(button, input, select, textarea, optgroup, option, menuitem, fieldset) + // not IE: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Check form elements and option elements for explicit disabling + return "label" in elem && elem.disabled === disabled || + "form" in elem && elem.disabled === disabled || + + // Check non-disabled form elements for fieldset[disabled] ancestors + "form" in elem && elem.disabled === false && ( + // Support: IE6-11+ + // Ancestry is covered for us + elem.isDisabled === disabled || + + // Otherwise, assume any non-