├── .bowerrc ├── .gitignore ├── Gruntfile.js ├── LICENSE ├── README.md ├── TODO.md ├── WebContent ├── app-built.html ├── app.html ├── css │ ├── bootstrap │ │ ├── accordion.less │ │ ├── alerts.less │ │ ├── bootstrap.less │ │ ├── breadcrumbs.less │ │ ├── button-groups.less │ │ ├── buttons-custom.less │ │ ├── buttons.less │ │ ├── carousel.less │ │ ├── close.less │ │ ├── code.less │ │ ├── component-animations.less │ │ ├── dropdowns.less │ │ ├── forms.less │ │ ├── grid.less │ │ ├── hero-unit.less │ │ ├── labels-badges.less │ │ ├── layouts.less │ │ ├── media.less │ │ ├── mixins.less │ │ ├── modals.less │ │ ├── navbar.less │ │ ├── navs.less │ │ ├── pager.less │ │ ├── pagination.less │ │ ├── popovers.less │ │ ├── progress-bars.less │ │ ├── reset.less │ │ ├── responsive-1200px-min.less │ │ ├── responsive-767px-max.less │ │ ├── responsive-768px-979px.less │ │ ├── responsive-navbar.less │ │ ├── responsive-utilities.less │ │ ├── responsive.less │ │ ├── scaffolding.less │ │ ├── sprites.less │ │ ├── tables.less │ │ ├── thumbnails.less │ │ ├── tooltip.less │ │ ├── type.less │ │ ├── utilities.less │ │ ├── variables.less │ │ └── wells.less │ ├── custom │ │ ├── overrides.less │ │ ├── sprites.less │ │ ├── style-BAK1.less │ │ ├── style.less │ │ └── variables.less │ ├── ie8.css │ ├── images │ │ ├── activity-spinner-16-combined.gif │ │ ├── bitmap16-white.png │ │ ├── bitmap16.png │ │ ├── bitmap40-linkColor.png │ │ ├── bitmap40-linkColorHover.png │ │ ├── bitmap40-white.png │ │ ├── bitmap40.png │ │ ├── glyphicons-halflings-white.png │ │ ├── glyphicons-halflings.png │ │ ├── loading.gif │ │ ├── x_x5F_alt18.png │ │ └── x_x5F_alt23.png │ ├── jquery.qtip-2.0.1-108.css │ ├── jquery.qtip-2.0.1-57.css │ ├── ng-grid.css │ └── style.less └── scripts │ ├── app │ ├── constants.js │ ├── main │ │ ├── main.js │ │ ├── navbar.html │ │ └── navbarCtrl.js │ ├── modules │ │ ├── categories │ │ │ ├── categoriesCtrl.js │ │ │ ├── categoriesTemplate.html │ │ │ ├── categoryDirective.js │ │ │ ├── categoryTemplate.html │ │ │ ├── deleteTemplate.html │ │ │ ├── main.js │ │ │ └── main.metadata.json │ │ ├── expenses │ │ │ ├── chartPopup.tpl.html │ │ │ ├── chartPopupModule.js │ │ │ ├── chartPopupService.js │ │ │ ├── expensesCtrl.js │ │ │ ├── expensesTemplate.html │ │ │ ├── main.js │ │ │ ├── main.metadata.json │ │ │ └── pieChartDirective.js │ │ └── index │ │ │ ├── indexCtrl.js │ │ │ ├── indexTemplate.html │ │ │ ├── main.js │ │ │ └── main.metadata.json │ └── shared │ │ ├── dao │ │ ├── categoriesDao.js │ │ ├── expensesDao.js │ │ └── userDao.js │ │ └── model │ │ └── Expense.js │ ├── empty.js │ ├── globals.js │ ├── lib │ ├── Chart.js │ └── lazyload.js │ ├── require-cfg.js │ └── util │ ├── lib │ └── angular-require-lazy │ │ ├── bootstrap.js │ │ ├── currentModule.js │ │ ├── lazyAngularUtils.js │ │ ├── ngLazy.js │ │ ├── promiseAdaptorAngular.js │ │ ├── routeConfig.js │ │ ├── templateCache.js │ │ ├── templateCacheBuilder.js │ │ └── util.js │ ├── loginPrompt.html │ ├── loginPrompt.js │ ├── menuEntries.js │ ├── modalTemplates.js │ ├── resourceUtils.js │ ├── returnService.js │ ├── urlUtils.js │ └── viewUtils.js ├── WebTests └── scripts │ ├── app │ ├── modules │ │ └── categories │ │ │ └── categoryDirective.spec.js │ └── shared │ │ └── dao │ │ └── expensesDao.spec.js │ ├── mocks │ ├── app │ │ └── shared │ │ │ └── dao │ │ │ └── categoriesDao.js │ ├── lazy-registry.js │ ├── require-cfg.js │ └── test-main.js │ └── test-main.js ├── app.js ├── bower.json ├── build-scripts ├── app.build-grunt.json ├── discoverModules.js ├── grunt │ └── grunt.js ├── karma │ ├── index.js │ ├── preInstrumentedPreprocessor.js │ └── reporter.js └── options-grunt.js ├── build-stats ├── .gitignore ├── amd.html ├── bundles.html ├── deps.html ├── f.js ├── index.html ├── modules.html └── stats.js ├── karma-coverage.conf.js ├── karma.conf.js ├── package.json └── sonar-project.properties /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "WebContent/scripts/lib", 3 | "json": "bower.json" 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .directory 2 | node_modules/* 3 | WebContent/scripts/lib/* 4 | !WebContent/scripts/lib/lazyload.js 5 | !WebContent/scripts/lib/Chart.js 6 | build/* 7 | /build-coverage/* 8 | /.sonar/ 9 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | var options = require("./build-scripts/options-grunt.js"), 3 | config = require("./build-scripts/app.build-grunt.json"); 4 | 5 | grunt.initConfig({ 6 | instrument: { 7 | sources: { 8 | files: [{ 9 | expand: true, 10 | cwd: "WebContent/", 11 | src: ["scripts/app/**/*.js", "scripts/util/**/*.js"], 12 | dest: "build-coverage/instrumented" 13 | }], 14 | options: { 15 | baseline: "build-coverage/baseline.json" 16 | } 17 | } 18 | }, 19 | copy: { 20 | images: { 21 | expand: true, 22 | cwd: "WebContent/css/", 23 | src: ["images/**"], 24 | dest: "build/css/" 25 | } 26 | }, 27 | less: { 28 | compile: { 29 | options: { 30 | yuicompress: true 31 | }, 32 | files: { 33 | "build/css/css/style.css": "WebContent/css/style.less" 34 | } 35 | } 36 | }, 37 | require_lazy_grunt: { 38 | options: { 39 | buildOptions: options, 40 | config: config, 41 | callback: function(modules, pmresult) { 42 | // This callback is optional; included here just for demonstration purposes. 43 | var fs = require("fs"), util = require("util"), path = require("path"); 44 | fs.writeFileSync(path.join(options.outputBaseDir+"-stats", "modules.js"), "angular.module(\"app\").value(\"modules\"," + util.inspect(modules,{depth:null,colors:false}) + ");"); 45 | fs.writeFileSync(path.join(options.outputBaseDir+"-stats", "bundles.js"), "angular.module(\"app\").value(\"bundles\"," + util.inspect(pmresult.bundles,{depth:null,colors:false}) + ");"); 46 | } 47 | } 48 | }, 49 | karma: { 50 | options: { 51 | configFile: "karma.conf.js" 52 | }, 53 | single: { 54 | singleRun: true, 55 | reporters: "dots" 56 | }, 57 | coverage: { 58 | singleRun: true, 59 | configFile: "karma-coverage.conf.js" 60 | } 61 | }, 62 | clean: ["build/*","build-coverage/*"] 63 | }); 64 | 65 | grunt.loadNpmTasks("grunt-contrib-copy"); 66 | grunt.loadNpmTasks("grunt-contrib-less"); 67 | grunt.loadNpmTasks("require-lazy-grunt"); 68 | grunt.loadNpmTasks("grunt-contrib-clean"); 69 | grunt.loadNpmTasks("grunt-karma"); 70 | grunt.loadTasks("build-scripts/grunt"); 71 | 72 | grunt.registerTask("default", ["less:compile","copy:images","require_lazy_grunt"]); 73 | grunt.registerTask("test", ["karma:single"]); 74 | grunt.registerTask("coverage", ["instrument","karma:coverage"]); 75 | }; 76 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Nikos Paraskevopoulos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | - Write the "How To Configure a Project With angular-require-lazy" guide 2 | - Investigate the implementation of routeConfig in terms of ngLazy 3 | -------------------------------------------------------------------------------- /WebContent/app-built.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Expenses 6 | 7 | 8 | 9 | 12 | 17 | 18 | 19 | 20 | 21 |
22 |
23 |
24 | 25 | 26 | 27 | 39 | 40 | 41 | 44 | 45 | 46 | 47 | 48 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /WebContent/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Expenses 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
28 | 29 | 30 | 31 | 34 | 35 | 36 | 37 | 38 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/accordion.less: -------------------------------------------------------------------------------- 1 | // 2 | // Accordion 3 | // -------------------------------------------------- 4 | 5 | 6 | // Parent container 7 | .accordion { 8 | margin-bottom: @baseLineHeight; 9 | } 10 | 11 | // Group == heading + body 12 | .accordion-group { 13 | margin-bottom: 2px; 14 | border: 1px solid #e5e5e5; 15 | .border-radius(@baseBorderRadius); 16 | } 17 | .accordion-heading { 18 | border-bottom: 0; 19 | } 20 | .accordion-heading .accordion-toggle { 21 | display: block; 22 | padding: 8px 15px; 23 | } 24 | 25 | // General toggle styles 26 | .accordion-toggle { 27 | cursor: pointer; 28 | } 29 | 30 | // Inner needs the styles because you can't animate properly with any styles on the element 31 | .accordion-inner { 32 | padding: 9px 15px; 33 | border-top: 1px solid #e5e5e5; 34 | } 35 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/alerts.less: -------------------------------------------------------------------------------- 1 | // 2 | // Alerts 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base styles 7 | // ------------------------- 8 | 9 | .alert { 10 | padding: 8px 35px 8px 14px; 11 | margin-bottom: @baseLineHeight; 12 | text-shadow: 0 1px 0 rgba(255,255,255,.5); 13 | background-color: @warningBackground; 14 | border: 1px solid @warningBorder; 15 | .border-radius(@baseBorderRadius); 16 | } 17 | .alert, 18 | .alert h4 { 19 | // Specified for the h4 to prevent conflicts of changing @headingsColor 20 | color: @warningText; 21 | } 22 | .alert h4 { 23 | margin: 0; 24 | } 25 | 26 | // Adjust close link position 27 | .alert .close { 28 | position: relative; 29 | top: -2px; 30 | right: -21px; 31 | line-height: @baseLineHeight; 32 | } 33 | 34 | 35 | // Alternate styles 36 | // ------------------------- 37 | 38 | .alert-success { 39 | background-color: @successBackground; 40 | border-color: @successBorder; 41 | color: @successText; 42 | } 43 | .alert-success h4 { 44 | color: @successText; 45 | } 46 | .alert-danger, 47 | .alert-error { 48 | background-color: @errorBackground; 49 | border-color: @errorBorder; 50 | color: @errorText; 51 | } 52 | .alert-danger h4, 53 | .alert-error h4 { 54 | color: @errorText; 55 | } 56 | .alert-info { 57 | background-color: @infoBackground; 58 | border-color: @infoBorder; 59 | color: @infoText; 60 | } 61 | .alert-info h4 { 62 | color: @infoText; 63 | } 64 | 65 | 66 | // Block alerts 67 | // ------------------------- 68 | 69 | .alert-block { 70 | padding-top: 14px; 71 | padding-bottom: 14px; 72 | } 73 | .alert-block > p, 74 | .alert-block > ul { 75 | margin-bottom: 0; 76 | } 77 | .alert-block p + p { 78 | margin-top: 5px; 79 | } 80 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/bootstrap.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v2.3.1 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */ 10 | 11 | // Core variables and mixins 12 | @import "variables.less"; // Modify this for custom colors, font-sizes, etc 13 | @import "mixins.less"; 14 | 15 | // CSS Reset 16 | @import "reset.less"; 17 | 18 | // Grid system and page structure 19 | @import "scaffolding.less"; 20 | @import "grid.less"; 21 | @import "layouts.less"; 22 | 23 | // Base CSS 24 | @import "type.less"; 25 | //@import "code.less"; 26 | @import "forms.less"; 27 | //@import "tables.less"; 28 | 29 | // Components: common 30 | //@import "sprites.less"; 31 | //@import "dropdowns.less"; 32 | //@import "wells.less"; 33 | @import "component-animations.less"; 34 | //@import "close.less"; 35 | 36 | // Components: Buttons & Alerts 37 | @import "buttons.less"; 38 | //@import "button-groups.less"; 39 | //@import "alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less 40 | 41 | // Components: Nav 42 | @import "navs.less"; 43 | @import "navbar.less"; 44 | //@import "breadcrumbs.less"; 45 | //@import "pagination.less"; 46 | //@import "pager.less"; 47 | 48 | // Components: Popovers 49 | @import "modals.less"; 50 | //@import "tooltip.less"; 51 | //@import "popovers.less"; 52 | 53 | // Components: Misc 54 | //@import "thumbnails.less"; 55 | //@import "media.less"; 56 | //@import "labels-badges.less"; 57 | //@import "progress-bars.less"; 58 | //@import "accordion.less"; 59 | //@import "carousel.less"; 60 | //@import "hero-unit.less"; 61 | 62 | // Utility classes 63 | @import "utilities.less"; // Has to be last to override when necessary 64 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/breadcrumbs.less: -------------------------------------------------------------------------------- 1 | // 2 | // Breadcrumbs 3 | // -------------------------------------------------- 4 | 5 | 6 | .breadcrumb { 7 | padding: 8px 15px; 8 | margin: 0 0 @baseLineHeight; 9 | list-style: none; 10 | background-color: #f5f5f5; 11 | .border-radius(@baseBorderRadius); 12 | > li { 13 | display: inline-block; 14 | .ie7-inline-block(); 15 | text-shadow: 0 1px 0 @white; 16 | > .divider { 17 | padding: 0 5px; 18 | color: #ccc; 19 | } 20 | } 21 | > .active { 22 | color: @grayLight; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/button-groups.less: -------------------------------------------------------------------------------- 1 | // 2 | // Button groups 3 | // -------------------------------------------------- 4 | 5 | 6 | // Make the div behave like a button 7 | .btn-group { 8 | position: relative; 9 | display: inline-block; 10 | .ie7-inline-block(); 11 | font-size: 0; // remove as part 1 of font-size inline-block hack 12 | vertical-align: middle; // match .btn alignment given font-size hack above 13 | white-space: nowrap; // prevent buttons from wrapping when in tight spaces (e.g., the table on the tests page) 14 | .ie7-restore-left-whitespace(); 15 | } 16 | 17 | // Space out series of button groups 18 | .btn-group + .btn-group { 19 | margin-left: 5px; 20 | } 21 | 22 | // Optional: Group multiple button groups together for a toolbar 23 | .btn-toolbar { 24 | font-size: 0; // Hack to remove whitespace that results from using inline-block 25 | margin-top: @baseLineHeight / 2; 26 | margin-bottom: @baseLineHeight / 2; 27 | > .btn + .btn, 28 | > .btn-group + .btn, 29 | > .btn + .btn-group { 30 | margin-left: 5px; 31 | } 32 | } 33 | 34 | // Float them, remove border radius, then re-add to first and last elements 35 | .btn-group > .btn { 36 | position: relative; 37 | .border-radius(0); 38 | } 39 | .btn-group > .btn + .btn { 40 | margin-left: -1px; 41 | } 42 | .btn-group > .btn, 43 | .btn-group > .dropdown-menu, 44 | .btn-group > .popover { 45 | font-size: @baseFontSize; // redeclare as part 2 of font-size inline-block hack 46 | } 47 | 48 | // Reset fonts for other sizes 49 | .btn-group > .btn-mini { 50 | font-size: @fontSizeMini; 51 | } 52 | .btn-group > .btn-small { 53 | font-size: @fontSizeSmall; 54 | } 55 | .btn-group > .btn-large { 56 | font-size: @fontSizeLarge; 57 | } 58 | 59 | // Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match 60 | .btn-group > .btn:first-child { 61 | margin-left: 0; 62 | .border-top-left-radius(@baseBorderRadius); 63 | .border-bottom-left-radius(@baseBorderRadius); 64 | } 65 | // Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it 66 | .btn-group > .btn:last-child, 67 | .btn-group > .dropdown-toggle { 68 | .border-top-right-radius(@baseBorderRadius); 69 | .border-bottom-right-radius(@baseBorderRadius); 70 | } 71 | // Reset corners for large buttons 72 | .btn-group > .btn.large:first-child { 73 | margin-left: 0; 74 | .border-top-left-radius(@borderRadiusLarge); 75 | .border-bottom-left-radius(@borderRadiusLarge); 76 | } 77 | .btn-group > .btn.large:last-child, 78 | .btn-group > .large.dropdown-toggle { 79 | .border-top-right-radius(@borderRadiusLarge); 80 | .border-bottom-right-radius(@borderRadiusLarge); 81 | } 82 | 83 | // On hover/focus/active, bring the proper btn to front 84 | .btn-group > .btn:hover, 85 | .btn-group > .btn:focus, 86 | .btn-group > .btn:active, 87 | .btn-group > .btn.active { 88 | z-index: 2; 89 | } 90 | 91 | // On active and open, don't show outline 92 | .btn-group .dropdown-toggle:active, 93 | .btn-group.open .dropdown-toggle { 94 | outline: 0; 95 | } 96 | 97 | 98 | 99 | // Split button dropdowns 100 | // ---------------------- 101 | 102 | // Give the line between buttons some depth 103 | .btn-group > .btn + .dropdown-toggle { 104 | padding-left: 8px; 105 | padding-right: 8px; 106 | .box-shadow(~"inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)"); 107 | *padding-top: 5px; 108 | *padding-bottom: 5px; 109 | } 110 | .btn-group > .btn-mini + .dropdown-toggle { 111 | padding-left: 5px; 112 | padding-right: 5px; 113 | *padding-top: 2px; 114 | *padding-bottom: 2px; 115 | } 116 | .btn-group > .btn-small + .dropdown-toggle { 117 | *padding-top: 5px; 118 | *padding-bottom: 4px; 119 | } 120 | .btn-group > .btn-large + .dropdown-toggle { 121 | padding-left: 12px; 122 | padding-right: 12px; 123 | *padding-top: 7px; 124 | *padding-bottom: 7px; 125 | } 126 | 127 | .btn-group.open { 128 | 129 | // The clickable button for toggling the menu 130 | // Remove the gradient and set the same inset shadow as the :active state 131 | .dropdown-toggle { 132 | background-image: none; 133 | .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); 134 | } 135 | 136 | // Keep the hover's background when dropdown is open 137 | .btn.dropdown-toggle { 138 | background-color: @btnBackgroundHighlight; 139 | } 140 | .btn-primary.dropdown-toggle { 141 | background-color: @btnPrimaryBackgroundHighlight; 142 | } 143 | .btn-warning.dropdown-toggle { 144 | background-color: @btnWarningBackgroundHighlight; 145 | } 146 | .btn-danger.dropdown-toggle { 147 | background-color: @btnDangerBackgroundHighlight; 148 | } 149 | .btn-success.dropdown-toggle { 150 | background-color: @btnSuccessBackgroundHighlight; 151 | } 152 | .btn-info.dropdown-toggle { 153 | background-color: @btnInfoBackgroundHighlight; 154 | } 155 | .btn-inverse.dropdown-toggle { 156 | background-color: @btnInverseBackgroundHighlight; 157 | } 158 | } 159 | 160 | 161 | // Reposition the caret 162 | .btn .caret { 163 | margin-top: 8px; 164 | margin-left: 0; 165 | } 166 | // Carets in other button sizes 167 | .btn-large .caret { 168 | margin-top: 6px; 169 | } 170 | .btn-large .caret { 171 | border-left-width: 5px; 172 | border-right-width: 5px; 173 | border-top-width: 5px; 174 | } 175 | .btn-mini .caret, 176 | .btn-small .caret { 177 | margin-top: 8px; 178 | } 179 | // Upside down carets for .dropup 180 | .dropup .btn-large .caret { 181 | border-bottom-width: 5px; 182 | } 183 | 184 | 185 | 186 | // Account for other colors 187 | .btn-primary, 188 | .btn-warning, 189 | .btn-danger, 190 | .btn-info, 191 | .btn-success, 192 | .btn-inverse { 193 | .caret { 194 | border-top-color: @white; 195 | border-bottom-color: @white; 196 | } 197 | } 198 | 199 | 200 | 201 | // Vertical button groups 202 | // ---------------------- 203 | 204 | .btn-group-vertical { 205 | display: inline-block; // makes buttons only take up the width they need 206 | .ie7-inline-block(); 207 | } 208 | .btn-group-vertical > .btn { 209 | display: block; 210 | float: none; 211 | max-width: 100%; 212 | .border-radius(0); 213 | } 214 | .btn-group-vertical > .btn + .btn { 215 | margin-left: 0; 216 | margin-top: -1px; 217 | } 218 | .btn-group-vertical > .btn:first-child { 219 | .border-radius(@baseBorderRadius @baseBorderRadius 0 0); 220 | } 221 | .btn-group-vertical > .btn:last-child { 222 | .border-radius(0 0 @baseBorderRadius @baseBorderRadius); 223 | } 224 | .btn-group-vertical > .btn-large:first-child { 225 | .border-radius(@borderRadiusLarge @borderRadiusLarge 0 0); 226 | } 227 | .btn-group-vertical > .btn-large:last-child { 228 | .border-radius(0 0 @borderRadiusLarge @borderRadiusLarge); 229 | } 230 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/buttons-custom.less: -------------------------------------------------------------------------------- 1 | // 2 | // Buttons 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base styles 7 | // -------------------------------------------------- 8 | 9 | // Core 10 | .btn { 11 | display: inline-block; 12 | .ie7-inline-block(); 13 | padding: 4px 12px; 14 | margin-bottom: 0; // For input.btn 15 | font-size: @baseFontSize; 16 | line-height: @baseLineHeight; 17 | text-align: center; 18 | vertical-align: middle; 19 | cursor: pointer; 20 | // .buttonBackground(@btnBackground, @btnBackgroundHighlight, @grayDark, 0 1px 1px rgba(255,255,255,.75)); 21 | background-color: mix(@btnBackground, @btnBackgroundHighlight, 60%); 22 | // border: 1px solid @btnBorder; 23 | border: 1px solid mix(@btnBackground, @btnBackgroundHighlight, 60%); 24 | *border: 0; // Remove the border to prevent IE7's black border on input:focus 25 | // border-bottom-color: darken(@btnBorder, 10%); 26 | .border-radius(@baseBorderRadius); 27 | .ie7-restore-left-whitespace(); // Give IE7 some love 28 | // .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)"); 29 | 30 | // Hover/focus state 31 | &:hover, 32 | &:focus { 33 | color: @grayDark; 34 | text-decoration: none; 35 | // background-position: 0 -15px; 36 | 37 | // transition is only when going to hover/focus, otherwise the background 38 | // behind the gradient (there for IE<=9 fallback) gets mismatched 39 | // .transition(background-position .1s linear); 40 | background-color: @btnBackgroundHighlight; 41 | border: 1px solid @btnBackgroundHighlight; 42 | } 43 | 44 | // Focus state for keyboard and accessibility 45 | &:focus { 46 | .tab-focus(); 47 | } 48 | 49 | // Active state 50 | &.active, 51 | &:active { 52 | background-image: none; 53 | outline: 0; 54 | .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); 55 | } 56 | 57 | // Disabled state 58 | &.disabled, 59 | &[disabled] { 60 | cursor: default; 61 | background-image: none; 62 | .opacity(65); 63 | .box-shadow(none); 64 | } 65 | 66 | } 67 | 68 | 69 | 70 | // Button Sizes 71 | // -------------------------------------------------- 72 | 73 | // Large 74 | .btn-large { 75 | padding: @paddingLarge; 76 | font-size: @fontSizeLarge; 77 | .border-radius(@borderRadiusLarge); 78 | } 79 | .btn-large [class^="icon-"], 80 | .btn-large [class*=" icon-"] { 81 | margin-top: 4px; 82 | } 83 | 84 | // Small 85 | .btn-small { 86 | padding: @paddingSmall; 87 | font-size: @fontSizeSmall; 88 | .border-radius(@borderRadiusSmall); 89 | } 90 | .btn-small [class^="icon-"], 91 | .btn-small [class*=" icon-"] { 92 | margin-top: 0; 93 | } 94 | .btn-mini [class^="icon-"], 95 | .btn-mini [class*=" icon-"] { 96 | margin-top: -1px; 97 | } 98 | 99 | // Mini 100 | .btn-mini { 101 | padding: @paddingMini; 102 | font-size: @fontSizeMini; 103 | .border-radius(@borderRadiusSmall); 104 | } 105 | 106 | 107 | // Block button 108 | // ------------------------- 109 | 110 | .btn-block { 111 | display: block; 112 | width: 100%; 113 | padding-left: 0; 114 | padding-right: 0; 115 | .box-sizing(border-box); 116 | } 117 | 118 | // Vertically space out multiple block buttons 119 | .btn-block + .btn-block { 120 | margin-top: 5px; 121 | } 122 | 123 | // Specificity overrides 124 | input[type="submit"], 125 | input[type="reset"], 126 | input[type="button"] { 127 | &.btn-block { 128 | width: 100%; 129 | } 130 | } 131 | 132 | 133 | 134 | // Alternate buttons 135 | // -------------------------------------------------- 136 | 137 | // Provide *some* extra contrast for those who can get it 138 | .btn-primary.active, 139 | .btn-warning.active, 140 | .btn-danger.active, 141 | .btn-success.active, 142 | .btn-info.active, 143 | .btn-inverse.active { 144 | color: rgba(255,255,255,.75); 145 | } 146 | 147 | // Set the backgrounds 148 | // ------------------------- 149 | .btn-primary { 150 | .buttonBackground(@btnPrimaryBackground, @btnPrimaryBackgroundHighlight); 151 | } 152 | // Warning appears are orange 153 | .btn-warning { 154 | .buttonBackground(@btnWarningBackground, @btnWarningBackgroundHighlight); 155 | } 156 | // Danger and error appear as red 157 | .btn-danger { 158 | .buttonBackground(@btnDangerBackground, @btnDangerBackgroundHighlight); 159 | } 160 | // Success appears as green 161 | .btn-success { 162 | color: white; 163 | // .buttonBackground(@btnSuccessBackground, @btnSuccessBackgroundHighlight); 164 | background-color: mix(@btnSuccessBackground, @btnSuccessBackgroundHighlight, 60%); 165 | // border: 1px solid @btnBorder; 166 | border: 1px solid mix(@btnSuccessBackground, @btnSuccessBackgroundHighlight, 60%); 167 | // Hover/focus state 168 | &:hover, 169 | &:focus { 170 | color: white; 171 | background-color: @btnSuccessBackgroundHighlight; 172 | border: 1px solid @btnSuccessBackgroundHighlight; 173 | } 174 | } 175 | // Info appears as a neutral blue 176 | .btn-info { 177 | .buttonBackground(@btnInfoBackground, @btnInfoBackgroundHighlight); 178 | } 179 | // Inverse appears as dark gray 180 | .btn-inverse { 181 | .buttonBackground(@btnInverseBackground, @btnInverseBackgroundHighlight); 182 | } 183 | 184 | 185 | // Cross-browser Jank 186 | // -------------------------------------------------- 187 | 188 | button.btn, 189 | input[type="submit"].btn { 190 | 191 | // Firefox 3.6 only I believe 192 | &::-moz-focus-inner { 193 | padding: 0; 194 | border: 0; 195 | } 196 | 197 | // IE7 has some default padding on button controls 198 | *padding-top: 3px; 199 | *padding-bottom: 3px; 200 | 201 | &.btn-large { 202 | *padding-top: 7px; 203 | *padding-bottom: 7px; 204 | } 205 | &.btn-small { 206 | *padding-top: 3px; 207 | *padding-bottom: 3px; 208 | } 209 | &.btn-mini { 210 | *padding-top: 1px; 211 | *padding-bottom: 1px; 212 | } 213 | } 214 | 215 | 216 | // Link buttons 217 | // -------------------------------------------------- 218 | 219 | // Make a button look and behave like a link 220 | .btn-link, 221 | .btn-link:active, 222 | .btn-link[disabled] { 223 | background-color: transparent; 224 | background-image: none; 225 | .box-shadow(none); 226 | } 227 | .btn-link { 228 | border-color: transparent; 229 | cursor: pointer; 230 | color: @linkColor; 231 | .border-radius(0); 232 | } 233 | .btn-link:hover, 234 | .btn-link:focus { 235 | color: @linkColorHover; 236 | text-decoration: underline; 237 | background-color: transparent; 238 | } 239 | .btn-link[disabled]:hover, 240 | .btn-link[disabled]:focus { 241 | color: @grayDark; 242 | text-decoration: none; 243 | } 244 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/buttons.less: -------------------------------------------------------------------------------- 1 | // 2 | // Buttons 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base styles 7 | // -------------------------------------------------- 8 | 9 | // Core 10 | .btn { 11 | display: inline-block; 12 | .ie7-inline-block(); 13 | padding: 4px 12px; 14 | margin-bottom: 0; // For input.btn 15 | font-size: @baseFontSize; 16 | line-height: @baseLineHeight; 17 | text-align: center; 18 | vertical-align: middle; 19 | cursor: pointer; 20 | .buttonBackground(@btnBackground, @btnBackgroundHighlight, @grayDark, 0 1px 1px rgba(255,255,255,.75)); 21 | border: 1px solid @btnBorder; 22 | *border: 0; // Remove the border to prevent IE7's black border on input:focus 23 | border-bottom-color: darken(@btnBorder, 10%); 24 | .border-radius(@baseBorderRadius); 25 | .ie7-restore-left-whitespace(); // Give IE7 some love 26 | .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)"); 27 | 28 | // Hover/focus state 29 | &:hover, 30 | &:focus { 31 | color: @grayDark; 32 | text-decoration: none; 33 | background-position: 0 -15px; 34 | 35 | // transition is only when going to hover/focus, otherwise the background 36 | // behind the gradient (there for IE<=9 fallback) gets mismatched 37 | .transition(background-position .1s linear); 38 | } 39 | 40 | // Focus state for keyboard and accessibility 41 | &:focus { 42 | .tab-focus(); 43 | } 44 | 45 | // Active state 46 | &.active, 47 | &:active { 48 | background-image: none; 49 | outline: 0; 50 | .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); 51 | } 52 | 53 | // Disabled state 54 | &.disabled, 55 | &[disabled] { 56 | cursor: default; 57 | background-image: none; 58 | .opacity(65); 59 | .box-shadow(none); 60 | } 61 | 62 | } 63 | 64 | 65 | 66 | // Button Sizes 67 | // -------------------------------------------------- 68 | 69 | // Large 70 | .btn-large { 71 | padding: @paddingLarge; 72 | font-size: @fontSizeLarge; 73 | .border-radius(@borderRadiusLarge); 74 | } 75 | .btn-large [class^="icon-"], 76 | .btn-large [class*=" icon-"] { 77 | margin-top: 4px; 78 | } 79 | 80 | // Small 81 | .btn-small { 82 | padding: @paddingSmall; 83 | font-size: @fontSizeSmall; 84 | .border-radius(@borderRadiusSmall); 85 | } 86 | .btn-small [class^="icon-"], 87 | .btn-small [class*=" icon-"] { 88 | margin-top: 0; 89 | } 90 | .btn-mini [class^="icon-"], 91 | .btn-mini [class*=" icon-"] { 92 | margin-top: -1px; 93 | } 94 | 95 | // Mini 96 | .btn-mini { 97 | padding: @paddingMini; 98 | font-size: @fontSizeMini; 99 | .border-radius(@borderRadiusSmall); 100 | } 101 | 102 | 103 | // Block button 104 | // ------------------------- 105 | 106 | .btn-block { 107 | display: block; 108 | width: 100%; 109 | padding-left: 0; 110 | padding-right: 0; 111 | .box-sizing(border-box); 112 | } 113 | 114 | // Vertically space out multiple block buttons 115 | .btn-block + .btn-block { 116 | margin-top: 5px; 117 | } 118 | 119 | // Specificity overrides 120 | input[type="submit"], 121 | input[type="reset"], 122 | input[type="button"] { 123 | &.btn-block { 124 | width: 100%; 125 | } 126 | } 127 | 128 | 129 | 130 | // Alternate buttons 131 | // -------------------------------------------------- 132 | 133 | // Provide *some* extra contrast for those who can get it 134 | .btn-primary.active, 135 | .btn-warning.active, 136 | .btn-danger.active, 137 | .btn-success.active, 138 | .btn-info.active, 139 | .btn-inverse.active { 140 | color: rgba(255,255,255,.75); 141 | } 142 | 143 | // Set the backgrounds 144 | // ------------------------- 145 | .btn-primary { 146 | .buttonBackground(@btnPrimaryBackground, @btnPrimaryBackgroundHighlight); 147 | } 148 | // Warning appears are orange 149 | .btn-warning { 150 | .buttonBackground(@btnWarningBackground, @btnWarningBackgroundHighlight); 151 | } 152 | // Danger and error appear as red 153 | .btn-danger { 154 | .buttonBackground(@btnDangerBackground, @btnDangerBackgroundHighlight); 155 | } 156 | // Success appears as green 157 | .btn-success { 158 | .buttonBackground(@btnSuccessBackground, @btnSuccessBackgroundHighlight); 159 | } 160 | // Info appears as a neutral blue 161 | .btn-info { 162 | .buttonBackground(@btnInfoBackground, @btnInfoBackgroundHighlight); 163 | } 164 | // Inverse appears as dark gray 165 | .btn-inverse { 166 | .buttonBackground(@btnInverseBackground, @btnInverseBackgroundHighlight); 167 | } 168 | 169 | 170 | // Cross-browser Jank 171 | // -------------------------------------------------- 172 | 173 | button.btn, 174 | input[type="submit"].btn { 175 | 176 | // Firefox 3.6 only I believe 177 | &::-moz-focus-inner { 178 | padding: 0; 179 | border: 0; 180 | } 181 | 182 | // IE7 has some default padding on button controls 183 | *padding-top: 3px; 184 | *padding-bottom: 3px; 185 | 186 | &.btn-large { 187 | *padding-top: 7px; 188 | *padding-bottom: 7px; 189 | } 190 | &.btn-small { 191 | *padding-top: 3px; 192 | *padding-bottom: 3px; 193 | } 194 | &.btn-mini { 195 | *padding-top: 1px; 196 | *padding-bottom: 1px; 197 | } 198 | } 199 | 200 | 201 | // Link buttons 202 | // -------------------------------------------------- 203 | 204 | // Make a button look and behave like a link 205 | .btn-link, 206 | .btn-link:active, 207 | .btn-link[disabled] { 208 | background-color: transparent; 209 | background-image: none; 210 | .box-shadow(none); 211 | } 212 | .btn-link { 213 | border-color: transparent; 214 | cursor: pointer; 215 | color: @linkColor; 216 | .border-radius(0); 217 | } 218 | .btn-link:hover, 219 | .btn-link:focus { 220 | color: @linkColorHover; 221 | text-decoration: underline; 222 | background-color: transparent; 223 | } 224 | .btn-link[disabled]:hover, 225 | .btn-link[disabled]:focus { 226 | color: @grayDark; 227 | text-decoration: none; 228 | } 229 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/carousel.less: -------------------------------------------------------------------------------- 1 | // 2 | // Carousel 3 | // -------------------------------------------------- 4 | 5 | 6 | .carousel { 7 | position: relative; 8 | margin-bottom: @baseLineHeight; 9 | line-height: 1; 10 | } 11 | 12 | .carousel-inner { 13 | overflow: hidden; 14 | width: 100%; 15 | position: relative; 16 | } 17 | 18 | .carousel-inner { 19 | 20 | > .item { 21 | display: none; 22 | position: relative; 23 | .transition(.6s ease-in-out left); 24 | 25 | // Account for jankitude on images 26 | > img, 27 | > a > img { 28 | display: block; 29 | line-height: 1; 30 | } 31 | } 32 | 33 | > .active, 34 | > .next, 35 | > .prev { display: block; } 36 | 37 | > .active { 38 | left: 0; 39 | } 40 | 41 | > .next, 42 | > .prev { 43 | position: absolute; 44 | top: 0; 45 | width: 100%; 46 | } 47 | 48 | > .next { 49 | left: 100%; 50 | } 51 | > .prev { 52 | left: -100%; 53 | } 54 | > .next.left, 55 | > .prev.right { 56 | left: 0; 57 | } 58 | 59 | > .active.left { 60 | left: -100%; 61 | } 62 | > .active.right { 63 | left: 100%; 64 | } 65 | 66 | } 67 | 68 | // Left/right controls for nav 69 | // --------------------------- 70 | 71 | .carousel-control { 72 | position: absolute; 73 | top: 40%; 74 | left: 15px; 75 | width: 40px; 76 | height: 40px; 77 | margin-top: -20px; 78 | font-size: 60px; 79 | font-weight: 100; 80 | line-height: 30px; 81 | color: @white; 82 | text-align: center; 83 | background: @grayDarker; 84 | border: 3px solid @white; 85 | .border-radius(23px); 86 | .opacity(50); 87 | 88 | // we can't have this transition here 89 | // because webkit cancels the carousel 90 | // animation if you trip this while 91 | // in the middle of another animation 92 | // ;_; 93 | // .transition(opacity .2s linear); 94 | 95 | // Reposition the right one 96 | &.right { 97 | left: auto; 98 | right: 15px; 99 | } 100 | 101 | // Hover/focus state 102 | &:hover, 103 | &:focus { 104 | color: @white; 105 | text-decoration: none; 106 | .opacity(90); 107 | } 108 | } 109 | 110 | // Carousel indicator pips 111 | // ----------------------------- 112 | .carousel-indicators { 113 | position: absolute; 114 | top: 15px; 115 | right: 15px; 116 | z-index: 5; 117 | margin: 0; 118 | list-style: none; 119 | 120 | li { 121 | display: block; 122 | float: left; 123 | width: 10px; 124 | height: 10px; 125 | margin-left: 5px; 126 | text-indent: -999px; 127 | background-color: #ccc; 128 | background-color: rgba(255,255,255,.25); 129 | border-radius: 5px; 130 | } 131 | .active { 132 | background-color: #fff; 133 | } 134 | } 135 | 136 | // Caption for text below images 137 | // ----------------------------- 138 | 139 | .carousel-caption { 140 | position: absolute; 141 | left: 0; 142 | right: 0; 143 | bottom: 0; 144 | padding: 15px; 145 | background: @grayDark; 146 | background: rgba(0,0,0,.75); 147 | } 148 | .carousel-caption h4, 149 | .carousel-caption p { 150 | color: @white; 151 | line-height: @baseLineHeight; 152 | } 153 | .carousel-caption h4 { 154 | margin: 0 0 5px; 155 | } 156 | .carousel-caption p { 157 | margin-bottom: 0; 158 | } 159 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/close.less: -------------------------------------------------------------------------------- 1 | // 2 | // Close icons 3 | // -------------------------------------------------- 4 | 5 | 6 | .close { 7 | float: right; 8 | font-size: 20px; 9 | font-weight: bold; 10 | line-height: @baseLineHeight; 11 | color: @black; 12 | text-shadow: 0 1px 0 rgba(255,255,255,1); 13 | .opacity(20); 14 | &:hover, 15 | &:focus { 16 | color: @black; 17 | text-decoration: none; 18 | cursor: pointer; 19 | .opacity(40); 20 | } 21 | } 22 | 23 | // Additional properties for button version 24 | // iOS requires the button element instead of an anchor tag. 25 | // If you want the anchor version, it requires `href="#"`. 26 | button.close { 27 | padding: 0; 28 | cursor: pointer; 29 | background: transparent; 30 | border: 0; 31 | -webkit-appearance: none; 32 | } -------------------------------------------------------------------------------- /WebContent/css/bootstrap/code.less: -------------------------------------------------------------------------------- 1 | // 2 | // Code (inline and blocK) 3 | // -------------------------------------------------- 4 | 5 | 6 | // Inline and block code styles 7 | code, 8 | pre { 9 | padding: 0 3px 2px; 10 | #font > #family > .monospace; 11 | font-size: @baseFontSize - 2; 12 | color: @grayDark; 13 | .border-radius(3px); 14 | } 15 | 16 | // Inline code 17 | code { 18 | padding: 2px 4px; 19 | color: #d14; 20 | background-color: #f7f7f9; 21 | border: 1px solid #e1e1e8; 22 | white-space: nowrap; 23 | } 24 | 25 | // Blocks of code 26 | pre { 27 | display: block; 28 | padding: (@baseLineHeight - 1) / 2; 29 | margin: 0 0 @baseLineHeight / 2; 30 | font-size: @baseFontSize - 1; // 14px to 13px 31 | line-height: @baseLineHeight; 32 | word-break: break-all; 33 | word-wrap: break-word; 34 | white-space: pre; 35 | white-space: pre-wrap; 36 | background-color: #f5f5f5; 37 | border: 1px solid #ccc; // fallback for IE7-8 38 | border: 1px solid rgba(0,0,0,.15); 39 | .border-radius(@baseBorderRadius); 40 | 41 | // Make prettyprint styles more spaced out for readability 42 | &.prettyprint { 43 | margin-bottom: @baseLineHeight; 44 | } 45 | 46 | // Account for some code outputs that place code tags in pre tags 47 | code { 48 | padding: 0; 49 | color: inherit; 50 | white-space: pre; 51 | white-space: pre-wrap; 52 | background-color: transparent; 53 | border: 0; 54 | } 55 | } 56 | 57 | // Enable scrollable blocks of code 58 | .pre-scrollable { 59 | max-height: 340px; 60 | overflow-y: scroll; 61 | } -------------------------------------------------------------------------------- /WebContent/css/bootstrap/component-animations.less: -------------------------------------------------------------------------------- 1 | // 2 | // Component animations 3 | // -------------------------------------------------- 4 | 5 | 6 | .fade { 7 | opacity: 0; 8 | .transition(opacity .15s linear); 9 | &.in { 10 | opacity: 1; 11 | } 12 | } 13 | 14 | .collapse { 15 | position: relative; 16 | height: 0; 17 | overflow: hidden; 18 | .transition(height .35s ease); 19 | &.in { 20 | height: auto; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/dropdowns.less: -------------------------------------------------------------------------------- 1 | // 2 | // Dropdown menus 3 | // -------------------------------------------------- 4 | 5 | 6 | // Use the .menu class on any
  • element within the topbar or ul.tabs and you'll get some superfancy dropdowns 7 | .dropup, 8 | .dropdown { 9 | position: relative; 10 | } 11 | .dropdown-toggle { 12 | // The caret makes the toggle a bit too tall in IE7 13 | *margin-bottom: -3px; 14 | } 15 | .dropdown-toggle:active, 16 | .open .dropdown-toggle { 17 | outline: 0; 18 | } 19 | 20 | // Dropdown arrow/caret 21 | // -------------------- 22 | .caret { 23 | display: inline-block; 24 | width: 0; 25 | height: 0; 26 | vertical-align: top; 27 | border-top: 4px solid @black; 28 | border-right: 4px solid transparent; 29 | border-left: 4px solid transparent; 30 | content: ""; 31 | } 32 | 33 | // Place the caret 34 | .dropdown .caret { 35 | margin-top: 8px; 36 | margin-left: 2px; 37 | } 38 | 39 | // The dropdown menu (ul) 40 | // ---------------------- 41 | .dropdown-menu { 42 | position: absolute; 43 | top: 100%; 44 | left: 0; 45 | z-index: @zindexDropdown; 46 | display: none; // none by default, but block on "open" of the menu 47 | float: left; 48 | min-width: 160px; 49 | padding: 5px 0; 50 | margin: 2px 0 0; // override default ul 51 | list-style: none; 52 | background-color: @dropdownBackground; 53 | border: 1px solid #ccc; // Fallback for IE7-8 54 | border: 1px solid @dropdownBorder; 55 | *border-right-width: 2px; 56 | *border-bottom-width: 2px; 57 | .border-radius(6px); 58 | .box-shadow(0 5px 10px rgba(0,0,0,.2)); 59 | -webkit-background-clip: padding-box; 60 | -moz-background-clip: padding; 61 | background-clip: padding-box; 62 | 63 | // Aligns the dropdown menu to right 64 | &.pull-right { 65 | right: 0; 66 | left: auto; 67 | } 68 | 69 | // Dividers (basically an hr) within the dropdown 70 | .divider { 71 | .nav-divider(@dropdownDividerTop, @dropdownDividerBottom); 72 | } 73 | 74 | // Links within the dropdown menu 75 | > li > a { 76 | display: block; 77 | padding: 3px 20px; 78 | clear: both; 79 | font-weight: normal; 80 | line-height: @baseLineHeight; 81 | color: @dropdownLinkColor; 82 | white-space: nowrap; 83 | } 84 | } 85 | 86 | // Hover/Focus state 87 | // ----------- 88 | .dropdown-menu > li > a:hover, 89 | .dropdown-menu > li > a:focus, 90 | .dropdown-submenu:hover > a, 91 | .dropdown-submenu:focus > a { 92 | text-decoration: none; 93 | color: @dropdownLinkColorHover; 94 | #gradient > .vertical(@dropdownLinkBackgroundHover, darken(@dropdownLinkBackgroundHover, 5%)); 95 | } 96 | 97 | // Active state 98 | // ------------ 99 | .dropdown-menu > .active > a, 100 | .dropdown-menu > .active > a:hover, 101 | .dropdown-menu > .active > a:focus { 102 | color: @dropdownLinkColorActive; 103 | text-decoration: none; 104 | outline: 0; 105 | #gradient > .vertical(@dropdownLinkBackgroundActive, darken(@dropdownLinkBackgroundActive, 5%)); 106 | } 107 | 108 | // Disabled state 109 | // -------------- 110 | // Gray out text and ensure the hover/focus state remains gray 111 | .dropdown-menu > .disabled > a, 112 | .dropdown-menu > .disabled > a:hover, 113 | .dropdown-menu > .disabled > a:focus { 114 | color: @grayLight; 115 | } 116 | // Nuke hover/focus effects 117 | .dropdown-menu > .disabled > a:hover, 118 | .dropdown-menu > .disabled > a:focus { 119 | text-decoration: none; 120 | background-color: transparent; 121 | background-image: none; // Remove CSS gradient 122 | .reset-filter(); 123 | cursor: default; 124 | } 125 | 126 | // Open state for the dropdown 127 | // --------------------------- 128 | .open { 129 | // IE7's z-index only goes to the nearest positioned ancestor, which would 130 | // make the menu appear below buttons that appeared later on the page 131 | *z-index: @zindexDropdown; 132 | 133 | & > .dropdown-menu { 134 | display: block; 135 | } 136 | } 137 | 138 | // Right aligned dropdowns 139 | // --------------------------- 140 | .pull-right > .dropdown-menu { 141 | right: 0; 142 | left: auto; 143 | } 144 | 145 | // Allow for dropdowns to go bottom up (aka, dropup-menu) 146 | // ------------------------------------------------------ 147 | // Just add .dropup after the standard .dropdown class and you're set, bro. 148 | // TODO: abstract this so that the navbar fixed styles are not placed here? 149 | .dropup, 150 | .navbar-fixed-bottom .dropdown { 151 | // Reverse the caret 152 | .caret { 153 | border-top: 0; 154 | border-bottom: 4px solid @black; 155 | content: ""; 156 | } 157 | // Different positioning for bottom up menu 158 | .dropdown-menu { 159 | top: auto; 160 | bottom: 100%; 161 | margin-bottom: 1px; 162 | } 163 | } 164 | 165 | // Sub menus 166 | // --------------------------- 167 | .dropdown-submenu { 168 | position: relative; 169 | } 170 | // Default dropdowns 171 | .dropdown-submenu > .dropdown-menu { 172 | top: 0; 173 | left: 100%; 174 | margin-top: -6px; 175 | margin-left: -1px; 176 | .border-radius(0 6px 6px 6px); 177 | } 178 | .dropdown-submenu:hover > .dropdown-menu { 179 | display: block; 180 | } 181 | 182 | // Dropups 183 | .dropup .dropdown-submenu > .dropdown-menu { 184 | top: auto; 185 | bottom: 0; 186 | margin-top: 0; 187 | margin-bottom: -2px; 188 | .border-radius(5px 5px 5px 0); 189 | } 190 | 191 | // Caret to indicate there is a submenu 192 | .dropdown-submenu > a:after { 193 | display: block; 194 | content: " "; 195 | float: right; 196 | width: 0; 197 | height: 0; 198 | border-color: transparent; 199 | border-style: solid; 200 | border-width: 5px 0 5px 5px; 201 | border-left-color: darken(@dropdownBackground, 20%); 202 | margin-top: 5px; 203 | margin-right: -10px; 204 | } 205 | .dropdown-submenu:hover > a:after { 206 | border-left-color: @dropdownLinkColorHover; 207 | } 208 | 209 | // Left aligned submenus 210 | .dropdown-submenu.pull-left { 211 | // Undo the float 212 | // Yes, this is awkward since .pull-left adds a float, but it sticks to our conventions elsewhere. 213 | float: none; 214 | 215 | // Positioning the submenu 216 | > .dropdown-menu { 217 | left: -100%; 218 | margin-left: 10px; 219 | .border-radius(6px 0 6px 6px); 220 | } 221 | } 222 | 223 | // Tweak nav headers 224 | // ----------------- 225 | // Increase padding from 15px to 20px on sides 226 | .dropdown .dropdown-menu .nav-header { 227 | padding-left: 20px; 228 | padding-right: 20px; 229 | } 230 | 231 | // Typeahead 232 | // --------- 233 | .typeahead { 234 | z-index: 1051; 235 | margin-top: 2px; // give it some space to breathe 236 | .border-radius(@baseBorderRadius); 237 | } 238 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/grid.less: -------------------------------------------------------------------------------- 1 | // 2 | // Grid system 3 | // -------------------------------------------------- 4 | 5 | 6 | // Fixed (940px) 7 | #grid > .core(@gridColumnWidth, @gridGutterWidth); 8 | 9 | // Fluid (940px) 10 | #grid > .fluid(@fluidGridColumnWidth, @fluidGridGutterWidth); 11 | 12 | // Reset utility classes due to specificity 13 | [class*="span"].hide, 14 | .row-fluid [class*="span"].hide { 15 | display: none; 16 | } 17 | 18 | [class*="span"].pull-right, 19 | .row-fluid [class*="span"].pull-right { 20 | float: right; 21 | } 22 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/hero-unit.less: -------------------------------------------------------------------------------- 1 | // 2 | // Hero unit 3 | // -------------------------------------------------- 4 | 5 | 6 | .hero-unit { 7 | padding: 60px; 8 | margin-bottom: 30px; 9 | font-size: 18px; 10 | font-weight: 200; 11 | line-height: @baseLineHeight * 1.5; 12 | color: @heroUnitLeadColor; 13 | background-color: @heroUnitBackground; 14 | .border-radius(6px); 15 | h1 { 16 | margin-bottom: 0; 17 | font-size: 60px; 18 | line-height: 1; 19 | color: @heroUnitHeadingColor; 20 | letter-spacing: -1px; 21 | } 22 | li { 23 | line-height: @baseLineHeight * 1.5; // Reset since we specify in type.less 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/labels-badges.less: -------------------------------------------------------------------------------- 1 | // 2 | // Labels and badges 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base classes 7 | .label, 8 | .badge { 9 | display: inline-block; 10 | padding: 2px 4px; 11 | font-size: @baseFontSize * .846; 12 | font-weight: bold; 13 | line-height: 14px; // ensure proper line-height if floated 14 | color: @white; 15 | vertical-align: baseline; 16 | white-space: nowrap; 17 | text-shadow: 0 -1px 0 rgba(0,0,0,.25); 18 | background-color: @grayLight; 19 | } 20 | // Set unique padding and border-radii 21 | .label { 22 | .border-radius(3px); 23 | } 24 | .badge { 25 | padding-left: 9px; 26 | padding-right: 9px; 27 | .border-radius(9px); 28 | } 29 | 30 | // Empty labels/badges collapse 31 | .label, 32 | .badge { 33 | &:empty { 34 | display: none; 35 | } 36 | } 37 | 38 | // Hover/focus state, but only for links 39 | a { 40 | &.label:hover, 41 | &.label:focus, 42 | &.badge:hover, 43 | &.badge:focus { 44 | color: @white; 45 | text-decoration: none; 46 | cursor: pointer; 47 | } 48 | } 49 | 50 | // Colors 51 | // Only give background-color difference to links (and to simplify, we don't qualifty with `a` but [href] attribute) 52 | .label, 53 | .badge { 54 | // Important (red) 55 | &-important { background-color: @errorText; } 56 | &-important[href] { background-color: darken(@errorText, 10%); } 57 | // Warnings (orange) 58 | &-warning { background-color: @orange; } 59 | &-warning[href] { background-color: darken(@orange, 10%); } 60 | // Success (green) 61 | &-success { background-color: @successText; } 62 | &-success[href] { background-color: darken(@successText, 10%); } 63 | // Info (turquoise) 64 | &-info { background-color: @infoText; } 65 | &-info[href] { background-color: darken(@infoText, 10%); } 66 | // Inverse (black) 67 | &-inverse { background-color: @grayDark; } 68 | &-inverse[href] { background-color: darken(@grayDark, 10%); } 69 | } 70 | 71 | // Quick fix for labels/badges in buttons 72 | .btn { 73 | .label, 74 | .badge { 75 | position: relative; 76 | top: -1px; 77 | } 78 | } 79 | .btn-mini { 80 | .label, 81 | .badge { 82 | top: 0; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/layouts.less: -------------------------------------------------------------------------------- 1 | // 2 | // Layouts 3 | // -------------------------------------------------- 4 | 5 | 6 | // Container (centered, fixed-width layouts) 7 | .container { 8 | .container-fixed(); 9 | } 10 | 11 | // Fluid layouts (left aligned, with sidebar, min- & max-width content) 12 | .container-fluid { 13 | padding-right: @gridGutterWidth; 14 | padding-left: @gridGutterWidth; 15 | .clearfix(); 16 | } -------------------------------------------------------------------------------- /WebContent/css/bootstrap/media.less: -------------------------------------------------------------------------------- 1 | // Media objects 2 | // Source: http://stubbornella.org/content/?p=497 3 | // -------------------------------------------------- 4 | 5 | 6 | // Common styles 7 | // ------------------------- 8 | 9 | // Clear the floats 10 | .media, 11 | .media-body { 12 | overflow: hidden; 13 | *overflow: visible; 14 | zoom: 1; 15 | } 16 | 17 | // Proper spacing between instances of .media 18 | .media, 19 | .media .media { 20 | margin-top: 15px; 21 | } 22 | .media:first-child { 23 | margin-top: 0; 24 | } 25 | 26 | // For images and videos, set to block 27 | .media-object { 28 | display: block; 29 | } 30 | 31 | // Reset margins on headings for tighter default spacing 32 | .media-heading { 33 | margin: 0 0 5px; 34 | } 35 | 36 | 37 | // Media image alignment 38 | // ------------------------- 39 | 40 | .media > .pull-left { 41 | margin-right: 10px; 42 | } 43 | .media > .pull-right { 44 | margin-left: 10px; 45 | } 46 | 47 | 48 | // Media list variation 49 | // ------------------------- 50 | 51 | // Undo default ul/ol styles 52 | .media-list { 53 | margin-left: 0; 54 | list-style: none; 55 | } 56 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/modals.less: -------------------------------------------------------------------------------- 1 | // 2 | // Modals 3 | // -------------------------------------------------- 4 | 5 | // Background 6 | .modal-backdrop { 7 | position: fixed; 8 | top: 0; 9 | right: 0; 10 | bottom: 0; 11 | left: 0; 12 | z-index: @zindexModalBackdrop; 13 | background-color: @black; 14 | // Fade for backdrop 15 | &.fade { opacity: 0; } 16 | } 17 | 18 | .modal-backdrop, 19 | .modal-backdrop.fade.in { 20 | .opacity(80); 21 | } 22 | 23 | // Base modal 24 | .modal { 25 | position: fixed; 26 | top: 10%; 27 | left: 50%; 28 | z-index: @zindexModal; 29 | width: 560px; 30 | margin-left: -280px; 31 | background-color: @white; 32 | border: 1px solid #999; 33 | border: 1px solid rgba(0,0,0,.3); 34 | *border: 1px solid #999; /* IE6-7 */ 35 | .border-radius(6px); 36 | .box-shadow(0 3px 7px rgba(0,0,0,0.3)); 37 | .background-clip(padding-box); 38 | // Remove focus outline from opened modal 39 | outline: none; 40 | 41 | &.fade { 42 | .transition(e('opacity .3s linear, top .3s ease-out')); 43 | top: -25%; 44 | } 45 | &.fade.in { top: 10%; } 46 | } 47 | .modal-header { 48 | padding: 9px 15px; 49 | border-bottom: 1px solid #eee; 50 | // Close icon 51 | .close { margin-top: 2px; } 52 | // Heading 53 | h3 { 54 | margin: 0; 55 | line-height: 30px; 56 | } 57 | } 58 | 59 | // Body (where all modal content resides) 60 | .modal-body { 61 | position: relative; 62 | overflow-y: auto; 63 | max-height: 400px; 64 | padding: 15px; 65 | } 66 | // Remove bottom margin if need be 67 | .modal-form { 68 | margin-bottom: 0; 69 | } 70 | 71 | // Footer (for actions) 72 | .modal-footer { 73 | padding: 14px 15px 15px; 74 | margin-bottom: 0; 75 | text-align: right; // right align buttons 76 | background-color: #f5f5f5; 77 | border-top: 1px solid #ddd; 78 | .border-radius(0 0 6px 6px); 79 | .box-shadow(inset 0 1px 0 @white); 80 | .clearfix(); // clear it in case folks use .pull-* classes on buttons 81 | 82 | // Properly space out buttons 83 | .btn + .btn { 84 | margin-left: 5px; 85 | margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs 86 | } 87 | // but override that for button groups 88 | .btn-group .btn + .btn { 89 | margin-left: -1px; 90 | } 91 | // and override it for block buttons as well 92 | .btn-block + .btn-block { 93 | margin-left: 0; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/pager.less: -------------------------------------------------------------------------------- 1 | // 2 | // Pager pagination 3 | // -------------------------------------------------- 4 | 5 | 6 | .pager { 7 | margin: @baseLineHeight 0; 8 | list-style: none; 9 | text-align: center; 10 | .clearfix(); 11 | } 12 | .pager li { 13 | display: inline; 14 | } 15 | .pager li > a, 16 | .pager li > span { 17 | display: inline-block; 18 | padding: 5px 14px; 19 | background-color: #fff; 20 | border: 1px solid #ddd; 21 | .border-radius(15px); 22 | } 23 | .pager li > a:hover, 24 | .pager li > a:focus { 25 | text-decoration: none; 26 | background-color: #f5f5f5; 27 | } 28 | .pager .next > a, 29 | .pager .next > span { 30 | float: right; 31 | } 32 | .pager .previous > a, 33 | .pager .previous > span { 34 | float: left; 35 | } 36 | .pager .disabled > a, 37 | .pager .disabled > a:hover, 38 | .pager .disabled > a:focus, 39 | .pager .disabled > span { 40 | color: @grayLight; 41 | background-color: #fff; 42 | cursor: default; 43 | } -------------------------------------------------------------------------------- /WebContent/css/bootstrap/pagination.less: -------------------------------------------------------------------------------- 1 | // 2 | // Pagination (multiple pages) 3 | // -------------------------------------------------- 4 | 5 | // Space out pagination from surrounding content 6 | .pagination { 7 | margin: @baseLineHeight 0; 8 | } 9 | 10 | .pagination ul { 11 | // Allow for text-based alignment 12 | display: inline-block; 13 | .ie7-inline-block(); 14 | // Reset default ul styles 15 | margin-left: 0; 16 | margin-bottom: 0; 17 | // Visuals 18 | .border-radius(@baseBorderRadius); 19 | .box-shadow(0 1px 2px rgba(0,0,0,.05)); 20 | } 21 | .pagination ul > li { 22 | display: inline; // Remove list-style and block-level defaults 23 | } 24 | .pagination ul > li > a, 25 | .pagination ul > li > span { 26 | float: left; // Collapse white-space 27 | padding: 4px 12px; 28 | line-height: @baseLineHeight; 29 | text-decoration: none; 30 | background-color: @paginationBackground; 31 | border: 1px solid @paginationBorder; 32 | border-left-width: 0; 33 | } 34 | .pagination ul > li > a:hover, 35 | .pagination ul > li > a:focus, 36 | .pagination ul > .active > a, 37 | .pagination ul > .active > span { 38 | background-color: @paginationActiveBackground; 39 | } 40 | .pagination ul > .active > a, 41 | .pagination ul > .active > span { 42 | color: @grayLight; 43 | cursor: default; 44 | } 45 | .pagination ul > .disabled > span, 46 | .pagination ul > .disabled > a, 47 | .pagination ul > .disabled > a:hover, 48 | .pagination ul > .disabled > a:focus { 49 | color: @grayLight; 50 | background-color: transparent; 51 | cursor: default; 52 | } 53 | .pagination ul > li:first-child > a, 54 | .pagination ul > li:first-child > span { 55 | border-left-width: 1px; 56 | .border-left-radius(@baseBorderRadius); 57 | } 58 | .pagination ul > li:last-child > a, 59 | .pagination ul > li:last-child > span { 60 | .border-right-radius(@baseBorderRadius); 61 | } 62 | 63 | 64 | // Alignment 65 | // -------------------------------------------------- 66 | 67 | .pagination-centered { 68 | text-align: center; 69 | } 70 | .pagination-right { 71 | text-align: right; 72 | } 73 | 74 | 75 | // Sizing 76 | // -------------------------------------------------- 77 | 78 | // Large 79 | .pagination-large { 80 | ul > li > a, 81 | ul > li > span { 82 | padding: @paddingLarge; 83 | font-size: @fontSizeLarge; 84 | } 85 | ul > li:first-child > a, 86 | ul > li:first-child > span { 87 | .border-left-radius(@borderRadiusLarge); 88 | } 89 | ul > li:last-child > a, 90 | ul > li:last-child > span { 91 | .border-right-radius(@borderRadiusLarge); 92 | } 93 | } 94 | 95 | // Small and mini 96 | .pagination-mini, 97 | .pagination-small { 98 | ul > li:first-child > a, 99 | ul > li:first-child > span { 100 | .border-left-radius(@borderRadiusSmall); 101 | } 102 | ul > li:last-child > a, 103 | ul > li:last-child > span { 104 | .border-right-radius(@borderRadiusSmall); 105 | } 106 | } 107 | 108 | // Small 109 | .pagination-small { 110 | ul > li > a, 111 | ul > li > span { 112 | padding: @paddingSmall; 113 | font-size: @fontSizeSmall; 114 | } 115 | } 116 | // Mini 117 | .pagination-mini { 118 | ul > li > a, 119 | ul > li > span { 120 | padding: @paddingMini; 121 | font-size: @fontSizeMini; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/popovers.less: -------------------------------------------------------------------------------- 1 | // 2 | // Popovers 3 | // -------------------------------------------------- 4 | 5 | 6 | .popover { 7 | position: absolute; 8 | top: 0; 9 | left: 0; 10 | z-index: @zindexPopover; 11 | display: none; 12 | max-width: 276px; 13 | padding: 1px; 14 | text-align: left; // Reset given new insertion method 15 | background-color: @popoverBackground; 16 | -webkit-background-clip: padding-box; 17 | -moz-background-clip: padding; 18 | background-clip: padding-box; 19 | border: 1px solid #ccc; 20 | border: 1px solid rgba(0,0,0,.2); 21 | .border-radius(6px); 22 | .box-shadow(0 5px 10px rgba(0,0,0,.2)); 23 | 24 | // Overrides for proper insertion 25 | white-space: normal; 26 | 27 | // Offset the popover to account for the popover arrow 28 | &.top { margin-top: -10px; } 29 | &.right { margin-left: 10px; } 30 | &.bottom { margin-top: 10px; } 31 | &.left { margin-left: -10px; } 32 | } 33 | 34 | .popover-title { 35 | margin: 0; // reset heading margin 36 | padding: 8px 14px; 37 | font-size: 14px; 38 | font-weight: normal; 39 | line-height: 18px; 40 | background-color: @popoverTitleBackground; 41 | border-bottom: 1px solid darken(@popoverTitleBackground, 5%); 42 | .border-radius(5px 5px 0 0); 43 | 44 | &:empty { 45 | display: none; 46 | } 47 | } 48 | 49 | .popover-content { 50 | padding: 9px 14px; 51 | } 52 | 53 | // Arrows 54 | // 55 | // .arrow is outer, .arrow:after is inner 56 | 57 | .popover .arrow, 58 | .popover .arrow:after { 59 | position: absolute; 60 | display: block; 61 | width: 0; 62 | height: 0; 63 | border-color: transparent; 64 | border-style: solid; 65 | } 66 | .popover .arrow { 67 | border-width: @popoverArrowOuterWidth; 68 | } 69 | .popover .arrow:after { 70 | border-width: @popoverArrowWidth; 71 | content: ""; 72 | } 73 | 74 | .popover { 75 | &.top .arrow { 76 | left: 50%; 77 | margin-left: -@popoverArrowOuterWidth; 78 | border-bottom-width: 0; 79 | border-top-color: #999; // IE8 fallback 80 | border-top-color: @popoverArrowOuterColor; 81 | bottom: -@popoverArrowOuterWidth; 82 | &:after { 83 | bottom: 1px; 84 | margin-left: -@popoverArrowWidth; 85 | border-bottom-width: 0; 86 | border-top-color: @popoverArrowColor; 87 | } 88 | } 89 | &.right .arrow { 90 | top: 50%; 91 | left: -@popoverArrowOuterWidth; 92 | margin-top: -@popoverArrowOuterWidth; 93 | border-left-width: 0; 94 | border-right-color: #999; // IE8 fallback 95 | border-right-color: @popoverArrowOuterColor; 96 | &:after { 97 | left: 1px; 98 | bottom: -@popoverArrowWidth; 99 | border-left-width: 0; 100 | border-right-color: @popoverArrowColor; 101 | } 102 | } 103 | &.bottom .arrow { 104 | left: 50%; 105 | margin-left: -@popoverArrowOuterWidth; 106 | border-top-width: 0; 107 | border-bottom-color: #999; // IE8 fallback 108 | border-bottom-color: @popoverArrowOuterColor; 109 | top: -@popoverArrowOuterWidth; 110 | &:after { 111 | top: 1px; 112 | margin-left: -@popoverArrowWidth; 113 | border-top-width: 0; 114 | border-bottom-color: @popoverArrowColor; 115 | } 116 | } 117 | 118 | &.left .arrow { 119 | top: 50%; 120 | right: -@popoverArrowOuterWidth; 121 | margin-top: -@popoverArrowOuterWidth; 122 | border-right-width: 0; 123 | border-left-color: #999; // IE8 fallback 124 | border-left-color: @popoverArrowOuterColor; 125 | &:after { 126 | right: 1px; 127 | border-right-width: 0; 128 | border-left-color: @popoverArrowColor; 129 | bottom: -@popoverArrowWidth; 130 | } 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/progress-bars.less: -------------------------------------------------------------------------------- 1 | // 2 | // Progress bars 3 | // -------------------------------------------------- 4 | 5 | 6 | // ANIMATIONS 7 | // ---------- 8 | 9 | // Webkit 10 | @-webkit-keyframes progress-bar-stripes { 11 | from { background-position: 40px 0; } 12 | to { background-position: 0 0; } 13 | } 14 | 15 | // Firefox 16 | @-moz-keyframes progress-bar-stripes { 17 | from { background-position: 40px 0; } 18 | to { background-position: 0 0; } 19 | } 20 | 21 | // IE9 22 | @-ms-keyframes progress-bar-stripes { 23 | from { background-position: 40px 0; } 24 | to { background-position: 0 0; } 25 | } 26 | 27 | // Opera 28 | @-o-keyframes progress-bar-stripes { 29 | from { background-position: 0 0; } 30 | to { background-position: 40px 0; } 31 | } 32 | 33 | // Spec 34 | @keyframes progress-bar-stripes { 35 | from { background-position: 40px 0; } 36 | to { background-position: 0 0; } 37 | } 38 | 39 | 40 | 41 | // THE BARS 42 | // -------- 43 | 44 | // Outer container 45 | .progress { 46 | overflow: hidden; 47 | height: @baseLineHeight; 48 | margin-bottom: @baseLineHeight; 49 | #gradient > .vertical(#f5f5f5, #f9f9f9); 50 | .box-shadow(inset 0 1px 2px rgba(0,0,0,.1)); 51 | .border-radius(@baseBorderRadius); 52 | } 53 | 54 | // Bar of progress 55 | .progress .bar { 56 | width: 0%; 57 | height: 100%; 58 | color: @white; 59 | float: left; 60 | font-size: 12px; 61 | text-align: center; 62 | text-shadow: 0 -1px 0 rgba(0,0,0,.25); 63 | #gradient > .vertical(#149bdf, #0480be); 64 | .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15)); 65 | .box-sizing(border-box); 66 | .transition(width .6s ease); 67 | } 68 | .progress .bar + .bar { 69 | .box-shadow(~"inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15)"); 70 | } 71 | 72 | // Striped bars 73 | .progress-striped .bar { 74 | #gradient > .striped(#149bdf); 75 | .background-size(40px 40px); 76 | } 77 | 78 | // Call animation for the active one 79 | .progress.active .bar { 80 | -webkit-animation: progress-bar-stripes 2s linear infinite; 81 | -moz-animation: progress-bar-stripes 2s linear infinite; 82 | -ms-animation: progress-bar-stripes 2s linear infinite; 83 | -o-animation: progress-bar-stripes 2s linear infinite; 84 | animation: progress-bar-stripes 2s linear infinite; 85 | } 86 | 87 | 88 | 89 | // COLORS 90 | // ------ 91 | 92 | // Danger (red) 93 | .progress-danger .bar, .progress .bar-danger { 94 | #gradient > .vertical(#ee5f5b, #c43c35); 95 | } 96 | .progress-danger.progress-striped .bar, .progress-striped .bar-danger { 97 | #gradient > .striped(#ee5f5b); 98 | } 99 | 100 | // Success (green) 101 | .progress-success .bar, .progress .bar-success { 102 | #gradient > .vertical(#62c462, #57a957); 103 | } 104 | .progress-success.progress-striped .bar, .progress-striped .bar-success { 105 | #gradient > .striped(#62c462); 106 | } 107 | 108 | // Info (teal) 109 | .progress-info .bar, .progress .bar-info { 110 | #gradient > .vertical(#5bc0de, #339bb9); 111 | } 112 | .progress-info.progress-striped .bar, .progress-striped .bar-info { 113 | #gradient > .striped(#5bc0de); 114 | } 115 | 116 | // Warning (orange) 117 | .progress-warning .bar, .progress .bar-warning { 118 | #gradient > .vertical(lighten(@orange, 15%), @orange); 119 | } 120 | .progress-warning.progress-striped .bar, .progress-striped .bar-warning { 121 | #gradient > .striped(lighten(@orange, 15%)); 122 | } 123 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/reset.less: -------------------------------------------------------------------------------- 1 | // 2 | // Reset CSS 3 | // Adapted from http://github.com/necolas/normalize.css 4 | // -------------------------------------------------- 5 | 6 | 7 | // Display in IE6-9 and FF3 8 | // ------------------------- 9 | 10 | article, 11 | aside, 12 | details, 13 | figcaption, 14 | figure, 15 | footer, 16 | header, 17 | hgroup, 18 | nav, 19 | section { 20 | display: block; 21 | } 22 | 23 | // Display block in IE6-9 and FF3 24 | // ------------------------- 25 | 26 | audio, 27 | canvas, 28 | video { 29 | display: inline-block; 30 | *display: inline; 31 | *zoom: 1; 32 | } 33 | 34 | // Prevents modern browsers from displaying 'audio' without controls 35 | // ------------------------- 36 | 37 | audio:not([controls]) { 38 | display: none; 39 | } 40 | 41 | // Base settings 42 | // ------------------------- 43 | 44 | html { 45 | font-size: 100%; 46 | -webkit-text-size-adjust: 100%; 47 | -ms-text-size-adjust: 100%; 48 | } 49 | // Focus states 50 | a:focus { 51 | .tab-focus(); 52 | } 53 | // Hover & Active 54 | a:hover, 55 | a:active { 56 | outline: 0; 57 | } 58 | 59 | // Prevents sub and sup affecting line-height in all browsers 60 | // ------------------------- 61 | 62 | sub, 63 | sup { 64 | position: relative; 65 | font-size: 75%; 66 | line-height: 0; 67 | vertical-align: baseline; 68 | } 69 | sup { 70 | top: -0.5em; 71 | } 72 | sub { 73 | bottom: -0.25em; 74 | } 75 | 76 | // Img border in a's and image quality 77 | // ------------------------- 78 | 79 | img { 80 | /* Responsive images (ensure images don't scale beyond their parents) */ 81 | max-width: 100%; /* Part 1: Set a maxium relative to the parent */ 82 | width: auto\9; /* IE7-8 need help adjusting responsive images */ 83 | height: auto; /* Part 2: Scale the height according to the width, otherwise you get stretching */ 84 | 85 | vertical-align: middle; 86 | border: 0; 87 | -ms-interpolation-mode: bicubic; 88 | } 89 | 90 | // Prevent max-width from affecting Google Maps 91 | #map_canvas img, 92 | .google-maps img { 93 | max-width: none; 94 | } 95 | 96 | // Forms 97 | // ------------------------- 98 | 99 | // Font size in all browsers, margin changes, misc consistency 100 | button, 101 | input, 102 | select, 103 | textarea { 104 | margin: 0; 105 | font-size: 100%; 106 | vertical-align: middle; 107 | } 108 | button, 109 | input { 110 | *overflow: visible; // Inner spacing ie IE6/7 111 | line-height: normal; // FF3/4 have !important on line-height in UA stylesheet 112 | } 113 | button::-moz-focus-inner, 114 | input::-moz-focus-inner { // Inner padding and border oddities in FF3/4 115 | padding: 0; 116 | border: 0; 117 | } 118 | button, 119 | html input[type="button"], // Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 120 | input[type="reset"], 121 | input[type="submit"] { 122 | -webkit-appearance: button; // Corrects inability to style clickable `input` types in iOS. 123 | cursor: pointer; // Improves usability and consistency of cursor style between image-type `input` and others. 124 | } 125 | label, 126 | select, 127 | button, 128 | input[type="button"], 129 | input[type="reset"], 130 | input[type="submit"], 131 | input[type="radio"], 132 | input[type="checkbox"] { 133 | cursor: pointer; // Improves usability and consistency of cursor style between image-type `input` and others. 134 | } 135 | input[type="search"] { // Appearance in Safari/Chrome 136 | .box-sizing(content-box); 137 | -webkit-appearance: textfield; 138 | } 139 | input[type="search"]::-webkit-search-decoration, 140 | input[type="search"]::-webkit-search-cancel-button { 141 | -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5 142 | } 143 | textarea { 144 | overflow: auto; // Remove vertical scrollbar in IE6-9 145 | vertical-align: top; // Readability and alignment cross-browser 146 | } 147 | 148 | 149 | // Printing 150 | // ------------------------- 151 | // Source: https://github.com/h5bp/html5-boilerplate/blob/master/css/main.css 152 | 153 | @media print { 154 | 155 | * { 156 | text-shadow: none !important; 157 | color: #000 !important; // Black prints faster: h5bp.com/s 158 | background: transparent !important; 159 | box-shadow: none !important; 160 | } 161 | 162 | a, 163 | a:visited { 164 | text-decoration: underline; 165 | } 166 | 167 | a[href]:after { 168 | content: " (" attr(href) ")"; 169 | } 170 | 171 | abbr[title]:after { 172 | content: " (" attr(title) ")"; 173 | } 174 | 175 | // Don't show links for images, or javascript/internal links 176 | .ir a:after, 177 | a[href^="javascript:"]:after, 178 | a[href^="#"]:after { 179 | content: ""; 180 | } 181 | 182 | pre, 183 | blockquote { 184 | border: 1px solid #999; 185 | page-break-inside: avoid; 186 | } 187 | 188 | thead { 189 | display: table-header-group; // h5bp.com/t 190 | } 191 | 192 | tr, 193 | img { 194 | page-break-inside: avoid; 195 | } 196 | 197 | img { 198 | max-width: 100% !important; 199 | } 200 | 201 | @page { 202 | margin: 0.5cm; 203 | } 204 | 205 | p, 206 | h2, 207 | h3 { 208 | orphans: 3; 209 | widows: 3; 210 | } 211 | 212 | h2, 213 | h3 { 214 | page-break-after: avoid; 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/responsive-1200px-min.less: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Large desktop and up 3 | // -------------------------------------------------- 4 | 5 | 6 | @media (min-width: 1200px) { 7 | 8 | // Fixed grid 9 | #grid > .core(@gridColumnWidth1200, @gridGutterWidth1200); 10 | 11 | // Fluid grid 12 | #grid > .fluid(@fluidGridColumnWidth1200, @fluidGridGutterWidth1200); 13 | 14 | // Input grid 15 | #grid > .input(@gridColumnWidth1200, @gridGutterWidth1200); 16 | 17 | // Thumbnails 18 | .thumbnails { 19 | margin-left: -@gridGutterWidth1200; 20 | } 21 | .thumbnails > li { 22 | margin-left: @gridGutterWidth1200; 23 | } 24 | .row-fluid .thumbnails { 25 | margin-left: 0; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/responsive-767px-max.less: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Landscape phone to desktop/tablet 3 | // -------------------------------------------------- 4 | 5 | 6 | @media (max-width: 767px) { 7 | 8 | // Padding to set content in a bit 9 | body { 10 | padding-left: 20px; 11 | padding-right: 20px; 12 | } 13 | // Negative indent the now static "fixed" navbar 14 | .navbar-fixed-top, 15 | .navbar-fixed-bottom, 16 | .navbar-static-top { 17 | margin-left: -20px; 18 | margin-right: -20px; 19 | } 20 | // Remove padding on container given explicit padding set on body 21 | .container-fluid { 22 | padding: 0; 23 | } 24 | 25 | // TYPOGRAPHY 26 | // ---------- 27 | // Reset horizontal dl 28 | .dl-horizontal { 29 | dt { 30 | float: none; 31 | clear: none; 32 | width: auto; 33 | text-align: left; 34 | } 35 | dd { 36 | margin-left: 0; 37 | } 38 | } 39 | 40 | // GRID & CONTAINERS 41 | // ----------------- 42 | // Remove width from containers 43 | .container { 44 | width: auto; 45 | } 46 | // Fluid rows 47 | .row-fluid { 48 | width: 100%; 49 | } 50 | // Undo negative margin on rows and thumbnails 51 | .row, 52 | .thumbnails { 53 | margin-left: 0; 54 | } 55 | .thumbnails > li { 56 | float: none; 57 | margin-left: 0; // Reset the default margin for all li elements when no .span* classes are present 58 | } 59 | // Make all grid-sized elements block level again 60 | [class*="span"], 61 | .uneditable-input[class*="span"], // Makes uneditable inputs full-width when using grid sizing 62 | .row-fluid [class*="span"] { 63 | float: none; 64 | display: block; 65 | width: 100%; 66 | margin-left: 0; 67 | .box-sizing(border-box); 68 | } 69 | .span12, 70 | .row-fluid .span12 { 71 | width: 100%; 72 | .box-sizing(border-box); 73 | } 74 | .row-fluid [class*="offset"]:first-child { 75 | margin-left: 0; 76 | } 77 | 78 | // FORM FIELDS 79 | // ----------- 80 | // Make span* classes full width 81 | .input-large, 82 | .input-xlarge, 83 | .input-xxlarge, 84 | input[class*="span"], 85 | select[class*="span"], 86 | textarea[class*="span"], 87 | .uneditable-input { 88 | .input-block-level(); 89 | } 90 | // But don't let it screw up prepend/append inputs 91 | .input-prepend input, 92 | .input-append input, 93 | .input-prepend input[class*="span"], 94 | .input-append input[class*="span"] { 95 | display: inline-block; // redeclare so they don't wrap to new lines 96 | width: auto; 97 | } 98 | .controls-row [class*="span"] + [class*="span"] { 99 | margin-left: 0; 100 | } 101 | 102 | // Modals 103 | .modal { 104 | position: fixed; 105 | top: 20px; 106 | left: 20px; 107 | right: 20px; 108 | width: auto; 109 | margin: 0; 110 | &.fade { top: -100px; } 111 | &.fade.in { top: 20px; } 112 | } 113 | 114 | } 115 | 116 | 117 | 118 | // UP TO LANDSCAPE PHONE 119 | // --------------------- 120 | 121 | @media (max-width: 480px) { 122 | 123 | // Smooth out the collapsing/expanding nav 124 | .nav-collapse { 125 | -webkit-transform: translate3d(0, 0, 0); // activate the GPU 126 | } 127 | 128 | // Block level the page header small tag for readability 129 | .page-header h1 small { 130 | display: block; 131 | line-height: @baseLineHeight; 132 | } 133 | 134 | // Update checkboxes for iOS 135 | input[type="checkbox"], 136 | input[type="radio"] { 137 | border: 1px solid #ccc; 138 | } 139 | 140 | // Remove the horizontal form styles 141 | .form-horizontal { 142 | .control-label { 143 | float: none; 144 | width: auto; 145 | padding-top: 0; 146 | text-align: left; 147 | } 148 | // Move over all input controls and content 149 | .controls { 150 | margin-left: 0; 151 | } 152 | // Move the options list down to align with labels 153 | .control-list { 154 | padding-top: 0; // has to be padding because margin collaspes 155 | } 156 | // Move over buttons in .form-actions to align with .controls 157 | .form-actions { 158 | padding-left: 10px; 159 | padding-right: 10px; 160 | } 161 | } 162 | 163 | // Medias 164 | // Reset float and spacing to stack 165 | .media .pull-left, 166 | .media .pull-right { 167 | float: none; 168 | display: block; 169 | margin-bottom: 10px; 170 | } 171 | // Remove side margins since we stack instead of indent 172 | .media-object { 173 | margin-right: 0; 174 | margin-left: 0; 175 | } 176 | 177 | // Modals 178 | .modal { 179 | top: 10px; 180 | left: 10px; 181 | right: 10px; 182 | } 183 | .modal-header .close { 184 | padding: 10px; 185 | margin: -10px; 186 | } 187 | 188 | // Carousel 189 | .carousel-caption { 190 | position: static; 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/responsive-768px-979px.less: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Tablet to desktop 3 | // -------------------------------------------------- 4 | 5 | 6 | @media (min-width: 768px) and (max-width: 979px) { 7 | 8 | // Fixed grid 9 | #grid > .core(@gridColumnWidth768, @gridGutterWidth768); 10 | 11 | // Fluid grid 12 | #grid > .fluid(@fluidGridColumnWidth768, @fluidGridGutterWidth768); 13 | 14 | // Input grid 15 | #grid > .input(@gridColumnWidth768, @gridGutterWidth768); 16 | 17 | // No need to reset .thumbnails here since it's the same @gridGutterWidth 18 | 19 | } 20 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/responsive-navbar.less: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Navbar 3 | // -------------------------------------------------- 4 | 5 | 6 | // TABLETS AND BELOW 7 | // ----------------- 8 | @media (max-width: @navbarCollapseWidth) { 9 | 10 | // UNFIX THE TOPBAR 11 | // ---------------- 12 | // Remove any padding from the body 13 | body { 14 | padding-top: 0; 15 | } 16 | // Unfix the navbars 17 | .navbar-fixed-top, 18 | .navbar-fixed-bottom { 19 | position: static; 20 | } 21 | .navbar-fixed-top { 22 | margin-bottom: @baseLineHeight; 23 | } 24 | .navbar-fixed-bottom { 25 | margin-top: @baseLineHeight; 26 | } 27 | .navbar-fixed-top .navbar-inner, 28 | .navbar-fixed-bottom .navbar-inner { 29 | padding: 5px; 30 | } 31 | .navbar .container { 32 | width: auto; 33 | padding: 0; 34 | } 35 | // Account for brand name 36 | .navbar .brand { 37 | padding-left: 10px; 38 | padding-right: 10px; 39 | margin: 0 0 0 -5px; 40 | } 41 | 42 | // COLLAPSIBLE NAVBAR 43 | // ------------------ 44 | // Nav collapse clears brand 45 | .nav-collapse { 46 | clear: both; 47 | } 48 | // Block-level the nav 49 | .nav-collapse .nav { 50 | float: none; 51 | margin: 0 0 (@baseLineHeight / 2); 52 | } 53 | .nav-collapse .nav > li { 54 | float: none; 55 | } 56 | .nav-collapse .nav > li > a { 57 | margin-bottom: 2px; 58 | } 59 | .nav-collapse .nav > .divider-vertical { 60 | display: none; 61 | } 62 | .nav-collapse .nav .nav-header { 63 | color: @navbarText; 64 | text-shadow: none; 65 | } 66 | // Nav and dropdown links in navbar 67 | .nav-collapse .nav > li > a, 68 | .nav-collapse .dropdown-menu a { 69 | padding: 9px 15px; 70 | font-weight: bold; 71 | color: @navbarLinkColor; 72 | .border-radius(3px); 73 | } 74 | // Buttons 75 | .nav-collapse .btn { 76 | padding: 4px 10px 4px; 77 | font-weight: normal; 78 | .border-radius(@baseBorderRadius); 79 | } 80 | .nav-collapse .dropdown-menu li + li a { 81 | margin-bottom: 2px; 82 | } 83 | .nav-collapse .nav > li > a:hover, 84 | .nav-collapse .nav > li > a:focus, 85 | .nav-collapse .dropdown-menu a:hover, 86 | .nav-collapse .dropdown-menu a:focus { 87 | background-color: @navbarBackground; 88 | } 89 | .navbar-inverse .nav-collapse .nav > li > a, 90 | .navbar-inverse .nav-collapse .dropdown-menu a { 91 | color: @navbarInverseLinkColor; 92 | } 93 | .navbar-inverse .nav-collapse .nav > li > a:hover, 94 | .navbar-inverse .nav-collapse .nav > li > a:focus, 95 | .navbar-inverse .nav-collapse .dropdown-menu a:hover, 96 | .navbar-inverse .nav-collapse .dropdown-menu a:focus { 97 | background-color: @navbarInverseBackground; 98 | } 99 | // Buttons in the navbar 100 | .nav-collapse.in .btn-group { 101 | margin-top: 5px; 102 | padding: 0; 103 | } 104 | // Dropdowns in the navbar 105 | .nav-collapse .dropdown-menu { 106 | position: static; 107 | top: auto; 108 | left: auto; 109 | float: none; 110 | display: none; 111 | max-width: none; 112 | margin: 0 15px; 113 | padding: 0; 114 | background-color: transparent; 115 | border: none; 116 | .border-radius(0); 117 | .box-shadow(none); 118 | } 119 | .nav-collapse .open > .dropdown-menu { 120 | display: block; 121 | } 122 | 123 | .nav-collapse .dropdown-menu:before, 124 | .nav-collapse .dropdown-menu:after { 125 | display: none; 126 | } 127 | .nav-collapse .dropdown-menu .divider { 128 | display: none; 129 | } 130 | .nav-collapse .nav > li > .dropdown-menu { 131 | &:before, 132 | &:after { 133 | display: none; 134 | } 135 | } 136 | // Forms in navbar 137 | .nav-collapse .navbar-form, 138 | .nav-collapse .navbar-search { 139 | float: none; 140 | padding: (@baseLineHeight / 2) 15px; 141 | margin: (@baseLineHeight / 2) 0; 142 | border-top: 1px solid @navbarBackground; 143 | border-bottom: 1px solid @navbarBackground; 144 | .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1)"); 145 | } 146 | .navbar-inverse .nav-collapse .navbar-form, 147 | .navbar-inverse .nav-collapse .navbar-search { 148 | border-top-color: @navbarInverseBackground; 149 | border-bottom-color: @navbarInverseBackground; 150 | } 151 | // Pull right (secondary) nav content 152 | .navbar .nav-collapse .nav.pull-right { 153 | float: none; 154 | margin-left: 0; 155 | } 156 | // Hide everything in the navbar save .brand and toggle button */ 157 | .nav-collapse, 158 | .nav-collapse.collapse { 159 | overflow: hidden; 160 | height: 0; 161 | } 162 | // Navbar button 163 | .navbar .btn-navbar { 164 | display: block; 165 | } 166 | 167 | // STATIC NAVBAR 168 | // ------------- 169 | .navbar-static .navbar-inner { 170 | padding-left: 10px; 171 | padding-right: 10px; 172 | } 173 | 174 | 175 | } 176 | 177 | 178 | // DEFAULT DESKTOP 179 | // --------------- 180 | 181 | @media (min-width: @navbarCollapseDesktopWidth) { 182 | 183 | // Required to make the collapsing navbar work on regular desktops 184 | .nav-collapse.collapse { 185 | height: auto !important; 186 | overflow: visible !important; 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/responsive-utilities.less: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Utility classes 3 | // -------------------------------------------------- 4 | 5 | 6 | // IE10 Metro responsive 7 | // Required for Windows 8 Metro split-screen snapping with IE10 8 | // Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/ 9 | @-ms-viewport{ 10 | width: device-width; 11 | } 12 | 13 | // Hide from screenreaders and browsers 14 | // Credit: HTML5 Boilerplate 15 | .hidden { 16 | display: none; 17 | visibility: hidden; 18 | } 19 | 20 | // Visibility utilities 21 | 22 | // For desktops 23 | .visible-phone { display: none !important; } 24 | .visible-tablet { display: none !important; } 25 | .hidden-phone { } 26 | .hidden-tablet { } 27 | .hidden-desktop { display: none !important; } 28 | .visible-desktop { display: inherit !important; } 29 | 30 | // Tablets & small desktops only 31 | @media (min-width: 768px) and (max-width: 979px) { 32 | // Hide everything else 33 | .hidden-desktop { display: inherit !important; } 34 | .visible-desktop { display: none !important ; } 35 | // Show 36 | .visible-tablet { display: inherit !important; } 37 | // Hide 38 | .hidden-tablet { display: none !important; } 39 | } 40 | 41 | // Phones only 42 | @media (max-width: 767px) { 43 | // Hide everything else 44 | .hidden-desktop { display: inherit !important; } 45 | .visible-desktop { display: none !important; } 46 | // Show 47 | .visible-phone { display: inherit !important; } // Use inherit to restore previous behavior 48 | // Hide 49 | .hidden-phone { display: none !important; } 50 | } 51 | 52 | // Print utilities 53 | .visible-print { display: none !important; } 54 | .hidden-print { } 55 | 56 | @media print { 57 | .visible-print { display: inherit !important; } 58 | .hidden-print { display: none !important; } 59 | } 60 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/responsive.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.3.1 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */ 10 | 11 | 12 | // Responsive.less 13 | // For phone and tablet devices 14 | // ------------------------------------------------------------- 15 | 16 | 17 | // REPEAT VARIABLES & MIXINS 18 | // ------------------------- 19 | // Required since we compile the responsive stuff separately 20 | 21 | @import "variables.less"; // Modify this for custom colors, font-sizes, etc 22 | @import "mixins.less"; 23 | 24 | 25 | // RESPONSIVE CLASSES 26 | // ------------------ 27 | 28 | @import "responsive-utilities.less"; 29 | 30 | 31 | // MEDIA QUERIES 32 | // ------------------ 33 | 34 | // Large desktops 35 | @import "responsive-1200px-min.less"; 36 | 37 | // Tablets to regular desktops 38 | @import "responsive-768px-979px.less"; 39 | 40 | // Phones to portrait tablets and narrow desktops 41 | @import "responsive-767px-max.less"; 42 | 43 | 44 | // RESPONSIVE NAVBAR 45 | // ------------------ 46 | 47 | // From 979px and below, show a button to toggle navbar contents 48 | @import "responsive-navbar.less"; 49 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/scaffolding.less: -------------------------------------------------------------------------------- 1 | // 2 | // Scaffolding 3 | // -------------------------------------------------- 4 | 5 | 6 | // Body reset 7 | // ------------------------- 8 | 9 | body { 10 | margin: 0; 11 | font-family: @baseFontFamily; 12 | font-size: @baseFontSize; 13 | line-height: @baseLineHeight; 14 | color: @textColor; 15 | background-color: @bodyBackground; 16 | } 17 | 18 | 19 | // Links 20 | // ------------------------- 21 | 22 | a { 23 | color: @linkColor; 24 | text-decoration: none; 25 | } 26 | a:hover, 27 | a:focus { 28 | color: @linkColorHover; 29 | text-decoration: underline; 30 | } 31 | 32 | 33 | // Images 34 | // ------------------------- 35 | 36 | // Rounded corners 37 | .img-rounded { 38 | .border-radius(6px); 39 | } 40 | 41 | // Add polaroid-esque trim 42 | .img-polaroid { 43 | padding: 4px; 44 | background-color: #fff; 45 | border: 1px solid #ccc; 46 | border: 1px solid rgba(0,0,0,.2); 47 | .box-shadow(0 1px 3px rgba(0,0,0,.1)); 48 | } 49 | 50 | // Perfect circle 51 | .img-circle { 52 | .border-radius(500px); // crank the border-radius so it works with most reasonably sized images 53 | } 54 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/tables.less: -------------------------------------------------------------------------------- 1 | // 2 | // Tables 3 | // -------------------------------------------------- 4 | 5 | 6 | // BASE TABLES 7 | // ----------------- 8 | 9 | table { 10 | max-width: 100%; 11 | background-color: @tableBackground; 12 | border-collapse: collapse; 13 | border-spacing: 0; 14 | } 15 | 16 | // BASELINE STYLES 17 | // --------------- 18 | 19 | .table { 20 | width: 100%; 21 | margin-bottom: @baseLineHeight; 22 | // Cells 23 | th, 24 | td { 25 | padding: 8px; 26 | line-height: @baseLineHeight; 27 | text-align: left; 28 | vertical-align: top; 29 | border-top: 1px solid @tableBorder; 30 | } 31 | th { 32 | font-weight: bold; 33 | } 34 | // Bottom align for column headings 35 | thead th { 36 | vertical-align: bottom; 37 | } 38 | // Remove top border from thead by default 39 | caption + thead tr:first-child th, 40 | caption + thead tr:first-child td, 41 | colgroup + thead tr:first-child th, 42 | colgroup + thead tr:first-child td, 43 | thead:first-child tr:first-child th, 44 | thead:first-child tr:first-child td { 45 | border-top: 0; 46 | } 47 | // Account for multiple tbody instances 48 | tbody + tbody { 49 | border-top: 2px solid @tableBorder; 50 | } 51 | 52 | // Nesting 53 | .table { 54 | background-color: @bodyBackground; 55 | } 56 | } 57 | 58 | 59 | 60 | // CONDENSED TABLE W/ HALF PADDING 61 | // ------------------------------- 62 | 63 | .table-condensed { 64 | th, 65 | td { 66 | padding: 4px 5px; 67 | } 68 | } 69 | 70 | 71 | // BORDERED VERSION 72 | // ---------------- 73 | 74 | .table-bordered { 75 | border: 1px solid @tableBorder; 76 | border-collapse: separate; // Done so we can round those corners! 77 | *border-collapse: collapse; // IE7 can't round corners anyway 78 | border-left: 0; 79 | .border-radius(@baseBorderRadius); 80 | th, 81 | td { 82 | border-left: 1px solid @tableBorder; 83 | } 84 | // Prevent a double border 85 | caption + thead tr:first-child th, 86 | caption + tbody tr:first-child th, 87 | caption + tbody tr:first-child td, 88 | colgroup + thead tr:first-child th, 89 | colgroup + tbody tr:first-child th, 90 | colgroup + tbody tr:first-child td, 91 | thead:first-child tr:first-child th, 92 | tbody:first-child tr:first-child th, 93 | tbody:first-child tr:first-child td { 94 | border-top: 0; 95 | } 96 | // For first th/td in the first row in the first thead or tbody 97 | thead:first-child tr:first-child > th:first-child, 98 | tbody:first-child tr:first-child > td:first-child, 99 | tbody:first-child tr:first-child > th:first-child { 100 | .border-top-left-radius(@baseBorderRadius); 101 | } 102 | // For last th/td in the first row in the first thead or tbody 103 | thead:first-child tr:first-child > th:last-child, 104 | tbody:first-child tr:first-child > td:last-child, 105 | tbody:first-child tr:first-child > th:last-child { 106 | .border-top-right-radius(@baseBorderRadius); 107 | } 108 | // For first th/td (can be either) in the last row in the last thead, tbody, and tfoot 109 | thead:last-child tr:last-child > th:first-child, 110 | tbody:last-child tr:last-child > td:first-child, 111 | tbody:last-child tr:last-child > th:first-child, 112 | tfoot:last-child tr:last-child > td:first-child, 113 | tfoot:last-child tr:last-child > th:first-child { 114 | .border-bottom-left-radius(@baseBorderRadius); 115 | } 116 | // For last th/td (can be either) in the last row in the last thead, tbody, and tfoot 117 | thead:last-child tr:last-child > th:last-child, 118 | tbody:last-child tr:last-child > td:last-child, 119 | tbody:last-child tr:last-child > th:last-child, 120 | tfoot:last-child tr:last-child > td:last-child, 121 | tfoot:last-child tr:last-child > th:last-child { 122 | .border-bottom-right-radius(@baseBorderRadius); 123 | } 124 | 125 | // Clear border-radius for first and last td in the last row in the last tbody for table with tfoot 126 | tfoot + tbody:last-child tr:last-child td:first-child { 127 | .border-bottom-left-radius(0); 128 | } 129 | tfoot + tbody:last-child tr:last-child td:last-child { 130 | .border-bottom-right-radius(0); 131 | } 132 | 133 | // Special fixes to round the left border on the first td/th 134 | caption + thead tr:first-child th:first-child, 135 | caption + tbody tr:first-child td:first-child, 136 | colgroup + thead tr:first-child th:first-child, 137 | colgroup + tbody tr:first-child td:first-child { 138 | .border-top-left-radius(@baseBorderRadius); 139 | } 140 | caption + thead tr:first-child th:last-child, 141 | caption + tbody tr:first-child td:last-child, 142 | colgroup + thead tr:first-child th:last-child, 143 | colgroup + tbody tr:first-child td:last-child { 144 | .border-top-right-radius(@baseBorderRadius); 145 | } 146 | 147 | } 148 | 149 | 150 | 151 | 152 | // ZEBRA-STRIPING 153 | // -------------- 154 | 155 | // Default zebra-stripe styles (alternating gray and transparent backgrounds) 156 | .table-striped { 157 | tbody { 158 | > tr:nth-child(odd) > td, 159 | > tr:nth-child(odd) > th { 160 | background-color: @tableBackgroundAccent; 161 | } 162 | } 163 | } 164 | 165 | 166 | // HOVER EFFECT 167 | // ------------ 168 | // Placed here since it has to come after the potential zebra striping 169 | .table-hover { 170 | tbody { 171 | tr:hover > td, 172 | tr:hover > th { 173 | background-color: @tableBackgroundHover; 174 | } 175 | } 176 | } 177 | 178 | 179 | // TABLE CELL SIZING 180 | // ----------------- 181 | 182 | // Reset default grid behavior 183 | table td[class*="span"], 184 | table th[class*="span"], 185 | .row-fluid table td[class*="span"], 186 | .row-fluid table th[class*="span"] { 187 | display: table-cell; 188 | float: none; // undo default grid column styles 189 | margin-left: 0; // undo default grid column styles 190 | } 191 | 192 | // Change the column widths to account for td/th padding 193 | .table td, 194 | .table th { 195 | &.span1 { .tableColumns(1); } 196 | &.span2 { .tableColumns(2); } 197 | &.span3 { .tableColumns(3); } 198 | &.span4 { .tableColumns(4); } 199 | &.span5 { .tableColumns(5); } 200 | &.span6 { .tableColumns(6); } 201 | &.span7 { .tableColumns(7); } 202 | &.span8 { .tableColumns(8); } 203 | &.span9 { .tableColumns(9); } 204 | &.span10 { .tableColumns(10); } 205 | &.span11 { .tableColumns(11); } 206 | &.span12 { .tableColumns(12); } 207 | } 208 | 209 | 210 | 211 | // TABLE BACKGROUNDS 212 | // ----------------- 213 | // Exact selectors below required to override .table-striped 214 | 215 | .table tbody tr { 216 | &.success > td { 217 | background-color: @successBackground; 218 | } 219 | &.error > td { 220 | background-color: @errorBackground; 221 | } 222 | &.warning > td { 223 | background-color: @warningBackground; 224 | } 225 | &.info > td { 226 | background-color: @infoBackground; 227 | } 228 | } 229 | 230 | // Hover states for .table-hover 231 | .table-hover tbody tr { 232 | &.success:hover > td { 233 | background-color: darken(@successBackground, 5%); 234 | } 235 | &.error:hover > td { 236 | background-color: darken(@errorBackground, 5%); 237 | } 238 | &.warning:hover > td { 239 | background-color: darken(@warningBackground, 5%); 240 | } 241 | &.info:hover > td { 242 | background-color: darken(@infoBackground, 5%); 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/thumbnails.less: -------------------------------------------------------------------------------- 1 | // 2 | // Thumbnails 3 | // -------------------------------------------------- 4 | 5 | 6 | // Note: `.thumbnails` and `.thumbnails > li` are overriden in responsive files 7 | 8 | // Make wrapper ul behave like the grid 9 | .thumbnails { 10 | margin-left: -@gridGutterWidth; 11 | list-style: none; 12 | .clearfix(); 13 | } 14 | // Fluid rows have no left margin 15 | .row-fluid .thumbnails { 16 | margin-left: 0; 17 | } 18 | 19 | // Float li to make thumbnails appear in a row 20 | .thumbnails > li { 21 | float: left; // Explicity set the float since we don't require .span* classes 22 | margin-bottom: @baseLineHeight; 23 | margin-left: @gridGutterWidth; 24 | } 25 | 26 | // The actual thumbnail (can be `a` or `div`) 27 | .thumbnail { 28 | display: block; 29 | padding: 4px; 30 | line-height: @baseLineHeight; 31 | border: 1px solid #ddd; 32 | .border-radius(@baseBorderRadius); 33 | .box-shadow(0 1px 3px rgba(0,0,0,.055)); 34 | .transition(all .2s ease-in-out); 35 | } 36 | // Add a hover/focus state for linked versions only 37 | a.thumbnail:hover, 38 | a.thumbnail:focus { 39 | border-color: @linkColor; 40 | .box-shadow(0 1px 4px rgba(0,105,214,.25)); 41 | } 42 | 43 | // Images and captions 44 | .thumbnail > img { 45 | display: block; 46 | max-width: 100%; 47 | margin-left: auto; 48 | margin-right: auto; 49 | } 50 | .thumbnail .caption { 51 | padding: 9px; 52 | color: @gray; 53 | } 54 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/tooltip.less: -------------------------------------------------------------------------------- 1 | // 2 | // Tooltips 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .tooltip { 8 | position: absolute; 9 | z-index: @zindexTooltip; 10 | display: block; 11 | visibility: visible; 12 | font-size: 11px; 13 | line-height: 1.4; 14 | .opacity(0); 15 | &.in { .opacity(80); } 16 | &.top { margin-top: -3px; padding: 5px 0; } 17 | &.right { margin-left: 3px; padding: 0 5px; } 18 | &.bottom { margin-top: 3px; padding: 5px 0; } 19 | &.left { margin-left: -3px; padding: 0 5px; } 20 | } 21 | 22 | // Wrapper for the tooltip content 23 | .tooltip-inner { 24 | max-width: 200px; 25 | padding: 8px; 26 | color: @tooltipColor; 27 | text-align: center; 28 | text-decoration: none; 29 | background-color: @tooltipBackground; 30 | .border-radius(@baseBorderRadius); 31 | } 32 | 33 | // Arrows 34 | .tooltip-arrow { 35 | position: absolute; 36 | width: 0; 37 | height: 0; 38 | border-color: transparent; 39 | border-style: solid; 40 | } 41 | .tooltip { 42 | &.top .tooltip-arrow { 43 | bottom: 0; 44 | left: 50%; 45 | margin-left: -@tooltipArrowWidth; 46 | border-width: @tooltipArrowWidth @tooltipArrowWidth 0; 47 | border-top-color: @tooltipArrowColor; 48 | } 49 | &.right .tooltip-arrow { 50 | top: 50%; 51 | left: 0; 52 | margin-top: -@tooltipArrowWidth; 53 | border-width: @tooltipArrowWidth @tooltipArrowWidth @tooltipArrowWidth 0; 54 | border-right-color: @tooltipArrowColor; 55 | } 56 | &.left .tooltip-arrow { 57 | top: 50%; 58 | right: 0; 59 | margin-top: -@tooltipArrowWidth; 60 | border-width: @tooltipArrowWidth 0 @tooltipArrowWidth @tooltipArrowWidth; 61 | border-left-color: @tooltipArrowColor; 62 | } 63 | &.bottom .tooltip-arrow { 64 | top: 0; 65 | left: 50%; 66 | margin-left: -@tooltipArrowWidth; 67 | border-width: 0 @tooltipArrowWidth @tooltipArrowWidth; 68 | border-bottom-color: @tooltipArrowColor; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/type.less: -------------------------------------------------------------------------------- 1 | // 2 | // Typography 3 | // -------------------------------------------------- 4 | 5 | 6 | // Body text 7 | // ------------------------- 8 | 9 | p { 10 | margin: 0 0 @baseLineHeight / 2; 11 | } 12 | .lead { 13 | margin-bottom: @baseLineHeight; 14 | font-size: @baseFontSize * 1.5; 15 | font-weight: 200; 16 | line-height: @baseLineHeight * 1.5; 17 | } 18 | 19 | 20 | // Emphasis & misc 21 | // ------------------------- 22 | 23 | // Ex: 14px base font * 85% = about 12px 24 | small { font-size: 85%; } 25 | 26 | strong { font-weight: bold; } 27 | em { font-style: italic; } 28 | cite { font-style: normal; } 29 | 30 | // Utility classes 31 | .muted { color: @grayLight; } 32 | a.muted:hover, 33 | a.muted:focus { color: darken(@grayLight, 10%); } 34 | 35 | .text-warning { color: @warningText; } 36 | a.text-warning:hover, 37 | a.text-warning:focus { color: darken(@warningText, 10%); } 38 | 39 | .text-error { color: @errorText; } 40 | a.text-error:hover, 41 | a.text-error:focus { color: darken(@errorText, 10%); } 42 | 43 | .text-info { color: @infoText; } 44 | a.text-info:hover, 45 | a.text-info:focus { color: darken(@infoText, 10%); } 46 | 47 | .text-success { color: @successText; } 48 | a.text-success:hover, 49 | a.text-success:focus { color: darken(@successText, 10%); } 50 | 51 | .text-left { text-align: left; } 52 | .text-right { text-align: right; } 53 | .text-center { text-align: center; } 54 | 55 | 56 | // Headings 57 | // ------------------------- 58 | 59 | h1, h2, h3, h4, h5, h6 { 60 | margin: (@baseLineHeight / 2) 0; 61 | font-family: @headingsFontFamily; 62 | font-weight: @headingsFontWeight; 63 | line-height: @baseLineHeight; 64 | color: @headingsColor; 65 | text-rendering: optimizelegibility; // Fix the character spacing for headings 66 | small { 67 | font-weight: normal; 68 | line-height: 1; 69 | color: @grayLight; 70 | } 71 | } 72 | 73 | h1, 74 | h2, 75 | h3 { line-height: @baseLineHeight * 2; } 76 | 77 | h1 { font-size: @baseFontSize * 2.75; } // ~38px 78 | h2 { font-size: @baseFontSize * 2.25; } // ~32px 79 | h3 { font-size: @baseFontSize * 1.75; } // ~24px 80 | h4 { font-size: @baseFontSize * 1.25; } // ~18px 81 | h5 { font-size: @baseFontSize; } 82 | h6 { font-size: @baseFontSize * 0.85; } // ~12px 83 | 84 | h1 small { font-size: @baseFontSize * 1.75; } // ~24px 85 | h2 small { font-size: @baseFontSize * 1.25; } // ~18px 86 | h3 small { font-size: @baseFontSize; } 87 | h4 small { font-size: @baseFontSize; } 88 | 89 | 90 | // Page header 91 | // ------------------------- 92 | 93 | .page-header { 94 | padding-bottom: (@baseLineHeight / 2) - 1; 95 | margin: @baseLineHeight 0 (@baseLineHeight * 1.5); 96 | border-bottom: 1px solid @grayLighter; 97 | } 98 | 99 | 100 | 101 | // Lists 102 | // -------------------------------------------------- 103 | 104 | // Unordered and Ordered lists 105 | ul, ol { 106 | padding: 0; 107 | margin: 0 0 @baseLineHeight / 2 25px; 108 | } 109 | ul ul, 110 | ul ol, 111 | ol ol, 112 | ol ul { 113 | margin-bottom: 0; 114 | } 115 | li { 116 | line-height: @baseLineHeight; 117 | } 118 | 119 | // Remove default list styles 120 | ul.unstyled, 121 | ol.unstyled { 122 | margin-left: 0; 123 | list-style: none; 124 | } 125 | 126 | // Single-line list items 127 | ul.inline, 128 | ol.inline { 129 | margin-left: 0; 130 | list-style: none; 131 | > li { 132 | display: inline-block; 133 | .ie7-inline-block(); 134 | padding-left: 5px; 135 | padding-right: 5px; 136 | } 137 | } 138 | 139 | // Description Lists 140 | dl { 141 | margin-bottom: @baseLineHeight; 142 | } 143 | dt, 144 | dd { 145 | line-height: @baseLineHeight; 146 | } 147 | dt { 148 | font-weight: bold; 149 | } 150 | dd { 151 | margin-left: @baseLineHeight / 2; 152 | } 153 | // Horizontal layout (like forms) 154 | .dl-horizontal { 155 | .clearfix(); // Ensure dl clears floats if empty dd elements present 156 | dt { 157 | float: left; 158 | width: @horizontalComponentOffset - 20; 159 | clear: left; 160 | text-align: right; 161 | .text-overflow(); 162 | } 163 | dd { 164 | margin-left: @horizontalComponentOffset; 165 | } 166 | } 167 | 168 | // MISC 169 | // ---- 170 | 171 | // Horizontal rules 172 | hr { 173 | margin: @baseLineHeight 0; 174 | border: 0; 175 | border-top: 1px solid @hrBorder; 176 | border-bottom: 1px solid @white; 177 | } 178 | 179 | // Abbreviations and acronyms 180 | abbr[title], 181 | // Added data-* attribute to help out our tooltip plugin, per https://github.com/twitter/bootstrap/issues/5257 182 | abbr[data-original-title] { 183 | cursor: help; 184 | border-bottom: 1px dotted @grayLight; 185 | } 186 | abbr.initialism { 187 | font-size: 90%; 188 | text-transform: uppercase; 189 | } 190 | 191 | // Blockquotes 192 | blockquote { 193 | padding: 0 0 0 15px; 194 | margin: 0 0 @baseLineHeight; 195 | border-left: 5px solid @grayLighter; 196 | p { 197 | margin-bottom: 0; 198 | font-size: @baseFontSize * 1.25; 199 | font-weight: 300; 200 | line-height: 1.25; 201 | } 202 | small { 203 | display: block; 204 | line-height: @baseLineHeight; 205 | color: @grayLight; 206 | &:before { 207 | content: '\2014 \00A0'; 208 | } 209 | } 210 | 211 | // Float right with text-align: right 212 | &.pull-right { 213 | float: right; 214 | padding-right: 15px; 215 | padding-left: 0; 216 | border-right: 5px solid @grayLighter; 217 | border-left: 0; 218 | p, 219 | small { 220 | text-align: right; 221 | } 222 | small { 223 | &:before { 224 | content: ''; 225 | } 226 | &:after { 227 | content: '\00A0 \2014'; 228 | } 229 | } 230 | } 231 | } 232 | 233 | // Quotes 234 | q:before, 235 | q:after, 236 | blockquote:before, 237 | blockquote:after { 238 | content: ""; 239 | } 240 | 241 | // Addresses 242 | address { 243 | display: block; 244 | margin-bottom: @baseLineHeight; 245 | font-style: normal; 246 | line-height: @baseLineHeight; 247 | } 248 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/utilities.less: -------------------------------------------------------------------------------- 1 | // 2 | // Utility classes 3 | // -------------------------------------------------- 4 | 5 | 6 | // Quick floats 7 | .pull-right { 8 | float: right; 9 | } 10 | .pull-left { 11 | float: left; 12 | } 13 | 14 | // Toggling content 15 | .hide { 16 | display: none; 17 | } 18 | .show { 19 | display: block; 20 | } 21 | 22 | // Visibility 23 | .invisible { 24 | visibility: hidden; 25 | } 26 | 27 | // For Affix plugin 28 | .affix { 29 | position: fixed; 30 | } 31 | -------------------------------------------------------------------------------- /WebContent/css/bootstrap/wells.less: -------------------------------------------------------------------------------- 1 | // 2 | // Wells 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .well { 8 | min-height: 20px; 9 | padding: 19px; 10 | margin-bottom: 20px; 11 | background-color: @wellBackground; 12 | border: 1px solid darken(@wellBackground, 7%); 13 | .border-radius(@baseBorderRadius); 14 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); 15 | blockquote { 16 | border-color: #ddd; 17 | border-color: rgba(0,0,0,.15); 18 | } 19 | } 20 | 21 | // Sizes 22 | .well-large { 23 | padding: 24px; 24 | .border-radius(@borderRadiusLarge); 25 | } 26 | .well-small { 27 | padding: 9px; 28 | .border-radius(@borderRadiusSmall); 29 | } 30 | -------------------------------------------------------------------------------- /WebContent/css/custom/overrides.less: -------------------------------------------------------------------------------- 1 | @import "../bootstrap/variables.less"; 2 | @import "../bootstrap/mixins.less"; 3 | 4 | .navbar-fixed-top .navbar-inner, 5 | .navbar-fixed-bottom .navbar-inner { 6 | padding: 0; 7 | } 8 | 9 | .btn:focus, select:focus { 10 | outline: none; 11 | } 12 | 13 | 14 | 15 | 16 | .btn { 17 | display: inline-block; 18 | .ie7-inline-block(); 19 | padding: 4px 12px; 20 | margin-bottom: 0; // For input.btn 21 | font-size: @baseFontSize; 22 | line-height: @baseLineHeight; 23 | text-align: center; 24 | vertical-align: middle; 25 | cursor: pointer; 26 | // .buttonBackground(@btnBackground, @btnBackgroundHighlight, @grayDark, 0 1px 1px rgba(255,255,255,.75)); 27 | background-image: none; 28 | .box-shadow(none); 29 | background-color: mix(@btnBackground, @btnBackgroundHighlight, 60%); 30 | // border: 1px solid @btnBorder; 31 | border: 1px solid mix(@btnBackground, @btnBackgroundHighlight, 60%); 32 | *border: 0; // Remove the border to prevent IE7's black border on input:focus 33 | // border-bottom-color: darken(@btnBorder, 10%); 34 | .border-radius(@baseBorderRadius); 35 | .ie7-restore-left-whitespace(); // Give IE7 some love 36 | // .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)"); 37 | 38 | // Hover/focus state 39 | &:hover, 40 | &:focus { 41 | color: @grayDark; 42 | text-decoration: none; 43 | // background-position: 0 -15px; 44 | 45 | // transition is only when going to hover/focus, otherwise the background 46 | // behind the gradient (there for IE<=9 fallback) gets mismatched 47 | // .transition(background-position .1s linear); 48 | background-color: @btnBackgroundHighlight; 49 | border: 1px solid @btnBackgroundHighlight; 50 | } 51 | 52 | // Focus state for keyboard and accessibility 53 | &:focus { 54 | 55 | } 56 | 57 | // Active state 58 | &.active, 59 | &:active { 60 | background-image: none; 61 | outline: 0; 62 | .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); 63 | } 64 | 65 | // Disabled state 66 | &.disabled, 67 | &[disabled] { 68 | cursor: default; 69 | background-image: none; 70 | .opacity(65); 71 | .box-shadow(none); 72 | } 73 | 74 | } 75 | 76 | .btn-success { 77 | color: white; 78 | // .buttonBackground(@btnSuccessBackground, @btnSuccessBackgroundHighlight); 79 | background-color: mix(@btnSuccessBackground, @btnSuccessBackgroundHighlight, 60%); 80 | // border: 1px solid @btnBorder; 81 | border: 1px solid mix(@btnSuccessBackground, @btnSuccessBackgroundHighlight, 60%); 82 | // Hover/focus state 83 | &:hover, 84 | &:focus { 85 | color: white; 86 | background-color: @btnSuccessBackgroundHighlight; 87 | border: 1px solid @btnSuccessBackgroundHighlight; 88 | } 89 | } 90 | .btn-danger { 91 | color: white; 92 | background-color: mix(@btnDangerBackground, @btnDangerBackgroundHighlight, 60%); 93 | border: 1px solid mix(@btnDangerBackground, @btnDangerBackgroundHighlight, 60%); 94 | // Hover/focus state 95 | &:hover, 96 | &:focus { 97 | color: white; 98 | background-color: @btnDangerBackgroundHighlight; 99 | border: 1px solid @btnDangerBackgroundHighlight; 100 | } 101 | } 102 | 103 | 104 | 105 | textarea, 106 | input[type="text"], 107 | input[type="password"], 108 | input[type="datetime"], 109 | input[type="datetime-local"], 110 | input[type="date"], 111 | input[type="month"], 112 | input[type="time"], 113 | input[type="week"], 114 | input[type="number"], 115 | input[type="email"], 116 | input[type="url"], 117 | input[type="search"], 118 | input[type="tel"], 119 | input[type="color"], 120 | .uneditable-input, 121 | .uneditable-textarea { 122 | .box-shadow(none); 123 | // This disables the "halo" effect on focus 124 | // &:focus { 125 | // .box-shadow(none); 126 | // } 127 | } 128 | -------------------------------------------------------------------------------- /WebContent/css/custom/sprites.less: -------------------------------------------------------------------------------- 1 | // extract from bootstrap/sprites.less - include only what you need 2 | [class^="icon-"], 3 | [class*=" icon-"] { 4 | display: inline-block; 5 | width: 14px; 6 | height: 14px; 7 | // .ie7-restore-right-whitespace(); 8 | line-height: 14px; 9 | vertical-align: text-top; 10 | background-image: url("@{iconSpritePath}"); 11 | background-position: 14px 14px; 12 | background-repeat: no-repeat; 13 | margin-top: 1px; 14 | } 15 | 16 | .icon-edit { background-position: -96px -72px; } 17 | .icon-remove { background-position: -312px 0; } 18 | .icon-return { background-position: -48px -168px; } 19 | /////////////////////////////////////////////////////////////////////////////// 20 | /////////////////////////////////////////////////////////////////////////////// 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | .iconsetup(@dim,@path) { 24 | display: inline-block; 25 | width: @dim; 26 | height: @dim; 27 | line-height: @dim; 28 | vertical-align: text-top; 29 | background-image: url("@{path}"); 30 | background-repeat: no-repeat; 31 | } 32 | .iconsm(@x, @y) { 33 | background-position: -@iconDimSm*@x -@iconDimSm*@y; 34 | } 35 | .iconlg(@x, @y) { 36 | background-position: -@iconDimLg*@x -@iconDimLg*@y; 37 | } 38 | 39 | 40 | [class^="iconsm-"], 41 | [class*=" iconsm-"] { 42 | .iconsetup(@iconDimSm,@iconSmSpritePath) 43 | } 44 | [class^="iconlg-"], 45 | [class*=" iconlg-"] { 46 | .iconsetup(@iconDimLg,@iconLgSpritePath) 47 | } 48 | 49 | .iconsm-back { .iconsm(@iconBackX,@iconBackY); } 50 | .iconlg-back { .iconlg(@iconBackX,@iconBackY); } 51 | .iconsm-x { .iconsm(@iconXX,@iconXY); } 52 | .iconlg-x { .iconlg(@iconXX,@iconXY); } 53 | .iconsm-trash { .iconsm(@iconTrashX,@iconTrashY); } 54 | .iconlg-trash { .iconlg(@iconTrashX,@iconTrashY); } 55 | .iconsm-pencil1 { .iconsm(@iconPencil1X,@iconPencil1Y); } 56 | .iconlg-pencil1 { .iconlg(@iconPencil1X,@iconPencil1Y); } 57 | .iconsm-pencil2 { .iconsm(@iconPencil2X,@iconPencil2Y); } 58 | .iconlg-pencil2 { .iconlg(@iconPencil2X,@iconPencil2Y); } 59 | .iconsm-pie { .iconsm(@iconPieX,@iconPieY); } 60 | .iconlg-pie { .iconlg(@iconPieX,@iconPieY); } 61 | -------------------------------------------------------------------------------- /WebContent/css/custom/style-BAK1.less: -------------------------------------------------------------------------------- 1 | @import "../bootstrap/variables.less"; 2 | @import "../bootstrap/mixins.less"; 3 | 4 | body { 5 | padding-top: @navbarHeight; 6 | } 7 | 8 | label { 9 | display: inline-block; 10 | padding-left: @inputBorderRadius + 1px; // 1px for the border 11 | } 12 | 13 | .control-action { 14 | float: right; 15 | padding: 0 1px; 16 | opacity: 0; 17 | transition: opacity 0.3s; 18 | -webkit-transition: opacity 0.3s; 19 | 20 | &:hover { 21 | background-color: @textColor; 22 | 23 | i { 24 | background-image: url("@{iconWhiteSpritePath}"); 25 | } 26 | } 27 | } 28 | 29 | .validation-msg { 30 | font-size: 0.8em; 31 | display: none; 32 | } 33 | 34 | .control-group { 35 | &:hover { 36 | .control-action { 37 | opacity: 1; 38 | } 39 | } 40 | 41 | &.error { 42 | input, textarea { 43 | padding-left: 25px; 44 | } 45 | 46 | .msg-ind { 47 | display: block; 48 | } 49 | } 50 | 51 | // &.required { 52 | // label:before { 53 | // content: "(*) "; 54 | // color: @infoText; 55 | // font-weight: bold; 56 | // } 57 | // } 58 | } 59 | 60 | .controls{ 61 | position: relative; 62 | } 63 | 64 | @msgIndDimension: 18px; 65 | .msg-ind { 66 | background-image: url("../images/x_x5F_alt18.png"); 67 | background-position: center center; 68 | // background-color: @inputBackground; 69 | background-repeat: no-repeat; 70 | width: @msgIndDimension; 71 | height: @msgIndDimension; 72 | position: absolute; 73 | left: 5px; 74 | top: (@inputHeight - @msgIndDimension) / 2; 75 | cursor: pointer; 76 | display: none; 77 | } 78 | 79 | 80 | 81 | @import "sprites.less"; 82 | -------------------------------------------------------------------------------- /WebContent/css/custom/style.less: -------------------------------------------------------------------------------- 1 | @import "../bootstrap/variables.less"; 2 | @import "variables.less"; 3 | @import "../bootstrap/mixins.less"; 4 | 5 | body { 6 | padding-top: @navbarHeight; 7 | } 8 | 9 | label { 10 | display: inline-block; 11 | padding-left: @inputBorderRadius + 1px; // 1px for the border 12 | } 13 | 14 | .control-action { 15 | float: right; 16 | padding: 0 1px; 17 | // opacity: 0; 18 | .transition(opacity 0.3s); 19 | 20 | &:hover { 21 | background-color: @textColor; 22 | 23 | i { 24 | background-image: url("@{iconSmWhiteSpritePath}"); 25 | } 26 | } 27 | } 28 | // 29 | //.validation-msg { 30 | // font-size: 0.8em; 31 | // display: none; 32 | //} 33 | //////////////////////////////////////////////////////////////////////// 34 | ///// DEPRECATED 35 | //.link-action { 36 | // padding: 1px 1px 0 1px; 37 | // &:hover { 38 | // background-color: @textColor; 39 | // 40 | // [class^="iconsm-"], 41 | // [class*=" iconsm-"] { 42 | // background-image: url("@{iconSmWhiteSpritePath}"); 43 | // } 44 | // [class^="iconlg-"], 45 | // [class*=" iconlg-"] { 46 | // background-image: url("@{iconLgWhiteSpritePath}"); 47 | // } 48 | // } 49 | //} 50 | //////////////////////////////////////////////////////////////////////// 51 | .link-action { 52 | //a { 53 | [class^="iconsm-"], 54 | [class*=" iconsm-"] { 55 | background-image: url("@{iconLgLinkColorSpritePath}"); 56 | } 57 | [class^="iconlg-"], 58 | [class*=" iconlg-"] { 59 | background-image: url("@{iconLgLinkColorSpritePath}"); 60 | } 61 | 62 | &:hover { 63 | [class^="iconsm-"], 64 | [class*=" iconsm-"] { 65 | background-image: url("@{iconLgLinkColorHoverSpritePath}"); 66 | } 67 | [class^="iconlg-"], 68 | [class*=" iconlg-"] { 69 | background-image: url("@{iconLgLinkColorHoverSpritePath}"); 70 | } 71 | } 72 | } 73 | 74 | .control-group { 75 | .control-action { 76 | .opacity(0); 77 | } 78 | 79 | &:hover { 80 | .control-action { 81 | .opacity(100); 82 | } 83 | } 84 | 85 | input, select { 86 | // see http://stackoverflow.com/questions/2226666/background-image-for-select-dropdown-does-not-work-in-chrome 87 | // -webkit-appearance: none; 88 | padding-left: 23px; 89 | background-image: url("@{iconSpritePath}"); 90 | background-position: -480px -1px; 91 | background-repeat: no-repeat; 92 | 93 | &:focus { 94 | background-position: -480px -31px; 95 | } 96 | } 97 | 98 | &.required { 99 | input, select { 100 | background-position: -480px -61px; 101 | 102 | &:focus { 103 | background-position: -480px -91px; 104 | } 105 | } 106 | } 107 | 108 | &.error { 109 | input, select { 110 | background-position: -480px -181px; 111 | 112 | &:focus { 113 | background-position: -480px -211px; 114 | } 115 | } 116 | } 117 | 118 | input.ng-valid, select.ng-valid { 119 | background-position: -480px -121px; 120 | 121 | &:focus { 122 | background-position: -480px -151px; 123 | } 124 | } 125 | } 126 | 127 | // chrome does not support backgrounds in selects, so reset the padding too 128 | @media screen and (-webkit-min-device-pixel-ratio:0) { 129 | .control-group select { 130 | background-image: none; 131 | padding-left: 6px; 132 | } 133 | } 134 | 135 | .controls{ 136 | position: relative; 137 | } 138 | 139 | @msgIndDimension: 18px; 140 | .msg-ind { 141 | width: @msgIndDimension; 142 | height: @msgIndDimension; 143 | position: absolute; 144 | left: 1px; 145 | top: (@inputHeight - @msgIndDimension) / 2; 146 | cursor: pointer; 147 | display: block; 148 | 149 | p { 150 | display: none; 151 | } 152 | } 153 | 154 | .qtip-content { 155 | p { 156 | margin: 2px 0; 157 | } 158 | } 159 | 160 | // a bootstrap grid cell that contains anchor commands 161 | .grid-command-cell { 162 | p { 163 | padding: 4px 12px; // make it similar to .btn 164 | text-align: center; 165 | } 166 | } 167 | 168 | .icobusysm { 169 | .iconsetup(@iconDimSm,"../images/activity-spinner-16-combined.gif"); 170 | background-position: 0 0; 171 | } 172 | 173 | 174 | /////////////////////////////////////////////////////////////////////////////// 175 | // categories view specific 176 | ul.category-list { 177 | list-style: none; 178 | margin: 0; 179 | 180 | li { 181 | float: left; 182 | width: 33.3%; 183 | .box-sizing(border-box); 184 | padding: 0 0 6px; 185 | 186 | div { 187 | border: 1px solid @inputBorder; 188 | .border-radius(@inputBorderRadius); 189 | padding: 3px; 190 | position: relative; // position container for the control-action 191 | 192 | &.dflt-category { 193 | background-color: @grayLighter; 194 | .control-action { 195 | display: none; 196 | } 197 | .icobusysm { 198 | background-position: -@iconDimSm 0; 199 | } 200 | } 201 | 202 | p { 203 | margin: 0; 204 | overflow: hidden; 205 | text-overflow: ellipsis; 206 | white-space: nowrap; 207 | padding-right: 20px; 208 | } 209 | 210 | .control-action, span { 211 | position: absolute; 212 | top: 50%; 213 | right: 3px; 214 | margin-top: -@baseLineHeight/2; 215 | } 216 | 217 | span { 218 | display: none; 219 | } 220 | 221 | &.pending { 222 | a.control-action { 223 | display: none; 224 | } 225 | span { 226 | display: inline; 227 | } 228 | } 229 | 230 | &.deleted { 231 | p { 232 | text-decoration: line-through; 233 | } 234 | } 235 | } 236 | 237 | &:nth-child(3n+1) { 238 | padding-right: 4px; 239 | } 240 | &:nth-child(3n+2) { 241 | padding-left: 2px; 242 | padding-right: 2px; 243 | } 244 | &:nth-child(3n+0) { 245 | padding-left: 4px; 246 | } 247 | } 248 | } 249 | 250 | 251 | /////////////////////////////////////////////////////////////////////////////// 252 | // effects 253 | /* 254 | .drop-in-setup { 255 | .transition(all linear 1.3s); 256 | line-height: 0; 257 | // .opacity(0); 258 | // height: 0; 259 | overflow: hidden; 260 | 261 | &.drop-in-start { 262 | line-height: @baseLineHeight; 263 | // .opacity(100); 264 | // height: @baseLineHeight; 265 | // background-color: red; 266 | } 267 | } 268 | 269 | .drop-out-setup { 270 | .transition(all linear 1.3s); 271 | line-height: @baseLineHeight; 272 | // .opacity(100); 273 | // height: @baseLineHeight; 274 | // overflow: hidden; 275 | 276 | &.drop-out-start { 277 | line-height: 0; 278 | // .opacity(0); 279 | // height: 0; 280 | // background-color: green; 281 | } 282 | } 283 | */ 284 | 285 | 286 | /////////////////////////////////////////////////////////////////////////////// 287 | // grid 288 | .grid-style { 289 | border: 1px solid rgb(212,212,212); 290 | width: 100%; 291 | height: 300px; 292 | } 293 | 294 | 295 | /////////////////////////////////////////////////////////////////////////////// 296 | @import "sprites.less"; 297 | -------------------------------------------------------------------------------- /WebContent/css/custom/variables.less: -------------------------------------------------------------------------------- 1 | @iconDimSm: 16px; 2 | @iconDimLg: 40px; 3 | 4 | @iconSmSpritePath: "../images/bitmap16.png"; 5 | @iconSmWhiteSpritePath: "../images/bitmap16-white.png"; 6 | @iconLgSpritePath: "../images/bitmap40.png"; 7 | @iconLgWhiteSpritePath: "../images/bitmap40-white.png"; 8 | @iconLgLinkColorSpritePath: "../images/bitmap40-linkColor.png"; 9 | @iconLgLinkColorHoverSpritePath: "../images/bitmap40-linkColorHover.png"; 10 | 11 | @iconBackX: 0; 12 | @iconBackY: 0; 13 | @iconXX: 1; 14 | @iconXY: 0; 15 | @iconTrashX: 2; 16 | @iconTrashY: 0; 17 | @iconPencil1X: 3; 18 | @iconPencil1Y: 0; 19 | @iconPencil2X: 4; 20 | @iconPencil2Y: 0; 21 | @iconPieX: 5; 22 | @iconPieY: 0; 23 | -------------------------------------------------------------------------------- /WebContent/css/ie8.css: -------------------------------------------------------------------------------- 1 | .input-block-level { 2 | min-height: 20px; 3 | } 4 | .msg-ind { 5 | background-color: white; 6 | filter: alpha(opacity=1); 7 | } 8 | -------------------------------------------------------------------------------- /WebContent/css/images/activity-spinner-16-combined.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikospara/angular-require-lazy/b071c66fc4ae6e5c147bc338a87ddf1553e93e8b/WebContent/css/images/activity-spinner-16-combined.gif -------------------------------------------------------------------------------- /WebContent/css/images/bitmap16-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikospara/angular-require-lazy/b071c66fc4ae6e5c147bc338a87ddf1553e93e8b/WebContent/css/images/bitmap16-white.png -------------------------------------------------------------------------------- /WebContent/css/images/bitmap16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikospara/angular-require-lazy/b071c66fc4ae6e5c147bc338a87ddf1553e93e8b/WebContent/css/images/bitmap16.png -------------------------------------------------------------------------------- /WebContent/css/images/bitmap40-linkColor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikospara/angular-require-lazy/b071c66fc4ae6e5c147bc338a87ddf1553e93e8b/WebContent/css/images/bitmap40-linkColor.png -------------------------------------------------------------------------------- /WebContent/css/images/bitmap40-linkColorHover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikospara/angular-require-lazy/b071c66fc4ae6e5c147bc338a87ddf1553e93e8b/WebContent/css/images/bitmap40-linkColorHover.png -------------------------------------------------------------------------------- /WebContent/css/images/bitmap40-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikospara/angular-require-lazy/b071c66fc4ae6e5c147bc338a87ddf1553e93e8b/WebContent/css/images/bitmap40-white.png -------------------------------------------------------------------------------- /WebContent/css/images/bitmap40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikospara/angular-require-lazy/b071c66fc4ae6e5c147bc338a87ddf1553e93e8b/WebContent/css/images/bitmap40.png -------------------------------------------------------------------------------- /WebContent/css/images/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikospara/angular-require-lazy/b071c66fc4ae6e5c147bc338a87ddf1553e93e8b/WebContent/css/images/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /WebContent/css/images/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikospara/angular-require-lazy/b071c66fc4ae6e5c147bc338a87ddf1553e93e8b/WebContent/css/images/glyphicons-halflings.png -------------------------------------------------------------------------------- /WebContent/css/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikospara/angular-require-lazy/b071c66fc4ae6e5c147bc338a87ddf1553e93e8b/WebContent/css/images/loading.gif -------------------------------------------------------------------------------- /WebContent/css/images/x_x5F_alt18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikospara/angular-require-lazy/b071c66fc4ae6e5c147bc338a87ddf1553e93e8b/WebContent/css/images/x_x5F_alt18.png -------------------------------------------------------------------------------- /WebContent/css/images/x_x5F_alt23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikospara/angular-require-lazy/b071c66fc4ae6e5c147bc338a87ddf1553e93e8b/WebContent/css/images/x_x5F_alt23.png -------------------------------------------------------------------------------- /WebContent/css/style.less: -------------------------------------------------------------------------------- 1 | @import "bootstrap/bootstrap.less"; 2 | @import "custom/style.less"; 3 | @import "bootstrap/responsive.less"; 4 | @import "custom/overrides.less"; 5 | -------------------------------------------------------------------------------- /WebContent/scripts/app/constants.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | HOME_PATH: "/index" 4 | }; 5 | }); 6 | -------------------------------------------------------------------------------- /WebContent/scripts/app/main/main.js: -------------------------------------------------------------------------------- 1 | define([ 2 | // injected dependencies 3 | "angular", "util/lib/angular-require-lazy/bootstrap", "./navbarCtrl", "lazy-registry", "util/lib/angular-require-lazy/routeConfig", "app/constants", 4 | // side-effect deps 5 | "templateCache!./navbar.html", 6 | // side-effect, non-AMD deps 7 | "lib/angular-route/angular-route", "lib/angular-ui-bootstrap/src/modal/modal" 8 | ], 9 | function(angular, bootstrap, navbarCtrl, lazyRegistry, routeConfig, constants) { 10 | 11 | var main = angular.module("main", ["ngRoute", "ui.bootstrap.collapse", "ui.bootstrap.modal"]); 12 | 13 | main.config(["$routeProvider", addAllRoutes]); 14 | 15 | main.controller("NavbarCtrl", navbarCtrl); 16 | 17 | bootstrap(document, main); 18 | 19 | return main; 20 | 21 | function addAllRoutes($routeProvider) { 22 | var module, i, modules = lazyRegistry.getModules(); 23 | for( i=0; i < modules.length; i++ ) { 24 | module = modules[i]; 25 | addAllRoutesForModule($routeProvider, module); 26 | } 27 | $routeProvider.otherwise({redirectTo: constants.HOME_PATH}); 28 | } 29 | 30 | function addAllRoutesForModule($routeProvider, module) { 31 | var routes, i, route; 32 | if( module.metadata && module.metadata["angular-routes"] ) { 33 | routes = module.metadata["angular-routes"]; 34 | for( i=0; i < routes.length; i++ ) { 35 | route = routes[i]; 36 | $routeProvider.when(route.path, routeConfig.fromAmdModule(route,module)); 37 | } 38 | } 39 | } 40 | }); 41 | -------------------------------------------------------------------------------- /WebContent/scripts/app/main/navbar.html: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /WebContent/scripts/app/main/navbarCtrl.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "jquery", "util/menuEntries", "app/constants", 3 | "lib/angular-ui-bootstrap/src/collapse/collapse" 4 | ], 5 | function($, menuEntries, constants) { 6 | "use strict"; 7 | 8 | NavbarCtrl.$inject = ["$scope", "$location", "$route"]; 9 | function NavbarCtrl($scope, $location, $route) { 10 | 11 | $.extend($scope, { 12 | isNavbarCollapsed: true, 13 | menuEntries: menuEntries, 14 | isActive: isActive, 15 | isHome: isHome, 16 | homePath: homePath 17 | }); 18 | 19 | function isActive(menuEntry) { 20 | return $route.current.locals && $route.current.locals.amdModule && $route.current.locals.amdModule.name === menuEntry.moduleName; 21 | } 22 | 23 | function isHome() { 24 | return $location.path() === constants.HOME_PATH; 25 | } 26 | 27 | function homePath() { 28 | return constants.HOME_PATH; 29 | } 30 | } 31 | 32 | return NavbarCtrl; 33 | }); 34 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/categories/categoriesCtrl.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "jquery", "util/viewUtils", "currentModule", "text!./deleteTemplate.html", 3 | "templateCache!./categoriesTemplate.html", "./categoryDirective", "app/shared/dao/categoriesDao", 4 | "lib/angular-ui-bootstrap/src/modal/modal", "util/returnService" 5 | ], 6 | function($, viewUtils, currentModule, templateDelete) { 7 | "use strict"; 8 | 9 | var ADD_LABEL = "Add", RENAME_LABEL = "Rename", opts; 10 | 11 | opts = { 12 | backdrop: "static", 13 | keyboard: false, 14 | template: templateDelete 15 | }; 16 | 17 | EditCategoriesCtrl.$inject = ["$scope", "$q", "$modal", "returnService", "categoriesDao"]; 18 | function EditCategoriesCtrl($scope, $q, $modal, returnService, categoriesDao) { 19 | 20 | var lastAddedCategoryKey; 21 | 22 | $.extend($scope, { 23 | categories: categoriesDao.fetchCachedForCurrentUser(), 24 | form: { 25 | name: null 26 | }, 27 | executeLabel: initExecuteLabel(), 28 | execute: execute, 29 | cancel: cancel, 30 | selectedCategoryForEdit: initSelectedCategoryForEdit(), 31 | selectCategoryForEdit: selectCategoryForEdit, 32 | hasCancel: initHasCancel(), 33 | deleteCategory: deleteCategory, 34 | isPushed: initIsPushed(), 35 | doReturn: doReturn 36 | }); 37 | 38 | function execute(event) { 39 | if( $scope.selectedCategoryForEdit != null ) updateCategory(); 40 | else addCategory(); 41 | } 42 | 43 | function updateCategory() { 44 | var oldName = $scope.selectedCategoryForEdit.name; 45 | $scope.selectedCategoryForEdit.name = $scope.form.name; 46 | categoriesDao.updateCategory($scope.selectedCategoryForEdit).then( 47 | function success() { 48 | cancel(); 49 | }, 50 | function failure() { 51 | $scope.selectedCategoryForEdit.name = oldName; 52 | } 53 | ); 54 | } 55 | 56 | function addCategory() { 57 | var newcat = categoriesDao.addCategory($scope.form.name); 58 | newcat.$$promise.then(function() { 59 | lastAddedCategoryKey = newcat.key; 60 | }); 61 | $scope.form.name = null; 62 | $scope.editCategoriesForm.$setPristine(); 63 | } 64 | 65 | function cancel(event) { 66 | viewUtils.preprocessAnchorEvt(event); 67 | $scope.selectedCategoryForEdit = null; 68 | $scope.form.name = null; 69 | $scope.hasCancel = false; 70 | $scope.executeLabel = ADD_LABEL; 71 | } 72 | 73 | function selectCategoryForEdit(c) { 74 | if( c.key == null ) return; 75 | $scope.selectedCategoryForEdit = c; 76 | $scope.form.name = c.name; 77 | $scope.hasCancel = true; 78 | $scope.executeLabel = RENAME_LABEL; 79 | } 80 | 81 | function deleteCategory(c) { 82 | if( c.key == null ) return; 83 | opts.controller = ["$scope", "$modalInstance", DeleteCtrl]; 84 | var d = $modal.open(opts); 85 | d.result.then(function(result) { 86 | if( result === "yes" ) { 87 | categoriesDao.deleteCategory(c); 88 | } 89 | }); 90 | 91 | function DeleteCtrl($scope, $modalInstance) { 92 | $scope.category = c; 93 | $scope.close = function(result) { 94 | $modalInstance.close(result); 95 | }; 96 | } 97 | } 98 | 99 | function initHasCancel() { 100 | return false; 101 | } 102 | 103 | function initSelectedCategoryForEdit() { 104 | return null; 105 | } 106 | 107 | function initExecuteLabel() { 108 | return ADD_LABEL; 109 | } 110 | 111 | function initIsPushed() { 112 | return returnService.isPushed(); 113 | } 114 | 115 | function doReturn() { 116 | returnService.doReturn({categoryKey: lastAddedCategoryKey}); 117 | } 118 | } 119 | 120 | return EditCategoriesCtrl; 121 | }); 122 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/categories/categoriesTemplate.html: -------------------------------------------------------------------------------- 1 |
    2 |

    3 | 4 | Categories 5 |

    6 |
    7 |
    8 |
    9 |
      10 |
    • 11 |
      12 |
    • 13 |
    14 |
    15 |
    16 |
    17 |

    18 | Change name for category: {{ selectedCategoryForEdit.name }} 19 |

    20 |
    21 |
    22 | 23 |
    24 | 25 |
    26 |
    27 |
    28 |
    29 |
    30 |
    31 | 35 |
    36 |
    37 |

    38 | Cancel 39 |

    40 |
    41 |
    42 |
    43 |
    44 |
    45 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/categories/categoryDirective.js: -------------------------------------------------------------------------------- 1 | define(["angular", "util/viewUtils", "currentModule", "text!./categoryTemplate.html", "app/shared/dao/userDao"], 2 | function(angular, viewUtils, currentModule, template) { 3 | "use strict"; 4 | 5 | currentModule.directive("category", ["userDao", function(userDao) { 6 | return { 7 | restrict: "A", 8 | template: template, 9 | replace: true, 10 | scope: { 11 | category: "=", 12 | edit: "&", 13 | remove: "&" 14 | }, 15 | link: function(scope, element, attrs) { 16 | angular.extend(scope, { 17 | defaultCategoryId: null, 18 | editCb: editCb, 19 | removeCb: removeCb 20 | }); 21 | 22 | function editCb(event,c) { 23 | viewUtils.preprocessAnchorEvt(event); 24 | scope.edit({c:c}); 25 | } 26 | 27 | function removeCb(event,c) { 28 | viewUtils.preprocessAnchorEvt(event); 29 | scope.remove({c:c}); 30 | } 31 | 32 | (function initDefaultCategoryId() { 33 | return userDao.getUserData().then(function(userData) { 34 | scope.defaultCategoryId = userData.defaultCategoryId; 35 | }); 36 | })(); 37 | } 38 | }; 39 | }]); 40 | }); 41 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/categories/categoryTemplate.html: -------------------------------------------------------------------------------- 1 |
    2 |

    {{ category.name }}

    3 | 4 | 5 |
    6 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/categories/deleteTemplate.html: -------------------------------------------------------------------------------- 1 | 4 | 7 | 11 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/categories/main.js: -------------------------------------------------------------------------------- 1 | define(["angular", "./categoriesCtrl", "currentModule"], function(angular, categoriesCtrl, currentModule) { 2 | "use strict"; 3 | 4 | var m = angular.module("categories", currentModule.combineDependencies([])); 5 | 6 | m.controller("EditCategoriesCtrl", categoriesCtrl); 7 | 8 | return m; 9 | }); 10 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/categories/main.metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "angular-routes": [ 3 | { 4 | "path": "/categories", 5 | "controller": "EditCategoriesCtrl", 6 | "template": "app/modules/categories/categoriesTemplate.html", 7 | "display": "Categories", 8 | "weight": 100 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/expenses/chartPopup.tpl.html: -------------------------------------------------------------------------------- 1 | 4 | 7 | 10 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/expenses/chartPopupModule.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "angular", "currentModule", "templateCache!./chartPopup.tpl.html", 3 | // side-effect deps 4 | "./pieChartDirective", "./chartPopupService", 5 | "lib/angular-ui-bootstrap/src/modal/modal" 6 | ], 7 | function(angular, currentModule, chartPopupTemplate) { 8 | "use strict"; 9 | 10 | var m = angular.module("chartPopup", currentModule.combineDependencies([])); 11 | 12 | return m; 13 | }); 14 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/expenses/chartPopupService.js: -------------------------------------------------------------------------------- 1 | define(["angular", "currentModule", "templateCache!./chartPopup.tpl.html", "./pieChartDirective"], 2 | function(angular, currentModule, chartPopupTemplate) { 3 | "use strict"; 4 | 5 | var opts = { 6 | backdrop: "static", 7 | keyboard: false, 8 | templateUrl: chartPopupTemplate.path 9 | }; 10 | 11 | function mapExpensesForCharting(expenses) { 12 | var map = {}, result = [], i, tmp; 13 | for( i=0; i < expenses.length; i++ ) { 14 | tmp = map[expenses[i].categoryId]; 15 | if( !tmp ) { 16 | tmp = { value:0, label:"Category " + expenses[i].categoryId }; 17 | map[expenses[i].categoryId] = tmp; 18 | result.push(tmp); 19 | } 20 | tmp.value += expenses[i].amount; 21 | } 22 | return result; 23 | } 24 | 25 | currentModule.addDependencies().service("chartPopup", ["$modal", function($modal) { 26 | 27 | function show(expenses) { 28 | var d; 29 | 30 | opts.controller = ["$scope", ChartCtrl]; 31 | d = $modal.open(opts); 32 | 33 | function ChartCtrl($scope) { 34 | angular.extend($scope, { 35 | data: mapExpensesForCharting(expenses) 36 | }); 37 | } 38 | } 39 | 40 | return { 41 | show: show 42 | }; 43 | }]); 44 | }); 45 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/expenses/expensesCtrl.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "angular", "app/shared/model/Expense", "ngLazy!./chartPopupModule", 3 | // side-effect deps 4 | "app/shared/dao/userDao", "app/shared/dao/expensesDao", "templateCache!./expensesTemplate.html", "lib/ng-grid-bower/ng-grid", "lib/angular-ui-bootstrap/src/modal/modal" 5 | ], 6 | function(angular, Expense, chartPopupModule) { 7 | "use strict"; 8 | 9 | ExpensesCtrl.$inject = ["$scope", "expensesDao", "userDao", "$injector"]; 10 | function ExpensesCtrl($scope, expensesDao, userDao, $injector) { 11 | angular.extend($scope, { 12 | defaultCategoryId: initDefaultCategoryId(), 13 | expenses: initExpenses(), 14 | gridOptions: initGridOptions(), 15 | chart: chart, 16 | format: function(x) { 17 | // TODO 18 | return x; 19 | } 20 | }); 21 | 22 | function initDefaultCategoryId() { 23 | return userDao.getUserData().then(function(userData) { 24 | return userData.defaultCategoryId; 25 | }); 26 | } 27 | 28 | function initExpenses() { 29 | // TODO Determine month and year, route params 30 | var ret = []; 31 | expensesDao.fetch(2013,undefined,ret); 32 | return ret; 33 | } 34 | 35 | function initGridOptions() { 36 | return { 37 | data: "expenses", 38 | multiSelect: false, 39 | keepLastSelected: false, 40 | //selectedItems: $scope.mySelections, 41 | //showFooter:true, 42 | columnDefs: [ 43 | { 44 | field: "date", 45 | displayName: "Date", 46 | width: "****", 47 | // cellFilter: "date: 'dd/MM/yyyy'" 48 | cellTemplate: '
    {{ row.getProperty(col.field) | date: "dd/MM/yyyy" }}
    ', 49 | },{ 50 | field: "reason", 51 | displayName: "Reason", 52 | width: "*********" 53 | },{ 54 | field: "amount", 55 | displayName: "Amount", 56 | width: "****", 57 | cellClass: "text-right", 58 | cellTemplate: '
    {{ format(row.getProperty(col.field)) }}
    ', 59 | headerClass: "text-left" 60 | },{ 61 | field: "categoryId", 62 | displayName: "Category", 63 | width: "*******" 64 | },{ 65 | field: "special", 66 | displayName: "", 67 | width: "*", 68 | sortable: false 69 | } 70 | ] 71 | }; 72 | } 73 | 74 | function chart($event, expenses) { 75 | $event.preventDefault(); 76 | chartPopupModule.get().then( 77 | function() { 78 | $injector.invoke(["chartPopup", function(chartPopup) { 79 | chartPopup.show(expenses); 80 | }]); 81 | }, 82 | function() { 83 | 84 | } 85 | ); 86 | } 87 | } 88 | 89 | return ExpensesCtrl; 90 | }); 91 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/expenses/expensesTemplate.html: -------------------------------------------------------------------------------- 1 |
    2 |

    3 | Expenses 4 | 5 |

    6 |
    7 |
    8 |
    9 |
    10 |
    11 |
    12 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/expenses/main.js: -------------------------------------------------------------------------------- 1 | define(["angular", "./expensesCtrl", "currentModule"], function(angular, expensesCtrl, currentModule) { 2 | "use strict"; 3 | 4 | var m = angular.module("expenses", currentModule.combineDependencies([])); 5 | 6 | m.controller("ExpensesCtrl", expensesCtrl); 7 | 8 | return m; 9 | }); 10 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/expenses/main.metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "angular-routes": [ 3 | { 4 | "path": "/expenses", 5 | "controller": "ExpensesCtrl", 6 | "template": "app/modules/expenses/expensesTemplate.html", 7 | "display": "Expenses", 8 | "weight": 200 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/expenses/pieChartDirective.js: -------------------------------------------------------------------------------- 1 | define(["angular", "currentModule", "lib/Chart"], function(angular, currentModule, Chart) { 2 | "use strict"; 3 | 4 | var COLORS = [ 5 | ["#F7464A","#FF5A5E"], 6 | ["#46BFBD","#5AD3D1"], 7 | ["#FDB45C","#FFC870"] 8 | ]; 9 | 10 | function preprocessData(data) { 11 | var i, colorIndex = 0; 12 | for( i=0; i < data.length; i++ ) { 13 | if( data[i].color == null ) { 14 | data[i].color = COLORS[colorIndex][0]; 15 | data[i].highlight = COLORS[colorIndex][1]; 16 | colorIndex += 1; 17 | } 18 | } 19 | } 20 | 21 | currentModule.directive("pieChart", function() { 22 | return { 23 | restrict: "A", 24 | scope: { 25 | data: "=pieChart" 26 | }, 27 | link: function(scope, elem, attrs) { 28 | var ctx, pieChart; 29 | preprocessData(scope.data); 30 | ctx = elem[0].getContext("2d"); 31 | pieChart = new Chart(ctx).Pie(scope.data, { 32 | animationEasing: "easeOutQuint", 33 | legendTemplate: 34 | '' 39 | }); 40 | } 41 | }; 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/index/indexCtrl.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "angular", "app/shared/model/Expense", "app/shared/dao/userDao", 3 | "app/shared/dao/categoriesDao", "app/shared/dao/expensesDao", "templateCache!./indexTemplate.html", "util/returnService" 4 | ], 5 | function(angular, Expense) { 6 | "use strict"; 7 | 8 | IndexCtrl.$inject = ["$scope", "returnService", "expensesDao", "categoriesDao", "userDao"]; 9 | function IndexCtrl($scope, returnService, expensesDao, categoriesDao, userDao) { 10 | 11 | angular.extend($scope, { 12 | categories: categoriesDao.fetchCachedForCurrentUser(), 13 | form: initExpense(), 14 | enter: enter, 15 | editCategories: editCategories 16 | }); 17 | 18 | function initExpense() { 19 | var retData, e = returnService.get("expense"); 20 | if( e == null ) { 21 | e = new Expense(); 22 | userDao.getUserData().then(function(userData) { 23 | e.categoryId = userData.defaultCategoryId; 24 | }); 25 | } 26 | else { 27 | retData = returnService.getReturnedData(); 28 | if( retData && typeof(retData.categoryKey) === "number" ) e.categoryId = retData.categoryKey; 29 | } 30 | return e; 31 | } 32 | 33 | function enter(event) { 34 | expensesDao.add($scope.form); 35 | // TODO Clear 36 | } 37 | 38 | function editCategories(event) { 39 | returnService.put("expense", $scope.form); 40 | returnService.push(); 41 | } 42 | } 43 | 44 | return IndexCtrl; 45 | }); 46 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/index/indexTemplate.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Enter Expense

    4 |
    5 |
    6 |
    7 | 8 |
    9 | 10 |
    11 |
    12 |
    13 |
    14 | 15 |
    16 | 17 |
    18 |
    19 |
    20 |
    21 | 22 | 23 |
    24 | 30 |
    31 |
    32 |
    33 |
    34 |
    35 |
    36 | 40 |
    41 |
    42 |
    43 | 44 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/index/main.js: -------------------------------------------------------------------------------- 1 | define(["angular", "./indexCtrl", "currentModule"], function(angular, indexCtrl, currentModule) { 2 | "use strict"; 3 | 4 | var m = angular.module("index", currentModule.combineDependencies([])); 5 | 6 | m.controller("IndexCtrl", indexCtrl); 7 | 8 | return m; 9 | }); 10 | -------------------------------------------------------------------------------- /WebContent/scripts/app/modules/index/main.metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "angular-routes": [ 3 | { 4 | "path": "/index", 5 | "controller": "IndexCtrl", 6 | "template": "app/modules/index/indexTemplate.html", 7 | "display": "Home", 8 | "weight": 0 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /WebContent/scripts/app/shared/dao/categoriesDao.js: -------------------------------------------------------------------------------- 1 | define(["angular", "currentModule", "./userDao", "lib/angular-resource/angular-resource"], 2 | function(angular, currentModule) { 3 | "use strict"; 4 | 5 | var RC_URL = "api/user/:id/categories"; 6 | 7 | currentModule.addDependencies("ngResource").service("categoriesDao", ["$resource", "$q", "userDao", function($resource, $q, userDao) { 8 | var cachedCategories = null, cachedCategoriesMap = null, rc; 9 | 10 | rc = $resource(RC_URL, {}, { 11 | deleteCategory: { url: RC_URL+"/:key", method:"DELETE" }, 12 | updateCategory: { method:"PUT" } 13 | }); 14 | 15 | function fetchForCurrentUser(result) { 16 | if( result != null ) { 17 | result.$$status = "PENDING"; 18 | } 19 | return userDao.getUserData().then( 20 | function gotUserData(userData) { 21 | return rc.get({id:userData.id}).$promise; 22 | }, 23 | function errorGettingUserData(response) { 24 | return $q.reject(response); 25 | } 26 | ).then( 27 | function success(data) { 28 | if( data && data.payload && angular.isArray(result) ) { 29 | angular.copy(data.payload,result); 30 | } 31 | if( result != null ) { 32 | result.$$status = "SUCCESS"; 33 | } 34 | return data; 35 | }, 36 | function failure(response) { 37 | if( result != null ) { 38 | result.$$status = "FAILURE"; 39 | } 40 | return $q.reject(response); 41 | } 42 | ); 43 | } 44 | 45 | function fetchCachedForCurrentUser() { 46 | if( cachedCategories == null ) { 47 | cachedCategories = []; 48 | fetchForCurrentUser(cachedCategories).then(makeCachedCategoriesMap); 49 | } 50 | return cachedCategories; 51 | } 52 | 53 | function updateCategory(c) { 54 | c.$$status = "PENDING"; 55 | return userDao.getUserData().then( 56 | function gotUserData(userData) { 57 | return rc.updateCategory({id:userData.id},{name:c.name, key:c.key}).$promise; 58 | }, 59 | function errorGettingUserData(response) { 60 | return $q.reject(response); 61 | } 62 | ).then( 63 | function success(data) { 64 | if( data && data.payload === true ) { 65 | c.$$status = "SUCCESS"; 66 | return data; 67 | } 68 | else { 69 | // business failure 70 | c.$$status = "FAILURE"; 71 | return $q.reject(data); 72 | } 73 | }, 74 | function failure(response) { 75 | c.$$status = "FAILURE"; 76 | return $q.reject(response); 77 | } 78 | ); 79 | } 80 | 81 | function deleteCategory(c) { 82 | c.$$status = "PENDING"; 83 | c.$$deleted = true; 84 | return userDao.getUserData().then( 85 | function gotUserData(userData) { 86 | return rc.deleteCategory({id:userData.id,key:c.key},null).$promise; 87 | }, 88 | function errorGettingUserData(response) { 89 | return $q.reject(response); 90 | } 91 | ).then( 92 | function success(data) { 93 | if( data && data.payload === true ) { 94 | c.$$status = "SUCCESS"; 95 | removeFromCache(c); 96 | return data; 97 | } 98 | else { 99 | // business failure 100 | return deletionFailed(c,data); 101 | } 102 | }, 103 | function failure(response) { 104 | return deletionFailed(c,response); 105 | } 106 | ); 107 | } 108 | 109 | function removeFromCache(c) { 110 | var index = cachedCategories.indexOf(c); 111 | if( index >= 0 ) cachedCategories.splice(index,1); 112 | if( cachedCategoriesMap !== null ) delete cachedCategoriesMap[c.key]; 113 | } 114 | 115 | function deletionFailed(c, reason) { 116 | c.$$status = "FAILURE"; 117 | delete c.$$deleted; 118 | return $q.reject(reason); 119 | } 120 | 121 | function addCategory(name) { 122 | var c = {key:null, name:name, $$status:"PENDING", $$promise:null}; 123 | cachedCategories.push(c); 124 | c.$$promise = userDao.getUserData().then( 125 | function gotUserData(userData) { 126 | return rc.save({id:userData.id},c).$promise; 127 | }, 128 | function errorGettingUserData(response) { 129 | return $q.reject(response); 130 | } 131 | ).then( 132 | function success(data) { 133 | delete c.$$promise; 134 | if( data && data.payload && data.payload.key ) { 135 | c.$$status = "SUCCESS"; 136 | c.key = data.payload.key; 137 | mapCategory(c); 138 | return data; 139 | } 140 | else { 141 | // business failure 142 | return additionFailed(c,data); 143 | } 144 | }, 145 | function failure(response) { 146 | delete c.$$promise; 147 | return additionFailed(c,response); 148 | } 149 | ); 150 | return c; 151 | } 152 | 153 | function additionFailed(c,reason) { 154 | var index = cachedCategories.indexOf(c); 155 | if( index >= 0 ) cachedCategories.splice(index,1); 156 | c.$$status = "FAILURE"; 157 | return $q.reject(reason); 158 | } 159 | 160 | function clearCache() { 161 | cachedCategories = null; 162 | cachedCategoriesMap = null; 163 | } 164 | 165 | function makeCachedCategoriesMap() { 166 | var i, c; 167 | cachedCategoriesMap = {}; 168 | for( i=0; i < cachedCategories.length; i++ ) { 169 | c = cachedCategories[i]; 170 | cachedCategoriesMap[c.key] = c; 171 | } 172 | } 173 | 174 | function mapCategory(c) { 175 | if( cachedCategoriesMap === null ) cachedCategoriesMap = {}; 176 | cachedCategoriesMap[c.key] = c; 177 | } 178 | 179 | function promiseForKey(key) { 180 | throw new Error("UNIMPLEMENTED"); 181 | } 182 | 183 | return { 184 | fetchForCurrentUser: fetchForCurrentUser, 185 | fetchCachedForCurrentUser: fetchCachedForCurrentUser, 186 | addCategory: addCategory, 187 | deleteCategory: deleteCategory, 188 | updateCategory: updateCategory, 189 | promiseForKey: promiseForKey, 190 | clearCache: clearCache 191 | }; 192 | }]); 193 | }); 194 | -------------------------------------------------------------------------------- /WebContent/scripts/app/shared/dao/expensesDao.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "app/shared/model/Expense", "angular", "currentModule", 3 | "lib/angular-resource/angular-resource", "./userDao", "lib/angular-resource/angular-resource" 4 | ], 5 | function(Expense, angular, currentModule) { 6 | "use strict"; 7 | 8 | currentModule.addDependencies("ngResource").service("expensesDao", ["$resource", "$q", "userDao", function($resource, $q, userDao) { 9 | var rc; 10 | 11 | rc = $resource("api/user/:id/expenses", {}, {}); 12 | 13 | function fetch(year,month,result) { 14 | if( result != null ) { 15 | result.$$status = "PENDING"; 16 | } 17 | return userDao.getUserData().then( 18 | function gotUserData(userData) { 19 | return rc.get({id:userData.id, year:year, month:month}).$promise; 20 | }, 21 | function errorGettingUserData(response) { 22 | return $q.reject(response); 23 | } 24 | ).then( 25 | function success(data) { 26 | if( data && data.payload && angular.isArray(result) ) { 27 | angular.copy(data.payload,result); 28 | } 29 | if( result != null ) { 30 | result.$$status = "SUCCESS"; 31 | } 32 | return data; 33 | }, 34 | function failure(response) { 35 | if( result != null ) { 36 | result.$$status = "FAILURE"; 37 | } 38 | return $q.reject(response); 39 | } 40 | ); 41 | } 42 | 43 | function add(expense) { 44 | return userDao.getUserData().then( 45 | function gotUserData(userData) { 46 | return rc.save({id:userData.id}, expense).$promise; 47 | }, 48 | function errorGettingUserData(response) { 49 | return $q.reject(response); 50 | } 51 | ); 52 | } 53 | 54 | return { 55 | fetch: fetch, 56 | add: add 57 | }; 58 | }]); 59 | }); 60 | -------------------------------------------------------------------------------- /WebContent/scripts/app/shared/dao/userDao.js: -------------------------------------------------------------------------------- 1 | define(["angular", "currentModule", "util/loginPrompt", "lib/angular-resource/angular-resource"], 2 | function(angular, currentModule) { 3 | "use strict"; 4 | 5 | currentModule.addDependencies("ngResource").service("userDao", ["$http", "$q", "loginPrompt", function($http, $q, loginPrompt) { 6 | var userData = null, loggedIn = false, loginPending = false; 7 | 8 | function login(username, password) { 9 | userData = $q.defer(); // overwrite previous value; this is a new login 10 | return $http.post("api/user/login", {username:username,password:password}).then( 11 | function loginSucceeded(response) { 12 | loggedIn = true; 13 | userData.resolve(response.data); 14 | return response.data; 15 | }, 16 | function loginFailed(response) { 17 | loggedIn = false; 18 | if( userData !== null ) userData.reject(); 19 | return $q.reject(response); 20 | } 21 | ); 22 | } 23 | 24 | function isLoggedIn() { 25 | return loggedIn; 26 | } 27 | 28 | function getUserData(triggerLogin) { 29 | var d = userData || $q.defer(); 30 | if( userData == null ) { 31 | userData = d; 32 | if( !loginPending && triggerLogin !== false ) { 33 | loginPending = true; 34 | loginPrompt().then( 35 | function(loginData) { 36 | return login(loginData.username,loginData.password); 37 | }, 38 | function(err) { 39 | return $q.reject(err); 40 | } 41 | ).then( 42 | function(result) { 43 | d.resolve(result); 44 | loginPending = false; 45 | }, 46 | function(err) { 47 | d.reject(err); 48 | loginPending = false; 49 | return $q.reject(err); 50 | } 51 | ); 52 | } 53 | } 54 | return d.promise; 55 | } 56 | 57 | return { 58 | getUserData: getUserData, 59 | login: login, 60 | isLoggedIn: isLoggedIn 61 | }; 62 | }]); 63 | }); 64 | -------------------------------------------------------------------------------- /WebContent/scripts/app/shared/model/Expense.js: -------------------------------------------------------------------------------- 1 | define(["jquery"],function($) { 2 | "use strict"; 3 | 4 | function Expense(json) { 5 | json = json || {}; 6 | if( typeof(json.date) === "string" ) json.date = new Date(json.date); 7 | $.extend(this, json); 8 | } 9 | 10 | Expense.prototype = { 11 | key: null, 12 | date: null, 13 | amount: null, 14 | reason: null, 15 | categoryId: null, 16 | special: null 17 | }; 18 | Expense.prototype.constructor = Expense; 19 | 20 | Expense.amountValidators = [ 21 | ["positive", function(thisObj, value, validationContext) { 22 | validationContext.setMessage("Must be positive"); 23 | return value == null || value > 0; 24 | }] 25 | ]; 26 | 27 | return Expense; 28 | }); 29 | -------------------------------------------------------------------------------- /WebContent/scripts/empty.js: -------------------------------------------------------------------------------- 1 | // empty file to trick the optimizer -------------------------------------------------------------------------------- /WebContent/scripts/globals.js: -------------------------------------------------------------------------------- 1 | // given by server-side template 2 | -------------------------------------------------------------------------------- /WebContent/scripts/require-cfg.js: -------------------------------------------------------------------------------- 1 | var require = { 2 | baseUrl: "scripts", 3 | 4 | paths: { 5 | "angular": "lib/angular/angular" 6 | }, 7 | 8 | config: { 9 | 10 | }, 11 | 12 | map: { 13 | "*": { 14 | "text": "lib/requirejs-text/text", 15 | // this must match the libLazy property in build-scripts/options-grunt.js 16 | "lazy": "lib/require-lazy/lazy", 17 | "lazy-builder": "lib/require-lazy/lazy-builder", 18 | "promise-adaptor": "util/lib/angular-require-lazy/promiseAdaptorAngular", 19 | "currentModule": "util/lib/angular-require-lazy/currentModule", 20 | "templateCache": "util/lib/angular-require-lazy/templateCache", 21 | "ngLazy": "util/lib/angular-require-lazy/ngLazy", 22 | "ngLazyBuilder": "util/lib/angular-require-lazy/ngLazyBuilder" 23 | } 24 | }, 25 | 26 | shim: { 27 | "globals": { 28 | exports: "globals" 29 | }, 30 | "angular": { 31 | exports: "angular" 32 | }, 33 | "lib/angular-ui-bootstrap/src/transition/transition": { 34 | deps: ["angular"] 35 | }, 36 | "lib/angular-ui-bootstrap/src/collapse/collapse": { 37 | deps: ["angular", "lib/angular-ui-bootstrap/src/transition/transition"] 38 | }, 39 | "lib/angular-ui-bootstrap/src/modal/modal": { 40 | deps: ["angular","util/modalTemplates", "lib/angular-ui-bootstrap/src/transition/transition"] 41 | }, 42 | "lib/angular-route/angular-route": { 43 | deps: ["angular"] 44 | }, 45 | "lib/angular-resource/angular-resource": { 46 | deps: ["angular"] 47 | } 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /WebContent/scripts/util/lib/angular-require-lazy/bootstrap.js: -------------------------------------------------------------------------------- 1 | define(["angular", "./lazyAngularUtils", "./currentModule"], function(angular, lazyAngularUtils, currentModule) { 2 | "use strict"; 3 | 4 | function bootstrap(element, mainModule) { 5 | var injector; 6 | mainModule.config(lazyAngularUtils.cacheInternals); 7 | mainModule.run(lazyAngularUtils.captureQ); 8 | currentModule.resolveWith(mainModule); 9 | lazyAngularUtils.makeLazyAngular(); 10 | injector = angular.bootstrap(element, [mainModule.name]); 11 | bootstrap.injector = injector; 12 | return injector; 13 | } 14 | 15 | return bootstrap; 16 | }); 17 | -------------------------------------------------------------------------------- /WebContent/scripts/util/lib/angular-require-lazy/currentModule.js: -------------------------------------------------------------------------------- 1 | define(["angular", "./util"], function(angular, util) { 2 | "use strict"; 3 | 4 | var runBlocks = [], moduleDependenciesSet = [], currentModuleProxy = {}, makeArray = util.makeArray; 5 | 6 | currentModuleProxy.addDependencies = function() { 7 | for( var i=0; i < arguments.length; i++ ) { 8 | if( typeof(arguments[i]) === "string" ) { 9 | addToSet(moduleDependenciesSet, arguments[i]); 10 | } 11 | else if( angular.isArray(arguments[i]) ) { 12 | addAllToSet(moduleDependenciesSet, arguments[i]); 13 | } 14 | } 15 | return currentModuleProxy; 16 | }; 17 | 18 | function addToSet(set, d) { 19 | if( d && set.indexOf(d) < 0 ) { 20 | set.push(d); 21 | } 22 | } 23 | 24 | function addAllToSet(set, deps) { 25 | for( var i=0; i < deps.length; i++ ) { 26 | if( typeof(deps[i]) === "string" ) { 27 | addToSet(set, deps[i]); 28 | } 29 | } 30 | } 31 | 32 | currentModuleProxy.combineDependencies = function(deps) { 33 | var i, ret = angular.copy(deps); 34 | addAllToSet(ret, moduleDependenciesSet); 35 | return ret; 36 | }; 37 | 38 | // Angular API 39 | currentModuleProxy.factory = function() { 40 | runBlocks.push(["factory", makeArray(arguments)]); 41 | return currentModuleProxy; 42 | }; 43 | 44 | currentModuleProxy.directive = function() { 45 | runBlocks.push(["directive", makeArray(arguments)]); 46 | return currentModuleProxy; 47 | }; 48 | 49 | currentModuleProxy.filter = function() { 50 | runBlocks.push(["filter", makeArray(arguments)]); 51 | return currentModuleProxy; 52 | }; 53 | 54 | currentModuleProxy.controller = function() { 55 | runBlocks.push(["controller", makeArray(arguments)]); 56 | return currentModuleProxy; 57 | }; 58 | 59 | currentModuleProxy.provider = function() { 60 | runBlocks.push(["provider", makeArray(arguments)]); 61 | return currentModuleProxy; 62 | }; 63 | 64 | currentModuleProxy.service = function() { 65 | runBlocks.push(["service", makeArray(arguments)]); 66 | return currentModuleProxy; 67 | }; 68 | 69 | currentModuleProxy.run = function(r) { 70 | runBlocks.push(["run",[r]]); 71 | return currentModuleProxy; 72 | }; 73 | 74 | currentModuleProxy.value = function() { 75 | runBlocks.push(["value", makeArray(arguments)]); 76 | return currentModuleProxy; 77 | }; 78 | 79 | currentModuleProxy.constant = function() { 80 | runBlocks.push(["constant", makeArray(arguments)]); 81 | return currentModuleProxy; 82 | }; 83 | 84 | currentModuleProxy.animation = function() { 85 | runBlocks.push(["animation", makeArray(arguments)]); 86 | return currentModuleProxy; 87 | }; 88 | 89 | // TODO Only config() is missing from the angular.module interface; decide if we can handle it and how 90 | 91 | currentModuleProxy.resolveWith = function(realModule) { 92 | var i, b; 93 | for( i=0; i < runBlocks.length; i++ ) { 94 | b = runBlocks[i]; 95 | realModule[b[0]].apply(realModule,b[1]); 96 | } 97 | runBlocks = []; 98 | moduleDependenciesSet = []; 99 | }; 100 | 101 | return currentModuleProxy; 102 | }); 103 | -------------------------------------------------------------------------------- /WebContent/scripts/util/lib/angular-require-lazy/lazyAngularUtils.js: -------------------------------------------------------------------------------- 1 | define(["angular", "./promiseAdaptorAngular"], function(angular, promiseAdaptor) { 2 | "use strict"; 3 | 4 | var lazyAngularUtils, eagerAngularModuleFn = angular.module, cachedInternals = {}, lazyModules = {}; 5 | 6 | function makeLazyModule(name, cachedInternals) { 7 | var lazyModule = { 8 | name: name, 9 | realModule: null, 10 | __runBlocks: [], 11 | factory: function() { 12 | cachedInternals.$provide.factory.apply(null, arguments); 13 | return lazyModule; 14 | }, 15 | directive: function() { 16 | cachedInternals.$compileProvider.directive.apply(null, arguments); 17 | return lazyModule; 18 | }, 19 | filter: function() { 20 | cachedInternals.$filterProvider.register.apply(null, arguments); 21 | return lazyModule; 22 | }, 23 | controller: function() { 24 | cachedInternals.$controllerProvider.register.apply(null, arguments); 25 | return lazyModule; 26 | }, 27 | provider: function() { 28 | cachedInternals.$provide.provider.apply(null, arguments); 29 | return lazyModule; 30 | }, 31 | service: function() { 32 | cachedInternals.$provide.service.apply(null, arguments); 33 | return lazyModule; 34 | }, 35 | run: function(r) { 36 | this.__runBlocks.push(r); 37 | return lazyModule; 38 | }, 39 | value: function() { 40 | cachedInternals.$provide.value.apply(null, arguments); 41 | return lazyModule; 42 | }, 43 | constant: function() { 44 | cachedInternals.$provide.constant.apply(null, arguments); 45 | return lazyModule; 46 | }, 47 | animation: function() { 48 | cachedInternals.$animateProvider.register.apply(null, arguments); 49 | return lazyModule; 50 | } 51 | // TODO Only config() is missing from the angular.module interface; decide if we can handle it and how 52 | }; 53 | return lazyModule; 54 | } 55 | 56 | function makeLazyAngular() { 57 | angular.module = function(name, requires, configFn) { 58 | var ret, realModule; 59 | if( typeof(requires) === "undefined" ) { 60 | if( lazyModules.hasOwnProperty(name) ) ret = lazyModules[name]; 61 | else ret = eagerAngularModuleFn.call(angular, name); 62 | } 63 | else { 64 | //if( configFn != null ) throw new Error("config function unimplemented yet, module: " + name); 65 | ret = makeLazyModule(name, cachedInternals); 66 | lazyModules[name] = ret; 67 | ret.realModule = eagerAngularModuleFn.call(angular, name, requires, configFn); 68 | lazyAngularUtils.modulesQueue.push(ret); 69 | } 70 | return ret; 71 | }; 72 | } 73 | 74 | function initLazyModules(injector) { 75 | var i, modulesQueue = lazyAngularUtils.modulesQueue; 76 | if( modulesQueue != null && modulesQueue.length > 0 ) { 77 | // TODO Run lazy config functions, not implemented yet 78 | // for( i=0; i < modulesQueue.length; i++ ) { 79 | // callConfigBlocks(injector, modulesQueue[i]); 80 | // } 81 | for( i=0; i < modulesQueue.length; i++ ) { 82 | callRunBlocks(injector, modulesQueue[i]); 83 | } 84 | modulesQueue.splice(0); 85 | } 86 | } 87 | 88 | function callRunBlocks(injector, module) { 89 | var i, blocks; 90 | blocks = module.__runBlocks || []; 91 | for( i=0; i < blocks.length; i++ ) { 92 | injector.invoke(blocks[i]); 93 | } 94 | } 95 | 96 | cacheInternals.$inject = ["$provide", "$compileProvider", "$filterProvider", "$controllerProvider", "$animateProvider"]; 97 | function cacheInternals($provide, $compileProvider, $filterProvider, $controllerProvider, $animateProvider) { 98 | cachedInternals.$provide = $provide; 99 | cachedInternals.$compileProvider = $compileProvider; 100 | cachedInternals.$filterProvider = $filterProvider; 101 | cachedInternals.$controllerProvider = $controllerProvider; 102 | cachedInternals.$animateProvider = $animateProvider; 103 | } 104 | 105 | lazyAngularUtils = { 106 | cacheInternals: cacheInternals, 107 | captureQ: ["$q", promiseAdaptor.setQ], 108 | makeLazyAngular: makeLazyAngular, 109 | initLazyModules: initLazyModules, 110 | modulesQueue: [] 111 | }; 112 | 113 | return lazyAngularUtils; 114 | }); 115 | -------------------------------------------------------------------------------- /WebContent/scripts/util/lib/angular-require-lazy/ngLazy.js: -------------------------------------------------------------------------------- 1 | define(["module", "./promiseAdaptorAngular", "./currentModule", "./lazyAngularUtils", "./bootstrap"], 2 | function(module, promiseAdaptor, currentModule, lazyAngularUtils, bootstrap) { 3 | "use strict"; 4 | 5 | /** RequireJS module loader entry point. */ 6 | function load(name, parentRequire, onload, config) { 7 | parentRequire(["lazy!" + name], function(stubModule) { 8 | if( !config || !config.isBuild ) { 9 | doDevTimeLoad(stubModule, onload); 10 | } 11 | else { 12 | onload(stubModule); 13 | } 14 | }); 15 | } 16 | 17 | function doDevTimeLoad(stubModule, onload) { 18 | var realGet = stubModule.get, cachedRealModule, gotRealModule = false; 19 | 20 | stubModule.get = function() { 21 | var d = promiseAdaptor.makeDeferred(); 22 | if( gotRealModule ) { 23 | d.resolve(cachedRealModule); 24 | } 25 | else { 26 | realGet.apply(stubModule).then( 27 | function(realModule) { 28 | currentModule.resolveWith(realModule); 29 | lazyAngularUtils.initLazyModules(bootstrap.injector); 30 | d.resolve(realModule); 31 | cachedRealModule = realModule; 32 | gotRealModule = true; 33 | realGet = null; 34 | }, 35 | function error(err) { 36 | d.reject(err); 37 | } 38 | ); 39 | } 40 | return promiseAdaptor.makePromise(d); 41 | }; 42 | 43 | onload(stubModule); 44 | } 45 | 46 | return { 47 | load: load 48 | }; 49 | }); 50 | -------------------------------------------------------------------------------- /WebContent/scripts/util/lib/angular-require-lazy/promiseAdaptorAngular.js: -------------------------------------------------------------------------------- 1 | define(["angular"], function() { 2 | "use strict"; 3 | 4 | var $q = null; 5 | 6 | return { 7 | makeDeferred: function() { 8 | return $q.defer(); 9 | }, 10 | makePromise: function(deferred) { 11 | return deferred.promise; 12 | }, 13 | all: function(promises) { 14 | return $q.all(promises); 15 | }, 16 | setQ: function(value) { 17 | $q = value; 18 | } 19 | }; 20 | }); 21 | -------------------------------------------------------------------------------- /WebContent/scripts/util/lib/angular-require-lazy/routeConfig.js: -------------------------------------------------------------------------------- 1 | /** This is based on angularjs-requirejs-lazy-controllers from github. */ 2 | define(["./currentModule", "./lazyAngularUtils"], function(currentModule, lazyAngularUtils) { 3 | "use strict"; 4 | 5 | function fromAmdModule(route,module) { 6 | var defer, templateDefer, loaded = false; 7 | 8 | return { 9 | controller: route.controller, 10 | resolve: { 11 | module: ["$q", "$route", "$templateCache", "$injector", function($q, $route, $templateCache, $injector) { 12 | if( typeof($route.current.template) !== "function" ) { 13 | if( templateDefer == null ) templateDefer = $q.defer(); 14 | $route.current.template = function() { 15 | return templateDefer.promise; 16 | }; 17 | }; 18 | 19 | if( defer == null ) { 20 | defer = $q.defer(); 21 | 22 | $q.when(module.get()).then( 23 | function(m) { 24 | if( m == null ) { 25 | defer.reject("module " + module.name + " does not export anything"); 26 | return; 27 | } 28 | 29 | currentModule.resolveWith(m); 30 | // init any angular modules that were lazy loaded 31 | lazyAngularUtils.initLazyModules($injector); 32 | loaded = true; 33 | templateDefer.resolve($templateCache.get(route.template)); 34 | defer.resolve(m); 35 | }, function(err) { 36 | // TODO 37 | defer.reject(err); 38 | } 39 | ); 40 | } 41 | 42 | return defer.promise; 43 | }], 44 | amdModule: function() { 45 | return module; 46 | } 47 | } 48 | }; 49 | } 50 | 51 | return { 52 | fromAmdModule: fromAmdModule 53 | }; 54 | }); 55 | -------------------------------------------------------------------------------- /WebContent/scripts/util/lib/angular-require-lazy/templateCache.js: -------------------------------------------------------------------------------- 1 | define(["module", "./currentModule"], function(module, currentModule) { 2 | "use strict"; 3 | 4 | /** RequireJS module loader entry point. */ 5 | function load(name, parentRequire, onload, config) { 6 | parentRequire(["text!" + name], function(t) { 7 | if( !config || !config.isBuild ) { 8 | currentModule.run(["$templateCache", function($templateCache) { 9 | $templateCache.put(name, t); 10 | }]); 11 | } 12 | onload({ 13 | text: t, 14 | path: name 15 | }); 16 | }); 17 | } 18 | 19 | return { 20 | load: load 21 | }; 22 | }); 23 | -------------------------------------------------------------------------------- /WebContent/scripts/util/lib/angular-require-lazy/templateCacheBuilder.js: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////// 2 | // THIS IS OUTDATED AND UNUSED !!! // 3 | ////////////////////////////////////// 4 | define(["module"], function(module) { 5 | "use strict"; 6 | 7 | return { 8 | load: function(name, parentRequire, onload, config) { 9 | parentRequire(["text!" + name], function(t) { 10 | onload(t); 11 | }); 12 | }, 13 | write: function(pluginName, moduleName, write) { 14 | var text = "define('" + pluginName + "!" + moduleName + "',['currentModule','text!" + moduleName + "'],function(currentModule, t) {\n"; 15 | text += "\tcurrentModule.run(['$templateCache', function($templateCache) {\n"; 16 | text += "\t\t$templateCache.put('" + moduleName + "', t);\n"; 17 | text += "\t}]);\n"; 18 | text += "});\n"; 19 | write(text); 20 | } 21 | }; 22 | }); 23 | -------------------------------------------------------------------------------- /WebContent/scripts/util/lib/angular-require-lazy/util.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | "use strict"; 3 | 4 | function makeArray(arr) { 5 | var ret = arr, i, x; 6 | if( arr != null && !angular.isArray(arr) ) { 7 | ret =[]; 8 | if( angular.isNumber(arr.length) ) { 9 | for( i=0; i < arr.length; i++ ) { 10 | x = arr[i]; 11 | ret.push(x); 12 | } 13 | } 14 | } 15 | return ret; 16 | } 17 | 18 | return { 19 | makeArray: makeArray 20 | }; 21 | }); 22 | -------------------------------------------------------------------------------- /WebContent/scripts/util/loginPrompt.html: -------------------------------------------------------------------------------- 1 | 4 | 7 | 10 | -------------------------------------------------------------------------------- /WebContent/scripts/util/loginPrompt.js: -------------------------------------------------------------------------------- 1 | define(["currentModule", "text!./loginPrompt.html", "lib/angular-ui-bootstrap/src/modal/modal"], 2 | function(currentModule, template) { 3 | "use strict"; 4 | 5 | currentModule.service("loginPrompt", ["$modal", function($modal) { 6 | var 7 | isOpen = false, 8 | 9 | opts = { 10 | backdrop: "static", 11 | keyboard: false, 12 | template: template 13 | }; 14 | 15 | function promptLogin() { 16 | isOpen = true; 17 | var d = $modal.open(opts); 18 | d.result["finally"](function() { 19 | isOpen = false; 20 | }); 21 | return d.result; 22 | } 23 | 24 | promptLogin.isOpen = function() { 25 | return isOpen; 26 | }; 27 | 28 | return promptLogin; 29 | }]); 30 | }); 31 | -------------------------------------------------------------------------------- /WebContent/scripts/util/menuEntries.js: -------------------------------------------------------------------------------- 1 | define(["lazy-registry"], function(lazyRegistry) { 2 | "use strict"; 3 | 4 | function makeMenuEntries() { 5 | var i, ret = [], module, modules = lazyRegistry.getModules(); 6 | for( i=0; i < modules.length; i++ ) { 7 | module = modules[i]; 8 | addMenuEntry(module,ret); 9 | } 10 | ret.sort(function(a,b) { return a.weight-b.weight; }); 11 | return ret; 12 | } 13 | 14 | function addMenuEntry(module, entries) { 15 | if( module.metadata && module.metadata["angular-routes"] ) { 16 | var i, meta = module.metadata["angular-routes"]; 17 | for( i=0; i < meta.length; i++ ) { 18 | entries.push({ 19 | display: meta[i].display, 20 | path: meta[i].path, 21 | moduleName: module.name, 22 | weight: meta[i].weight 23 | }); 24 | } 25 | } 26 | } 27 | 28 | return makeMenuEntries(); 29 | }); 30 | -------------------------------------------------------------------------------- /WebContent/scripts/util/modalTemplates.js: -------------------------------------------------------------------------------- 1 | define(["text!lib/angular-ui-bootstrap/template/modal/backdrop.html","text!lib/angular-ui-bootstrap/template/modal/window.html","currentModule"], 2 | function(backdropTemplate,windowTemplate,currentModule) { 3 | "use strict"; 4 | currentModule.run(["$templateCache", function($templateCache) { 5 | $templateCache.put("template/modal/backdrop.html", backdropTemplate); 6 | $templateCache.put("template/modal/window.html", windowTemplate); 7 | }]); 8 | }); 9 | -------------------------------------------------------------------------------- /WebContent/scripts/util/resourceUtils.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | "use strict"; 3 | 4 | function transformResponse(data, headersGetter) { 5 | try { 6 | if( data != null && data != "" ) data = JSON.parse(data); 7 | else data = null; 8 | } 9 | catch(e) { 10 | return { $error: e, $rc: "FAILURE" }; 11 | } 12 | if( data != null && typeof(data.payload) !== "undefined" ) { 13 | var ret = data.payload || []; 14 | if( $.isArray(data.messages) ) ret.$messages = data.messages; 15 | if( typeof(data.hasMore) === "boolean" ) ret.$hasMore = data.hasMore; 16 | if( data.exception == null ) { 17 | ret.$rc = "SUCCESS"; 18 | } 19 | else { 20 | ret.$rc = "FAILURE"; 21 | ret.$exception = data.exception; 22 | } 23 | return ret; 24 | } 25 | else return data; 26 | } 27 | 28 | return { 29 | transformResponse: transformResponse 30 | }; 31 | }); 32 | -------------------------------------------------------------------------------- /WebContent/scripts/util/returnService.js: -------------------------------------------------------------------------------- 1 | define(["currentModule"], function(currentModule) { 2 | "use strict"; 3 | 4 | currentModule.service("returnService", ["$rootScope", "$location", function($rootScope, $location) { 5 | var stack = [], pushing = false, pushed = false, returning = false, returned = false, startController = null, currentState = null, 6 | currentPushedData = null, currentReturnedData = null; 7 | 8 | $rootScope.$on("$routeChangeSuccess", function(event, currentRoute, previousRoute) { 9 | if( pushing ) { 10 | // TODO 11 | } 12 | else if( returning ) { 13 | // TODO 14 | } 15 | else { 16 | clear(); 17 | } 18 | startController = null; 19 | pushing = false; 20 | returning = false; 21 | }); 22 | 23 | $rootScope.$on("$routeChangeStart", function(event, nextRoute, currentRoute) { 24 | startController = currentRoute.controller; 25 | }); 26 | 27 | $rootScope.$on("$routeChangeError", function(event, currentRoute, previousRoute, rejection) { 28 | if( pushing ) { 29 | // TODO 30 | } 31 | else if( returning ) { 32 | // TODO 33 | } 34 | else { 35 | clear(); 36 | } 37 | startController = null; 38 | pushing = false; 39 | returning = false; 40 | }); 41 | 42 | function push(data, targetView) { 43 | pushing = true; 44 | stack.push({ 45 | location: $location.hash(), 46 | currentState: currentState, 47 | currentPushedData: currentPushedData, 48 | pushed: pushed, 49 | returned: returned 50 | }); 51 | currentState = null; 52 | currentPushedData = data; 53 | // set those here because the view gets rendered before the routeChangeSuccess event 54 | pushed = true; 55 | returned = false; 56 | if( typeof(targetView) === "string" ) $location.hash(targetView); 57 | } 58 | 59 | function doReturn(data) { 60 | var frame = stack.pop(); 61 | returning = true; 62 | currentState = frame.currentState; 63 | currentPushedData = frame.currentPushedData; 64 | currentReturnedData = data; 65 | pushed = frame.pushed; 66 | returned = true; 67 | $location.hash(frame.location); 68 | } 69 | 70 | function getPushedData() { 71 | if( pushed ) return currentPushedData; 72 | else return null; 73 | } 74 | 75 | function getReturnedData() { 76 | if( returned ) return currentReturnedData; 77 | else return null; 78 | } 79 | 80 | function clear() { 81 | stack = []; 82 | currentState = null; 83 | currentPushedData = null; 84 | currentReturnedData = null; 85 | pushed = false; 86 | returned = false; 87 | } 88 | 89 | function isPushed() { 90 | return pushed; 91 | } 92 | 93 | function isReturned() { 94 | return returned; 95 | } 96 | 97 | function put(key, value) { 98 | if( currentState === null ) currentState = {}; 99 | currentState[key] = value; 100 | } 101 | 102 | function get(key) { 103 | if( currentState === null ) return; 104 | return currentState[key]; 105 | } 106 | 107 | return { 108 | push: push, 109 | doReturn: doReturn, 110 | getPushedData: getPushedData, 111 | getReturnedData: getReturnedData, 112 | clear: clear, 113 | isPushed: isPushed, 114 | isReturned: isReturned, 115 | put: put, 116 | get: get 117 | }; 118 | }]); 119 | }); 120 | -------------------------------------------------------------------------------- /WebContent/scripts/util/urlUtils.js: -------------------------------------------------------------------------------- 1 | define(["jquery", "globals"], function($, globals) { 2 | "use strict"; 3 | 4 | var pathParamRe = /\{([a-zA-Z0-9]+)\}/; 5 | 6 | function Route(uri) { 7 | if( !(this instanceof Route) ) return new Route(uri); 8 | this._uri = $.trim(uri); 9 | } 10 | 11 | Route.prototype = { 12 | _uri: null, 13 | _uriComponents: null, 14 | _uriParams: null, 15 | 16 | applyParams: function(params) { 17 | var x, ret, comp, val; 18 | if( this._uriParams === null ) this._extractUriComponents(); 19 | params = $.extend({}, params); 20 | ret = { 21 | uri: null, 22 | queryParams: params 23 | }; 24 | comp = this._uriComponents.slice(0); 25 | for( x in this._uriParams ) { 26 | if( !this._uriParams.hasOwnProperty(x) ) continue; 27 | val = params[x]; 28 | if( val == null ) throw new Error("required path param " + x + " missing from params - Route(" + this._uri + ")"); 29 | if( $.isFunction(val) ) val = val(); 30 | comp[this._uriParams[x]] = val; 31 | delete params[x]; 32 | } 33 | ret.uri = "/" + comp.join("/"); 34 | return ret; 35 | }, 36 | 37 | _extractUriComponents: function() { 38 | var split, tmp, i, params={}, components=[]; 39 | split = this._uri.split("/"); 40 | for( i=0; i < split.length; i++ ) { 41 | if( !split[i] ) continue; 42 | if( pathParamRe.test(split[i]) ) { 43 | tmp = pathParamRe.exec(split[i])[1]; 44 | params[tmp] = i; 45 | components.push(tmp); 46 | } 47 | else components.push(split[i]); 48 | } 49 | this._uriComponents = components; 50 | this._uriParams = params; 51 | }, 52 | 53 | append: function(path) { 54 | var newUri = appendPath(this._uri, path); 55 | return new Route(newUri); 56 | } 57 | }; 58 | 59 | function appendPath(pathNow, path) { 60 | if( path != null ) { 61 | path = ""+path; 62 | var nowEndsWithSlash = (pathNow.length > 0 && pathNow.charAt(pathNow.length-1) === "/"), 63 | pathStartsWithSlash = (path.length > 0 && path.charAt(0) === "/"); 64 | if( nowEndsWithSlash && pathStartsWithSlash ) { 65 | pathNow += path.substring(1); 66 | } 67 | else if( (nowEndsWithSlash && !pathStartsWithSlash) || (!nowEndsWithSlash && pathStartsWithSlash) ) { 68 | pathNow += path; 69 | } 70 | else { 71 | pathNow += "/" + path; 72 | } 73 | } 74 | return pathNow; 75 | } 76 | 77 | function makeContextPath(path) { 78 | return appendPath(globals.contextPath.substr(1), path); 79 | } 80 | 81 | return { 82 | contextPath: globals.contextPath, 83 | Route: Route, 84 | appendPath: appendPath, 85 | makeContextPath: makeContextPath 86 | }; 87 | }); 88 | -------------------------------------------------------------------------------- /WebContent/scripts/util/viewUtils.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | "use strict"; 3 | 4 | function preprocessAnchorEvt(event) { 5 | if( event ) { 6 | if( typeof(event.preventDefault) === "function" ) event.preventDefault(); 7 | if( event.currentTarget ) $(event.currentTarget).blur(); 8 | } 9 | } 10 | 11 | return { 12 | preprocessAnchorEvt: preprocessAnchorEvt 13 | }; 14 | }); 15 | -------------------------------------------------------------------------------- /WebTests/scripts/app/modules/categories/categoryDirective.spec.js: -------------------------------------------------------------------------------- 1 | define(["app/modules/categories/categoryDirective", "angular"], function(categoryDirective, angular) { 2 | describe("The categoryDirective", function() { 3 | var scope, wrapperElement, element, $compile; 4 | 5 | function compile(html) { 6 | wrapperElement = angular.element('
    ' + html + '
    '); 7 | element = wrapperElement.find("div"); 8 | $compile(wrapperElement)(scope); 9 | scope.$digest(); 10 | } 11 | 12 | beforeEach(module("test-main")); 13 | 14 | beforeEach(inject(function($rootScope, _$compile_) { 15 | scope = $rootScope.$new(); 16 | $compile = _$compile_; 17 | })); 18 | 19 | afterEach(function() { 20 | scope.$destroy(); 21 | wrapperElement = element = null; 22 | }); 23 | 24 | it("should pass", function() { 25 | scope.c = { 26 | key: "abc", 27 | name: "Abc" 28 | }; 29 | scope.selectCategoryForEdit = jasmine.createSpy("selectCategoryForEdit"); 30 | scope.deleteCategory = jasmine.createSpy("deleteCategory"); 31 | compile('
    Span'); 32 | // var editLink = element.find("a:not(.control-action)"); 33 | var editLink = wrapperElement[0].querySelectorAll("a:not(.control-action)"); 34 | //console.log(wrapperElement.html()); 35 | expect(editLink.length).toBe(1); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /WebTests/scripts/app/shared/dao/expensesDao.spec.js: -------------------------------------------------------------------------------- 1 | define(["app/shared/dao/expensesDao"], function(expensesDao) { 2 | describe("The expensesDao", function() { 3 | beforeEach(module("test-main")); 4 | 5 | it("should pass", function() { 6 | expect(1).toBe(1); //dummy 7 | }); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /WebTests/scripts/mocks/app/shared/dao/categoriesDao.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return {}; 3 | }); 4 | -------------------------------------------------------------------------------- /WebTests/scripts/mocks/lazy-registry.js: -------------------------------------------------------------------------------- 1 | define(['lazy','require','promise-adaptor'], function(lazy,require,promiseAdaptor) { 2 | var moduleList = []; 3 | function registerModule(m) { 4 | moduleList.push(m); 5 | lazy.registerModule(m); 6 | } 7 | registerModule(new lazy.Stub('app/modules/categories/main',require,null,null,{"angular-routes":[{"path":"/categories","controller":"EditCategoriesCtrl","template":"app/modules/categories/categoriesTemplate.html","display":"Categories","weight":100}]})); 8 | registerModule(new lazy.Stub('app/modules/expenses/main',require,null,null,{"angular-routes":[{"path":"/expenses","controller":"ExpensesCtrl","template":"app/modules/expenses/expensesTemplate.html","display":"Expenses","weight":200}]})); 9 | registerModule(new lazy.Stub('app/modules/index/main',require,null,null,{"angular-routes":[{"path":"/index","controller":"IndexCtrl","template":"app/modules/index/indexTemplate.html","display":"Home","weight":0}]})); 10 | return { 11 | getModules: function() { return moduleList; }, 12 | getModule: lazy.getModule, 13 | get: function(moduleName) { 14 | var m = lazy.getModule(moduleName); 15 | if( m != null ) return m.get(); 16 | else { 17 | var d = promiseAdaptor.makeDeferred(); 18 | d.reject('does not exist'); 19 | return promiseAdaptor.makePromise(d); 20 | } 21 | } 22 | }; 23 | }); 24 | -------------------------------------------------------------------------------- /WebTests/scripts/mocks/require-cfg.js: -------------------------------------------------------------------------------- 1 | var require_config_main = require; 2 | delete require; 3 | -------------------------------------------------------------------------------- /WebTests/scripts/mocks/test-main.js: -------------------------------------------------------------------------------- 1 | define([ 2 | // injected dependencies 3 | "angular", "app/main/navbarCtrl", 4 | // side-effect deps 5 | "templateCache!app/main/navbar.html", 6 | // side-effect, non-AMD deps 7 | "lib/angular-route/angular-route", "lib/angular-ui-bootstrap/src/modal/modal" 8 | ], 9 | function(angular, navbarCtrl, navbarTemplate) { 10 | "use strict"; 11 | 12 | var main = angular.module("test-main", ["ngMock", "ngRoute", "ui.bootstrap.collapse", "ui.bootstrap.modal"]); 13 | 14 | main.controller("NavbarCtrl", navbarCtrl); 15 | 16 | return main; 17 | }); 18 | -------------------------------------------------------------------------------- /WebTests/scripts/test-main.js: -------------------------------------------------------------------------------- 1 | var allTestFiles = []; 2 | var TEST_REGEXP = /(spec|test)\.js$/i; 3 | 4 | var globals = { 5 | contextPath: "/base" 6 | }, baseUrl; 7 | 8 | 9 | var pathToModule = function(path) { 10 | // return path.replace(/^\/base\/WebContent\/scripts\//, '').replace(/\.js$/, ''); 11 | return path.replace(/^\/base\/WebContent\/scripts\//, ''); 12 | }; 13 | 14 | Object.keys(window.__karma__.files).forEach(function(file) { 15 | if( TEST_REGEXP.test(file) && file.indexOf('/base/WebContent/scripts/lib/') !== 0 ) { 16 | // Normalize paths to RequireJS module names. 17 | allTestFiles.push(pathToModule(file)); 18 | } 19 | }); 20 | 21 | 22 | require_config_main.paths["angular-mocks"] = "lib/angular-mocks/angular-mocks"; 23 | require_config_main.shim["angular-mocks"] = { 24 | deps: ["angular"] 25 | }; 26 | 27 | 28 | require.config({ 29 | // Karma serves files under /base, which is the basePath from your config file 30 | baseUrl: '/base/WebContent/scripts', 31 | 32 | paths: require_config_main.paths, 33 | 34 | shim: require_config_main.shim, 35 | 36 | map: { 37 | "*": { 38 | "lazy-registry": "mocks/lazy-registry" 39 | }, 40 | "app/shared/dao/expensesDao": { 41 | "util/resource": "mocks/util/resource" 42 | } 43 | }, 44 | 45 | callback: function() { 46 | // we need to duplicate the Angular bootstrapping functionality from bootstrap.js 47 | require(["angular", "util/lib/angular-require-lazy/bootstrap", "angular-mocks"], 48 | function(angular, bootstrap) { 49 | // dynamically load all test files 50 | require(allTestFiles, function() { 51 | var main = angular.module("test-main", ["ngMock", "ui.bootstrap.modal"]); 52 | bootstrap(document, main); 53 | window.__karma__.start(); 54 | }); 55 | }); 56 | } 57 | }); 58 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var 2 | fs = require("fs"), 3 | express = require("express"), 4 | app = express(), 5 | options = require("./build-scripts/options-grunt.js"), 6 | shared = require("require-lazy").shared; 7 | 8 | app.use(express.logger('dev')); 9 | app.use(express.static(__dirname + '/build')); 10 | app.use(express.static(__dirname + '/WebContent')); 11 | 12 | // lazy registry 13 | app.get("/scripts/lazy-registry.js", getLazyRegistry); 14 | 15 | // API //////////////////////////////////////////////////////////////////////// 16 | app.post("/api/user/login", express.bodyParser(), function(req, res) { 17 | //console.log(req.body); // works as expected 18 | res.json({id:1, defaultCategoryId:1, preferences:null}); 19 | }); 20 | 21 | app.get("/api/user/:id/categories", function(req, res) { 22 | console.log(req.route.params.id); // works as expected 23 | res.json({ 24 | "payload": [ 25 | {"key": 1, "name": "Other"}, 26 | {"key": 2, "name": "Rent"}, 27 | {"key": 3, "name": "Food"}, 28 | {"key": 4, "name": "Entertainment"} 29 | ] 30 | }); 31 | }); 32 | 33 | app.post("/api/user/:id/categories", singleStringBodyParser, function(req, res) { 34 | //console.log(require("util").inspect(req)); 35 | // 36 | // $.ajax("/api/user/1/categories",{dataType:"json",contentType:"application/json",type:"POST",data:"CatName"}).then(function(x){console.log(x);}) 37 | // 38 | /////////////////////////////////////////////////////////////////////////// 39 | // res.json({ 40 | // "payload": {"key": parseInt(Math.random()*1000+1000), "name": req.body} 41 | // }); 42 | /////////////////////////////////////////////////////////////////////////// 43 | setTimeout(function() { // simulate delay 44 | res.json({ 45 | "payload": {"key": parseInt(Math.random()*1000+1000), "name": req.body} 46 | }); 47 | }, 3000); 48 | }); 49 | 50 | app.put("/api/user/:id/categories", express.bodyParser(), function(req, res) { 51 | var c = req.body; 52 | console.log("Changing name of category with key ", c.key, " to ", c.name); 53 | setTimeout(function() { // simulate delay 54 | res.json({ 55 | "payload": Math.random() < 0.7 // simulate success with 70% chance 56 | }); 57 | }, 1000); 58 | }); 59 | 60 | app["delete"]("/api/user/:id/categories/:key", express.bodyParser(), function(req, res) { 61 | setTimeout(function() { // simulate delay 62 | res.json({ 63 | "payload": true 64 | }); 65 | }, 3000); 66 | }); 67 | 68 | app.post("/api/user/:userid/expenses", express.bodyParser(), function(req, res) { 69 | console.log(req.body); 70 | res.json({id:1}); 71 | }); 72 | 73 | app.get("/api/user/:id/expenses", function(req, res) { 74 | console.log(req.route.params.id + " - " + req.query.year + "/" + req.query.month); // works as expected 75 | res.json({ 76 | "payload": [ 77 | {key:1, date:"2013-08-07T21:00:01.000Z", amount: 10, reason: "One", categoryId: 1, special: null}, 78 | {key:2, date:"2013-08-08T21:00:02.000Z", amount: 12, reason: "Two", categoryId: 2, special: null}, 79 | {key:3, date:"2013-08-08T21:00:03.000Z", amount: 14, reason: "Three", categoryId: 3, special: null}, 80 | {key:4, date:"2013-08-08T21:00:04.000Z", amount: 15, reason: "Four", categoryId: 1, special: null}, 81 | {key:5, date:"2013-08-08T21:00:05.000Z", amount: 18, reason: "Five", categoryId: 1, special: null}, 82 | {key:11, date:"2013-08-08T21:00:11.000Z", amount: 10, reason: "One", categoryId: 2, special: null}, 83 | {key:12, date:"2013-08-08T21:00:12.000Z", amount: 12, reason: "Two", categoryId: 2, special: null}, 84 | {key:13, date:"2013-08-08T21:00:13.000Z", amount: 14, reason: "Three", categoryId: 1, special: null}, 85 | {key:14, date:"2013-08-08T21:00:14.000Z", amount: 15, reason: "Four", categoryId: 1, special: null}, 86 | {key:15, date:"2013-08-08T21:00:15.000Z", amount: 18, reason: "Five", categoryId: 1, special: null}, 87 | {key:21, date:"2013-08-08T21:00:21.000Z", amount: 10, reason: "One", categoryId: 3, special: null}, 88 | {key:22, date:"2013-08-08T21:00:22.000Z", amount: 12, reason: "Two", categoryId: 1, special: null}, 89 | {key:23, date:"2013-08-08T21:00:23.000Z", amount: 14, reason: "Three", categoryId: 1, special: null}, 90 | {key:24, date:"2013-08-08T21:00:24.000Z", amount: 15, reason: "Four", categoryId: 1, special: null}, 91 | {key:25, date:"2013-08-09T21:00:25.000Z", amount: 18, reason: "Five", categoryId: 2, special: null} 92 | ] 93 | }); 94 | }); 95 | /////////////////////////////////////////////////////////////////////////////// 96 | 97 | app.listen(8110); 98 | console.log("Server running at http://localhost:8110 - get app.html or app-built.html"); 99 | 100 | 101 | function getLazyRegistry(req, res) { 102 | var modules, text, i, metadata, pmresult; 103 | 104 | modules = options.discoverModules(); 105 | pmresult = makePmresult(modules); 106 | text = shared.createModulesRegistryText(pmresult, options, { 107 | inludeModuleName: false, 108 | generateBody: true, 109 | nullBundleDeps: true, 110 | writeBundleRegistrations: false 111 | }); 112 | 113 | res.type("application/javascript"); 114 | res.send(text); 115 | 116 | 117 | function makePmresult(modules) { 118 | var i, dummyParents = ["DUMMY"], pmresult = { 119 | modulesArray: [] 120 | }; 121 | 122 | for( i=0; i < modules.length; i++ ) { 123 | pmresult.modulesArray.push({ 124 | name: modules[i], 125 | index: i, 126 | parents: dummyParents 127 | }); 128 | } 129 | 130 | return pmresult; 131 | } 132 | } 133 | 134 | function singleStringBodyParser(req, res, next) { 135 | var buf = ""; 136 | req.setEncoding("utf8"); 137 | req.on("data", onData); 138 | req.on("end", onEnd); 139 | 140 | function onData(chunk) { 141 | buf += chunk; 142 | } 143 | 144 | function onEnd() { 145 | req.body = buf; 146 | next(); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-expenses", 3 | "version": "0.6.2", 4 | "private": true, 5 | "dependencies": { 6 | "angular": "1.3.x", 7 | "angular-ui-bootstrap": "0.13.4", 8 | "requirejs": "latest", 9 | "requirejs-text": "latest", 10 | "almond": "latest", 11 | "jquery": "~2.1", 12 | "less.js": "1.7.2", 13 | "require-lazy": "latest", 14 | "ng-grid-bower": "latest", 15 | "angular-route": "1.3.x", 16 | "angular-resource": "1.3.x", 17 | "chartjs": "*" 18 | }, 19 | "devDependencies": { 20 | "angular-mocks": "1.3.x" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /build-scripts/app.build-grunt.json: -------------------------------------------------------------------------------- 1 | { 2 | "appDir": "../WebContent/", 3 | "baseUrl": "scripts/", 4 | "mainConfigFile": "WebContent/scripts/require-cfg.js", 5 | "dir": "../build/IRRELEVANT", 6 | "inlineText": true, 7 | "name": "app/main/main", 8 | "optimize": "none", 9 | "normalizeDirDefines": true, 10 | 11 | "paths": { 12 | "jquery": "empty:", 13 | "angular": "empty:" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /build-scripts/discoverModules.js: -------------------------------------------------------------------------------- 1 | var path = require("path"), fs = require("fs"); 2 | 3 | function discoverModules() { 4 | var modulesDir = path.normalize(path.join(__dirname, "..", "WebContent", "scripts", "app", "modules")), 5 | filenames = fs.readdirSync(modulesDir), 6 | stat, i, res = []; 7 | for( i=0; i < filenames.length; i++ ) { 8 | stat = fs.statSync(path.join(modulesDir, filenames[i], "main.metadata.json")); 9 | if( stat != null && stat.isFile() ) { 10 | res.push("app/modules/" + filenames[i] + "/main"); 11 | } 12 | } 13 | return res; 14 | // return ["app/modules/index","app/modules/categories","app/modules/expenses"]; 15 | } 16 | 17 | module.exports = discoverModules; 18 | -------------------------------------------------------------------------------- /build-scripts/grunt/grunt.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.registerMultiTask("instrument", "Instrument with istanbul", function() { 3 | var istanbul = require("istanbul"), 4 | instrumenter, 5 | options, 6 | instrumenterOptions, 7 | baselineCollector; 8 | 9 | options = this.options({ 10 | }); 11 | 12 | if( options.baseline ) { 13 | baselineCollector = new istanbul.Collector(); 14 | } 15 | 16 | instrumenterOptions = { 17 | coverageVariable: options.coverageVariable || "__coverage__", 18 | embedSource: options.embedSource || false, 19 | preserveComments: options.preserveComments || false, 20 | noCompact: options.noCompact || false, 21 | noAutoWrap: options.noAutoWrap || false, 22 | codeGenerationOptions: options.codeGenerationOptions, 23 | debug: options.debug || false, 24 | walkDebug: options.walkDebug || false 25 | }; 26 | 27 | instrumenter = new istanbul.Instrumenter(instrumenterOptions); 28 | 29 | this.files.forEach(function(f) { 30 | //console.log(f.src, " -> ", f.dest); 31 | if( f.src.length !== 1 ) { 32 | throw new Error("encountered src with length: " + f.src.length + ": " + JSON.stringify(f.src)); 33 | } 34 | var filename = f.src[0], 35 | code = grunt.file.read(filename, {encoding: grunt.file.defaultEncoding}), 36 | result = instrumenter.instrumentSync(code, filename), 37 | baseline, 38 | coverage; 39 | 40 | if( options.baseline ) { 41 | baseline = instrumenter.lastFileCoverage(); 42 | coverage = {}; 43 | coverage[baseline.path] = baseline; 44 | baselineCollector.add(coverage); 45 | } 46 | 47 | grunt.file.write(f.dest, result, {encoding: grunt.file.defaultEncoding}); 48 | }); 49 | 50 | if( options.baseline ) { 51 | grunt.file.write(options.baseline, JSON.stringify(baselineCollector.getFinalCoverage()), {encoding: grunt.file.defaultEncoding}); 52 | } 53 | }); 54 | }; 55 | -------------------------------------------------------------------------------- /build-scripts/karma/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "preprocessor:preInstrumented": ["factory", require("./preInstrumentedPreprocessor")], 3 | "reporter:coverage": ["type", require("./reporter")] 4 | }; 5 | -------------------------------------------------------------------------------- /build-scripts/karma/preInstrumentedPreprocessor.js: -------------------------------------------------------------------------------- 1 | var path = require("path"), 2 | fs = require("fs"); 3 | 4 | createPreInstrumentedPreprocessor.$inject = ["args", "config.preInstrumentedPreprocessor", "config.basePath", "logger", "helper"]; 5 | function createPreInstrumentedPreprocessor(args, config, basePath, logger, helper) { 6 | var STRIP_PREFIX_RE = new RegExp("^" + path.join(basePath, config.stripPrefix).replace(/\\/g, "\\\\")); 7 | 8 | function instrumentedFilePath(file) { 9 | return path.join(basePath, config.basePath, path.normalize(file.originalPath).replace(STRIP_PREFIX_RE, "")); 10 | } 11 | 12 | return function(content, file, done) { 13 | fs.readFile(instrumentedFilePath(file), {encoding:"utf8"}, function(err, instrumentedContent) { 14 | if( err ) throw err; 15 | done(instrumentedContent); 16 | }); 17 | }; 18 | } 19 | 20 | module.exports = createPreInstrumentedPreprocessor; 21 | -------------------------------------------------------------------------------- /build-scripts/karma/reporter.js: -------------------------------------------------------------------------------- 1 | // DERIVED FROM THE COVERAGE REPORTER OF KARMA, https://github.com/karma-runner/karma-coverage/blob/master/lib/reporter.js 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var util = require('util'); 5 | var istanbul = require('istanbul'); 6 | var dateformat = require('dateformat'); 7 | 8 | 9 | var Store = istanbul.Store; 10 | 11 | var BasePathStore = function(opts) { 12 | Store.call(this, opts); 13 | opts = opts || {}; 14 | this.basePath = opts.basePath; 15 | this.delegate = Store.create('fslookup'); 16 | }; 17 | BasePathStore.TYPE = 'basePathlookup'; 18 | util.inherits(BasePathStore, Store); 19 | 20 | Store.mix(BasePathStore, { 21 | keys : function() { 22 | return this.delegate.keys(); 23 | }, 24 | toKey : function(key) { 25 | if (key.indexOf('./') === 0) { return path.join(this.basePath, key); } 26 | return key; 27 | }, 28 | get : function(key) { 29 | return this.delegate.get(this.toKey(key)); 30 | }, 31 | hasKey : function(key) { 32 | return this.delegate.hasKey(this.toKey(key)); 33 | }, 34 | set : function(key, contents) { 35 | return this.delegate.set(this.toKey(key), contents); 36 | } 37 | }); 38 | 39 | 40 | // TODO(vojta): inject only what required (config.basePath, config.coverageReporter) 41 | var CoverageReporter = function(rootConfig, helper, logger) { 42 | var log = logger.create('coverage'); 43 | var config = rootConfig.coverageReporter || {}; 44 | var basePath = rootConfig.basePath; 45 | var reporters = config.reporters; 46 | var baseLine; 47 | 48 | if (config.baseLine) { 49 | baseLine = JSON.parse(fs.readFileSync(path.join(basePath, config.baseLine), {encoding:"utf8"})); 50 | } 51 | 52 | if (!helper.isDefined(reporters)) { 53 | reporters = [config]; 54 | } 55 | 56 | this.adapters = []; 57 | var collectors; 58 | var pendingFileWritings = 0; 59 | var fileWritingFinished = function() {}; 60 | 61 | function writeEnd() { 62 | if (!--pendingFileWritings) { 63 | // cleanup collectors 64 | Object.keys(collectors).forEach(function(key) { 65 | collectors[key].dispose(); 66 | }); 67 | fileWritingFinished(); 68 | } 69 | } 70 | 71 | /** 72 | * Generate the output directory from the `coverageReporter.dir` and 73 | * `coverageReporter.subdir` options. 74 | * 75 | * @param {String} browserName - The browser name 76 | * @param {String} dir - The given option 77 | * @param {String|Function} subdir - The given option 78 | * 79 | * @return {String} - The output directory 80 | */ 81 | function generateOutputDir(browserName, dir, subdir) { 82 | dir = dir || 'coverage'; 83 | subdir = subdir || browserName; 84 | 85 | if (typeof subdir === 'function') { 86 | subdir = subdir(browserName); 87 | } 88 | 89 | return path.join(dir, subdir); 90 | } 91 | 92 | this.onRunStart = function(browsers) { 93 | collectors = Object.create(null); 94 | 95 | // TODO(vojta): remove once we don't care about Karma 0.10 96 | if (browsers) { 97 | browsers.forEach(function(browser) { 98 | collectors[browser.id] = new istanbul.Collector(); 99 | }); 100 | } 101 | }; 102 | 103 | this.onBrowserStart = function(browser) { 104 | var collector = new istanbul.Collector(); 105 | if( baseLine ) { 106 | collector.add(baseLine); 107 | } 108 | collectors[browser.id] = collector; 109 | }; 110 | 111 | this.onBrowserComplete = function(browser, result) { 112 | var collector = collectors[browser.id]; 113 | 114 | if (!collector) { 115 | return; 116 | } 117 | 118 | if (result && result.coverage) { 119 | collector.add(result.coverage); 120 | } 121 | }; 122 | 123 | this.onSpecComplete = function(browser, result) { 124 | if (result.coverage) { 125 | collectors[browser.id].add(result.coverage); 126 | } 127 | }; 128 | 129 | this.onRunComplete = function(browsers) { 130 | reporters.forEach(function(reporterConfig) { 131 | browsers.forEach(function(browser) { 132 | 133 | var collector = collectors[browser.id]; 134 | if (collector) { 135 | pendingFileWritings++; 136 | 137 | var outputDir = helper.normalizeWinPath(path.resolve(basePath, generateOutputDir(browser.name, 138 | reporterConfig.dir || config.dir, 139 | reporterConfig.subdir || config.subdir))); 140 | 141 | helper.mkdirIfNotExists(outputDir, function() { 142 | log.debug('Writing coverage to %s', outputDir); 143 | var options = helper.merge({}, reporterConfig, { 144 | dir : outputDir, 145 | sourceStore : new BasePathStore({ 146 | basePath : basePath 147 | }) 148 | }); 149 | var reporter = istanbul.Report.create(reporterConfig.type || 'html', options); 150 | try { 151 | reporter.writeReport(collector, true); 152 | } catch (e) { 153 | log.error(e); 154 | } 155 | writeEnd(); 156 | }); 157 | } 158 | 159 | }); 160 | }); 161 | }; 162 | 163 | this.onExit = function(done) { 164 | if (pendingFileWritings) { 165 | fileWritingFinished = done; 166 | } else { 167 | done(); 168 | } 169 | }; 170 | }; 171 | 172 | CoverageReporter.$inject = ['config', 'helper', 'logger']; 173 | 174 | // PUBLISH 175 | module.exports = CoverageReporter; 176 | -------------------------------------------------------------------------------- /build-scripts/options-grunt.js: -------------------------------------------------------------------------------- 1 | var 2 | path = require("path"), 3 | fs = require("fs"), 4 | discoverModules = require("./discoverModules"); 5 | 6 | module.exports = { 7 | // this must match the map['*'].lazy property in require-cfg.js 8 | libLazy: "lib/require-lazy/lazy", 9 | basePath: path.normalize(path.join(__dirname, "..", "WebContent")), 10 | outputBaseDir: path.normalize(path.join(__dirname, "..", "build")), 11 | discoverModules: discoverModules, 12 | retrieveMetaForModule: retrieveMetaForModule, 13 | makeBuildRelativePath: function(x) { 14 | return path.normalize(path.join(__dirname, "..", x)); 15 | } 16 | }; 17 | 18 | function retrieveMetaForModule(moduleName) { 19 | var 20 | stat = null, 21 | filename = path.normalize(path.join(__dirname, "../WebContent/scripts/", moduleName + ".metadata.json")), 22 | ret = null; 23 | try { 24 | stat = fs.statSync(filename); 25 | } 26 | catch(ignore) {} 27 | if( stat !== null && stat.isFile() ) { 28 | try { 29 | ret = fs.readFileSync(filename); 30 | ret = JSON.parse(ret); 31 | } 32 | catch(e) { 33 | ret = null; 34 | console.log(e); 35 | } 36 | } 37 | return ret; 38 | } 39 | -------------------------------------------------------------------------------- /build-stats/.gitignore: -------------------------------------------------------------------------------- 1 | modules.js 2 | bundles.js 3 | 4 | -------------------------------------------------------------------------------- /build-stats/amd.html: -------------------------------------------------------------------------------- 1 |
    2 |

    AMD Modules

    3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
    {{k}}{{v.id}}Exclusive{{v.includedIn}}
    13 |
    14 | -------------------------------------------------------------------------------- /build-stats/bundles.html: -------------------------------------------------------------------------------- 1 |
    2 |

    Bundles

    3 | 4 |
      5 |
    • 6 | {{bundle.id}} Exclusive: {{bundle.exclusive}} Included in: {{bundle.includedIn}} 7 |
      8 | 9 |
      10 |
    • 11 |
    12 |
    13 | -------------------------------------------------------------------------------- /build-stats/deps.html: -------------------------------------------------------------------------------- 1 |
    2 | + 3 | {{heading}}: 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
    #AMD ModuleBundle
    {{$index+1}}.{{dep}}{{depsToBundleMap[dep].id}}
    20 |
    21 | -------------------------------------------------------------------------------- /build-stats/f.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var obj = { 4 | internal : {} 5 | }; 6 | 7 | Object.freeze(obj); 8 | obj.internal.a = "aValue"; 9 | 10 | console.log(obj.internal.a); // "aValue" 11 | 12 | // To make obj fully immutable, freeze each object in obj. 13 | // To do so, we use this function. 14 | 15 | function deepFreeze (o) { 16 | var prop, propKey; 17 | Object.freeze(o); // First freeze the object. 18 | for (propKey in o) { 19 | prop = o[propKey]; 20 | if (!o.hasOwnProperty(propKey) || !(typeof prop === "object") || Object.isFrozen(prop)) { 21 | // If the object is on the prototype, not an object, or is already frozen, 22 | // skip it. Note that this might leave an unfrozen reference somewhere in the 23 | // object if there is an already frozen object containing an unfrozen object. 24 | continue; 25 | } 26 | 27 | deepFreeze(prop); // Recursively call deepFreeze. 28 | } 29 | } 30 | 31 | var obj2 = { 32 | internal : {} 33 | }; 34 | 35 | deepFreeze(obj2); 36 | obj2.internal.a = "anotherValue"; 37 | console.log(obj2.internal.a); // undefined 38 | -------------------------------------------------------------------------------- /build-stats/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Stats 6 | 29 | 30 | 31 |
    Modules | Bundles | AMD Modules
    32 |
    33 |
    34 |
    35 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /build-stats/modules.html: -------------------------------------------------------------------------------- 1 |
    2 |

    Modules

    3 | 4 |
      5 |
    • 6 | {{mname}} {{module.hash}} 7 |
      8 |
      9 | Parents: 10 | [
        11 |
      • {{parent}}
      • 12 |
      ] 13 |
      14 | 15 |
      16 | Bundle Dependencies: 17 | [] 20 |
      21 | 22 |
      23 |
    • 24 |
    25 |
    26 | -------------------------------------------------------------------------------- /build-stats/stats.js: -------------------------------------------------------------------------------- 1 | var app = angular.module("app", ["ngRoute"]); 2 | 3 | app.config(function($routeProvider) { 4 | $routeProvider.when("/modules", {controller:"ModulesCtrl", templateUrl:"modules.html", reloadOnSearch:false}); 5 | $routeProvider.when("/bundles", {controller:"BundlesCtrl", templateUrl:"bundles.html", reloadOnSearch:false}); 6 | $routeProvider.when("/amd", {controller:"AmdCtrl", templateUrl:"amd.html", reloadOnSearch:false}); 7 | $routeProvider.otherwise({redirectTo: "/modules"}); 8 | }); 9 | 10 | app.controller("ModulesCtrl", function($scope, modules, $location) { 11 | $scope.modules = modules; 12 | 13 | $scope.toggle = function(x, $event) { 14 | $event.preventDefault(); 15 | if( x == null ) return; 16 | if( x.$ui == null ) x.$ui = {}; 17 | if( x.$ui.on = !x.$ui.on ) { 18 | // $location.replace(); 19 | // $location.search("m", x.name); 20 | } 21 | // else { 22 | // if( $location.search("m") === x.name ) { 23 | // $location.replace(); 24 | // $location.search("m", null); 25 | // } 26 | // } 27 | }; 28 | 29 | $scope.collapseAll = function() { 30 | var x; 31 | for( x in modules ) { 32 | if( !modules.hasOwnProperty(x) ) continue; 33 | x = modules[x]; 34 | if( x.$ui ) { 35 | x.$ui.on = false; 36 | x.$ui.depsOn = false; 37 | } 38 | } 39 | }; 40 | }); 41 | 42 | app.controller("BundlesCtrl", function($scope, bundles, $location) { 43 | $scope.bundles = bundles.bundlesArray; 44 | 45 | initSelection(); 46 | 47 | $scope.toggle = function(x, $event) { 48 | $event.preventDefault(); 49 | if( x == null ) return; 50 | if( x.$ui == null ) x.$ui = {}; 51 | if( x.$ui.on = !x.$ui.on ) { 52 | // $location.replace(); 53 | // $location.search("b", x.name); 54 | } 55 | }; 56 | 57 | $scope.collapseAll = function() { 58 | var x; 59 | for( x in $scope.bundles ) { 60 | if( !$scope.bundles.hasOwnProperty(x) ) continue; 61 | x = $scope.bundles[x]; 62 | if( x.$ui ) { 63 | x.$ui.on = false; 64 | x.$ui.depsOn = false; 65 | } 66 | } 67 | }; 68 | 69 | function initSelection() { 70 | var selection = $location.search().b, i; 71 | if( selection ) { 72 | for( i=0; i < $scope.bundles.length; i++ ) { 73 | if( $scope.bundles[i].id === selection ) { 74 | if( $scope.bundles[i].$ui == null ) $scope.bundles[i].$ui = {}; 75 | $scope.bundles[i].$ui.on = true; 76 | break; 77 | } 78 | } 79 | } 80 | } 81 | }); 82 | 83 | app.directive("depsList", function() { 84 | return { 85 | restrict: "EA", 86 | templateUrl: "deps.html", 87 | replace: true, 88 | scope: { 89 | x: "=item", 90 | deps: "=", 91 | flag: "=", 92 | heading: "@" 93 | }, 94 | controller: function($scope, bundles) { 95 | $scope.depsToBundleMap = bundles.depsToBundleMap; 96 | $scope.toggleDeps = function($event) { 97 | $event.preventDefault(); 98 | $scope.flag = !$scope.flag; 99 | }; 100 | } 101 | }; 102 | }); 103 | 104 | app.controller("SanityCtrl", function($scope, bundles, modules) { 105 | }); 106 | 107 | app.controller("AmdCtrl", function($scope, bundles, modules) { 108 | $scope.depsToBundleMap = bundles.depsToBundleMap; 109 | }); 110 | -------------------------------------------------------------------------------- /karma-coverage.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | // base path that will be used to resolve all patterns (eg. files, exclude) 4 | basePath: '', 5 | 6 | // frameworks to use 7 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 8 | frameworks: [ 9 | 'jasmine', 10 | 'requirejs', 11 | 'angular-require-lazy' 12 | ], 13 | 14 | plugins: [ 15 | 'karma-phantomjs-launcher', 16 | 'karma-jasmine', 17 | 'karma-requirejs', 18 | // TODO Add our RequireJS configuration by using an inlined plugin, see http://karma-runner.github.io/0.12/config/plugins.html 19 | { 20 | 'framework:angular-require-lazy': ['factory', (function() { 21 | function alrPlugin(files) { 22 | files.unshift({pattern: __dirname + '/WebTests/scripts/mocks/require-cfg.js', included: true, served: true, watched: false}); 23 | files.unshift({pattern: __dirname + '/WebContent/scripts/require-cfg.js', included: true, served: true, watched: false}); 24 | } 25 | alrPlugin.$inject = ['config.files']; 26 | return alrPlugin; 27 | })()] 28 | }, 29 | require('./build-scripts/karma') 30 | ], 31 | 32 | // list of files / patterns to load in the browser 33 | files: [ 34 | 'WebTests/scripts/test-main.js', 35 | {pattern: 'WebContent/scripts/lib/jquery/dist/jquery.js', included: true}, 36 | {pattern: 'WebContent/scripts/**/*.html', included: false}, 37 | {pattern: 'WebContent/scripts/*.js', included: false}, 38 | {pattern: 'WebContent/scripts/app/**/*.js', included: false}, 39 | {pattern: 'WebContent/scripts/util/**/*.js', included: false}, 40 | {pattern: 'WebContent/scripts/lib/**/*.js', included: false}, 41 | {pattern: 'WebTests/scripts/**/*.js', included: false} 42 | ], 43 | 44 | // list of files to exclude 45 | exclude: [ 46 | 'WebContent/scripts/lib/**/*.spec.js' 47 | ], 48 | 49 | // preprocess matching files before serving them to the browser 50 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 51 | preprocessors: { 52 | 'WebContent/scripts/app/**/*.js': 'preInstrumented', 53 | 'WebContent/scripts/util/**/*.js': 'preInstrumented' 54 | }, 55 | 56 | preInstrumentedPreprocessor: { 57 | basePath: 'build-coverage/instrumented', 58 | stripPrefix: 'WebContent' 59 | }, 60 | 61 | // test results reporter to use 62 | // possible values: 'dots', 'progress' 63 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 64 | reporters: [ 65 | 'progress', 66 | 'coverage' 67 | ], 68 | 69 | coverageReporter: { 70 | type: 'lcov', 71 | dir: 'build-coverage/report', 72 | subdir: function(browser) { 73 | return 'output'; 74 | }, 75 | baseLine: 'build-coverage/baseline.json' 76 | }, 77 | 78 | // web server port 79 | port: 9876, 80 | 81 | // enable / disable colors in the output (reporters and logs) 82 | colors: true, 83 | 84 | // level of logging 85 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 86 | logLevel: config.LOG_INFO, 87 | 88 | // enable / disable watching file and executing tests whenever any file changes 89 | autoWatch: true, 90 | 91 | // start these browsers 92 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 93 | // browsers: ['Firefox'], 94 | browsers: ['PhantomJS'], 95 | 96 | // Continuous Integration mode 97 | // if true, Karma captures browsers, runs the tests and exits 98 | singleRun: false 99 | }); 100 | }; 101 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | // base path that will be used to resolve all patterns (eg. files, exclude) 4 | basePath: '', 5 | 6 | // frameworks to use 7 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 8 | frameworks: [ 9 | 'jasmine', 10 | 'requirejs', 11 | 'angular-require-lazy' 12 | ], 13 | 14 | plugins: [ 15 | 'karma-phantomjs-launcher', 16 | 'karma-jasmine', 17 | 'karma-requirejs', 18 | // TODO Add our RequireJS configuration by using an inlined plugin, see http://karma-runner.github.io/0.12/config/plugins.html 19 | { 20 | 'framework:angular-require-lazy': ['factory', (function() { 21 | function alrPlugin(files) { 22 | files.unshift({pattern: __dirname + '/WebTests/scripts/mocks/require-cfg.js', included: true, served: true, watched: false}); 23 | files.unshift({pattern: __dirname + '/WebContent/scripts/require-cfg.js', included: true, served: true, watched: false}); 24 | } 25 | alrPlugin.$inject = ['config.files']; 26 | return alrPlugin; 27 | })()] 28 | } 29 | ], 30 | 31 | // list of files / patterns to load in the browser 32 | files: [ 33 | 'WebTests/scripts/test-main.js', 34 | {pattern: 'WebContent/scripts/lib/jquery/dist/jquery.js', included: true}, 35 | {pattern: 'WebContent/scripts/**/*.html', included: false}, 36 | {pattern: 'WebContent/scripts/*.js', included: false}, 37 | {pattern: 'WebContent/scripts/app/**/*.js', included: false}, 38 | {pattern: 'WebContent/scripts/util/**/*.js', included: false}, 39 | {pattern: 'WebContent/scripts/lib/**/*.js', included: false}, 40 | {pattern: 'WebTests/scripts/**/*.js', included: false} 41 | ], 42 | 43 | // list of files to exclude 44 | exclude: [ 45 | 'WebContent/scripts/lib/**/*.spec.js' 46 | ], 47 | 48 | // preprocess matching files before serving them to the browser 49 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 50 | preprocessors: { 51 | 52 | }, 53 | 54 | // test results reporter to use 55 | // possible values: 'dots', 'progress' 56 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 57 | reporters: ['progress'], 58 | 59 | // web server port 60 | port: 9876, 61 | 62 | // enable / disable colors in the output (reporters and logs) 63 | colors: true, 64 | 65 | // level of logging 66 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 67 | logLevel: config.LOG_INFO, 68 | 69 | // enable / disable watching file and executing tests whenever any file changes 70 | autoWatch: true, 71 | 72 | // start these browsers 73 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 74 | // browsers: ['Firefox'], 75 | browsers: ['PhantomJS'], 76 | 77 | // Continuous Integration mode 78 | // if true, Karma captures browsers, runs the tests and exits 79 | singleRun: false 80 | }); 81 | }; 82 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-expenses", 3 | "description": "An experiment for mixing Angular, RequireJS and require-lazy", 4 | "version": "0.6.2", 5 | "private": true, 6 | "dependencies": { 7 | "express": "3.x", 8 | "require-lazy": ">=0.3.8" 9 | }, 10 | "devDependencies": { 11 | "grunt": "~0.4", 12 | "require-lazy-grunt": ">=0.2.0", 13 | "grunt-contrib-clean": "*", 14 | "grunt-contrib-copy": "*", 15 | "grunt-contrib-less": "0.12.0", 16 | "grunt-karma": "*", 17 | "bower": "*", 18 | "karma": "*", 19 | "karma-jasmine": "~0.2.0", 20 | "karma-chrome-launcher": "*", 21 | "karma-firefox-launcher": "*", 22 | "karma-requirejs": "*", 23 | "karma-phantomjs-launcher": "*", 24 | "istanbul": "*", 25 | "dateformat": "~1.0.6" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=nikospara:angular-require-lazy 2 | sonar.projectName=angular-require-lazy 3 | sonar.projectVersion=0.6.0 4 | sonar.language=js 5 | sonar.sources=WebContent/scripts/app,WebContent/scripts/util 6 | sonar.sourceEncoding=UTF-8 7 | # To import the LCOV report 8 | sonar.javascript.lcov.reportPath=build-coverage/report/output/lcov.info 9 | --------------------------------------------------------------------------------