├── .bowerrc ├── .buildignore ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── .yo-rc.json ├── Gruntfile.js ├── README.md ├── bower.json ├── client ├── .htaccess ├── .jshintrc ├── app │ ├── account │ │ ├── account.js │ │ ├── changePassword │ │ │ ├── changePassword.controller.js │ │ │ └── changePassword.html │ │ ├── forgotPassword │ │ │ ├── forgotPassword.controller.js │ │ │ └── forgotPassword.html │ │ ├── login │ │ │ ├── login.controller.js │ │ │ └── login.html │ │ └── signUp │ │ │ ├── signUp.controller.js │ │ │ └── signUp.html │ ├── app.js │ ├── app.scss │ ├── home │ │ ├── home.controller.js │ │ ├── home.html │ │ └── home.js │ ├── interceptor.js │ └── landing │ │ ├── landing.controller.js │ │ ├── landing.html │ │ └── landing.js ├── assets │ └── images │ │ └── yeoman.png ├── components │ ├── api-services │ │ └── user │ │ │ └── user.service.js │ ├── auth │ │ └── auth.service.js │ ├── config │ │ └── config.js │ ├── constants │ │ └── constants.js │ ├── footer │ │ ├── footer.directive.js │ │ └── footer.html │ ├── header │ │ ├── header.controller.js │ │ ├── header.directive.js │ │ └── header.html │ ├── loader │ │ └── loader.service.js │ ├── messages │ │ └── messages.js │ ├── modal │ │ ├── modal.controller.js │ │ ├── modal.css │ │ ├── modal.service.js │ │ ├── popUp.html │ │ └── views │ │ │ ├── error.html │ │ │ └── success.html │ └── util │ │ └── helper.js ├── favicon.ico ├── index.html ├── robots.txt └── test │ ├── account │ ├── forgotPassword │ │ └── forgotPassword.controller.spec.js │ ├── login │ │ └── login.controller.spec.js │ ├── setPassword │ │ └── setPassword.controller.spec.js │ └── signup │ │ └── signup.controller.spec.js │ ├── home │ └── home.controller.spec.js │ ├── landing │ └── landing.controller.spec.js │ └── mocked.data.js ├── e2e └── main │ ├── main.po.js │ └── main.spec.js ├── karma.conf.js ├── package.json ├── protractor.conf.js └── server └── app.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "client/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.buildignore: -------------------------------------------------------------------------------- 1 | *.coffee -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princesoni1989/angular-js-seed/2e8338075f6c03f6204dab1a73f357a48d16da5e/.gitattributes -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public 3 | .tmp 4 | .sass-cache 5 | .idea 6 | client/bower_components 7 | dist 8 | npm-debug.log 9 | coverage 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | - '0.11' 5 | before_script: 6 | - npm install -g bower grunt-cli 7 | - gem install sass 8 | - bower install 9 | -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-angular-fullstack": { 3 | "insertRoutes": true, 4 | "registerRoutesFile": "server/routes.js", 5 | "routesNeedle": "// Insert routes below", 6 | "routesBase": "/api/", 7 | "pluralizeRoutes": true, 8 | "insertSockets": true, 9 | "registerSocketsFile": "server/config/socketio.js", 10 | "socketsNeedle": "// Insert sockets below", 11 | "filters": { 12 | "babel": false, 13 | "js": true, 14 | "html": true, 15 | "sass": true, 16 | "uirouter": true, 17 | "bootstrap": true, 18 | "uibootstrap": true, 19 | "mongoose": true, 20 | "auth": true 21 | } 22 | }, 23 | "generator-ng-component": { 24 | "routeDirectory": "client/app/", 25 | "directiveDirectory": "client/app/", 26 | "filterDirectory": "client/app/", 27 | "serviceDirectory": "client/app/", 28 | "basePath": "client", 29 | "moduleName": "", 30 | "filters": [ 31 | "uirouter" 32 | ], 33 | "extensions": [ 34 | "js", 35 | "html", 36 | "scss" 37 | ], 38 | "directiveSimpleTemplates": "", 39 | "directiveComplexTemplates": "", 40 | "filterTemplates": "", 41 | "serviceTemplates": "", 42 | "factoryTemplates": "", 43 | "controllerTemplates": "", 44 | "decoratorTemplates": "", 45 | "providerTemplates": "", 46 | "routeTemplates": "" 47 | } 48 | } -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | // Generated on 2016-05-28 using generator-angular-fullstack 2.1.1 2 | 'use strict'; 3 | 4 | module.exports = function (grunt) { 5 | var localConfig; 6 | try { 7 | localConfig = require('./server/config/local.env'); 8 | } catch(e) { 9 | localConfig = {}; 10 | } 11 | 12 | // Load grunt tasks automatically, when needed 13 | require('jit-grunt')(grunt, { 14 | express: 'grunt-express-server', 15 | useminPrepare: 'grunt-usemin', 16 | ngtemplates: 'grunt-angular-templates', 17 | cdnify: 'grunt-google-cdn', 18 | protractor: 'grunt-protractor-runner', 19 | buildcontrol: 'grunt-build-control' 20 | }); 21 | 22 | // Time how long tasks take. Can help when optimizing build times 23 | require('time-grunt')(grunt); 24 | 25 | // Define the configuration for all the tasks 26 | grunt.initConfig({ 27 | 28 | // Project settings 29 | pkg: grunt.file.readJSON('package.json'), 30 | yeoman: { 31 | // configurable paths 32 | client: require('./bower.json').appPath || 'client', 33 | dist: 'dist' 34 | }, 35 | express: { 36 | options: { 37 | port: process.env.PORT || 9000 38 | }, 39 | dev: { 40 | options: { 41 | script: 'server/app.js', 42 | debug: true 43 | } 44 | }, 45 | prod: { 46 | options: { 47 | script: 'dist/server/app.js' 48 | } 49 | } 50 | }, 51 | open: { 52 | server: { 53 | url: 'http://localhost:<%= express.options.port %>' 54 | } 55 | }, 56 | watch: { 57 | injectJS: { 58 | files: [ 59 | '<%= yeoman.client %>/{app,components}/**/*.js', 60 | '!<%= yeoman.client %>/{app,components}/**/*.spec.js', 61 | '!<%= yeoman.client %>/{app,components}/**/*.mock.js', 62 | '!<%= yeoman.client %>/app/app.js'], 63 | tasks: ['injector:scripts'] 64 | }, 65 | injectCss: { 66 | files: [ 67 | '<%= yeoman.client %>/{app,components}/**/*.css' 68 | ], 69 | tasks: ['injector:css'] 70 | }, 71 | jsTest: { 72 | files: [ 73 | '<%= yeoman.client %>/{app,components}/**/*.spec.js', 74 | '<%= yeoman.client %>/{app,components}/**/*.mock.js' 75 | ], 76 | tasks: ['newer:jshint:all', 'karma'] 77 | }, 78 | injectSass: { 79 | files: [ 80 | '<%= yeoman.client %>/{app,components}/**/*.{scss,sass}'], 81 | tasks: ['injector:sass'] 82 | }, 83 | sass: { 84 | files: [ 85 | '<%= yeoman.client %>/{app,components}/**/*.{scss,sass}'], 86 | tasks: ['sass', 'autoprefixer'] 87 | }, 88 | gruntfile: { 89 | files: ['Gruntfile.js'] 90 | }, 91 | livereload: { 92 | files: [ 93 | '{.tmp,<%= yeoman.client %>}/{app,components}/**/*.css', 94 | '{.tmp,<%= yeoman.client %>}/{app,components}/**/*.html', 95 | 96 | '{.tmp,<%= yeoman.client %>}/{app,components}/**/*.js', 97 | 98 | '!{.tmp,<%= yeoman.client %>}{app,components}/**/*.spec.js', 99 | '!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.mock.js', 100 | '<%= yeoman.client %>/assets/images/{,*//*}*.{png,jpg,jpeg,gif,webp,svg}' 101 | ], 102 | options: { 103 | livereload: true 104 | } 105 | }, 106 | express: { 107 | files: [ 108 | 'server/**/*.{js,json}' 109 | ], 110 | tasks: ['express:dev', 'wait'], 111 | options: { 112 | livereload: true, 113 | nospawn: true //Without this option specified express won't be reloaded 114 | } 115 | } 116 | }, 117 | 118 | // Make sure code styles are up to par and there are no obvious mistakes 119 | jshint: { 120 | options: { 121 | jshintrc: '<%= yeoman.client %>/.jshintrc', 122 | reporter: require('jshint-stylish') 123 | }, 124 | server: { 125 | options: { 126 | jshintrc: 'server/.jshintrc' 127 | }, 128 | src: [ 129 | 'server/**/*.js', 130 | '!server/**/*.spec.js' 131 | ] 132 | }, 133 | serverTest: { 134 | options: { 135 | jshintrc: 'server/.jshintrc-spec' 136 | }, 137 | src: ['server/**/*.spec.js'] 138 | }, 139 | all: [ 140 | '<%= yeoman.client %>/{app,components}/**/*.js', 141 | '!<%= yeoman.client %>/{app,components}/**/*.spec.js', 142 | '!<%= yeoman.client %>/{app,components}/**/*.mock.js' 143 | ], 144 | test: { 145 | src: [ 146 | '<%= yeoman.client %>/{app,components}/**/*.spec.js', 147 | '<%= yeoman.client %>/{app,components}/**/*.mock.js' 148 | ] 149 | } 150 | }, 151 | 152 | // Empties folders to start fresh 153 | clean: { 154 | dist: { 155 | files: [{ 156 | dot: true, 157 | src: [ 158 | '.tmp', 159 | '<%= yeoman.dist %>/*', 160 | '!<%= yeoman.dist %>/.git*', 161 | '!<%= yeoman.dist %>/.openshift', 162 | '!<%= yeoman.dist %>/Procfile' 163 | ] 164 | }] 165 | }, 166 | server: '.tmp' 167 | }, 168 | 169 | // Add vendor prefixed styles 170 | autoprefixer: { 171 | options: { 172 | browsers: ['last 1 version'] 173 | }, 174 | dist: { 175 | files: [{ 176 | expand: true, 177 | cwd: '.tmp/', 178 | src: '{,*/}*.css', 179 | dest: '.tmp/' 180 | }] 181 | } 182 | }, 183 | 184 | // Debugging with node inspector 185 | 'node-inspector': { 186 | custom: { 187 | options: { 188 | 'web-host': 'localhost' 189 | } 190 | } 191 | }, 192 | 193 | // Use nodemon to run server in debug mode with an initial breakpoint 194 | nodemon: { 195 | debug: { 196 | script: 'server/app.js', 197 | options: { 198 | nodeArgs: ['--debug-brk'], 199 | env: { 200 | PORT: process.env.PORT || 9000 201 | }, 202 | callback: function (nodemon) { 203 | nodemon.on('log', function (event) { 204 | console.log(event.colour); 205 | }); 206 | 207 | // opens browser on initial server start 208 | nodemon.on('config:update', function () { 209 | setTimeout(function () { 210 | require('open')('http://localhost:8080/debug?port=5858'); 211 | }, 500); 212 | }); 213 | } 214 | } 215 | } 216 | }, 217 | 218 | // Automatically inject Bower components into the app 219 | wiredep: { 220 | target: { 221 | src: '<%= yeoman.client %>/index.html', 222 | ignorePath: '<%= yeoman.client %>/', 223 | exclude: [/bootstrap-sass-official/, /bootstrap.js/, '/json3/', '/es5-shim/', /bootstrap.css/, /font-awesome.css/ ] 224 | } 225 | }, 226 | 227 | // Renames files for browser caching purposes 228 | rev: { 229 | dist: { 230 | files: { 231 | src: [ 232 | '<%= yeoman.dist %>/client/{,*/}*.js', 233 | '<%= yeoman.dist %>/client/{,*/}*.css', 234 | '<%= yeoman.dist %>/client/assets/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', 235 | '<%= yeoman.dist %>/client/assets/fonts/*' 236 | ] 237 | } 238 | } 239 | }, 240 | 241 | // Reads HTML for usemin blocks to enable smart builds that automatically 242 | // concat, minify and revision files. Creates configurations in memory so 243 | // additional tasks can operate on them 244 | useminPrepare: { 245 | html: ['<%= yeoman.client %>/index.html'], 246 | options: { 247 | dest: '<%= yeoman.dist %>/client' 248 | } 249 | }, 250 | 251 | // Performs rewrites based on rev and the useminPrepare configuration 252 | usemin: { 253 | html: ['<%= yeoman.dist %>/client/{,*/}*.html'], 254 | css: ['<%= yeoman.dist %>/client/{,*/}*.css'], 255 | js: ['<%= yeoman.dist %>/client/{,*/}*.js'], 256 | options: { 257 | assetsDirs: [ 258 | '<%= yeoman.dist %>/client', 259 | '<%= yeoman.dist %>/client/assets/images' 260 | ], 261 | // This is so we update image references in our ng-templates 262 | patterns: { 263 | js: [ 264 | [/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the JS to reference our revved images'] 265 | ] 266 | } 267 | } 268 | }, 269 | 270 | // The following *-min tasks produce minified files in the dist folder 271 | imagemin: { 272 | dist: { 273 | files: [{ 274 | expand: true, 275 | cwd: '<%= yeoman.client %>/assets/images', 276 | src: '{,*/}*.{png,jpg,jpeg,gif}', 277 | dest: '<%= yeoman.dist %>/client/assets/images' 278 | }] 279 | } 280 | }, 281 | 282 | svgmin: { 283 | dist: { 284 | files: [{ 285 | expand: true, 286 | cwd: '<%= yeoman.client %>/assets/images', 287 | src: '{,*/}*.svg', 288 | dest: '<%= yeoman.dist %>/client/assets/images' 289 | }] 290 | } 291 | }, 292 | 293 | // Allow the use of non-minsafe AngularJS files. Automatically makes it 294 | // minsafe compatible so Uglify does not destroy the ng references 295 | ngAnnotate: { 296 | dist: { 297 | files: [{ 298 | expand: true, 299 | cwd: '.tmp/concat', 300 | src: '**/*.js', 301 | dest: '.tmp/concat' 302 | }] 303 | } 304 | }, 305 | 306 | // Package all the html partials into a single javascript payload 307 | ngtemplates: { 308 | options: { 309 | // This should be the name of your apps angular module 310 | module: 'angularJsSeedApp', 311 | htmlmin: { 312 | collapseBooleanAttributes: true, 313 | collapseWhitespace: true, 314 | removeAttributeQuotes: true, 315 | removeEmptyAttributes: true, 316 | removeRedundantAttributes: true, 317 | removeScriptTypeAttributes: true, 318 | removeStyleLinkTypeAttributes: true 319 | }, 320 | usemin: 'app/app.js' 321 | }, 322 | main: { 323 | cwd: '<%= yeoman.client %>', 324 | src: ['{app,components}/**/*.html'], 325 | dest: '.tmp/templates.js' 326 | }, 327 | tmp: { 328 | cwd: '.tmp', 329 | src: ['{app,components}/**/*.html'], 330 | dest: '.tmp/tmp-templates.js' 331 | } 332 | }, 333 | 334 | // Replace Google CDN references 335 | cdnify: { 336 | dist: { 337 | html: ['<%= yeoman.dist %>/client/*.html'] 338 | } 339 | }, 340 | 341 | // Copies remaining files to places other tasks can use 342 | copy: { 343 | dist: { 344 | files: [{ 345 | expand: true, 346 | dot: true, 347 | cwd: '<%= yeoman.client %>', 348 | dest: '<%= yeoman.dist %>/client', 349 | src: [ 350 | '*.{ico,png,txt}', 351 | '.htaccess', 352 | 'bower_components/**/*', 353 | 'assets/images/{,*/}*.{webp}', 354 | 'assets/fonts/**/*', 355 | 'index.html' 356 | ] 357 | }, { 358 | expand: true, 359 | cwd: '.tmp/images', 360 | dest: '<%= yeoman.dist %>/client/assets/images', 361 | src: ['generated/*'] 362 | }, { 363 | expand: true, 364 | dest: '<%= yeoman.dist %>', 365 | src: [ 366 | 'package.json', 367 | 'server/**/*' 368 | ] 369 | }] 370 | }, 371 | styles: { 372 | expand: true, 373 | cwd: '<%= yeoman.client %>', 374 | dest: '.tmp/', 375 | src: ['{app,components}/**/*.css'] 376 | } 377 | }, 378 | 379 | buildcontrol: { 380 | options: { 381 | dir: 'dist', 382 | commit: true, 383 | push: true, 384 | connectCommits: false, 385 | message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%' 386 | }, 387 | heroku: { 388 | options: { 389 | remote: 'heroku', 390 | branch: 'master' 391 | } 392 | }, 393 | openshift: { 394 | options: { 395 | remote: 'openshift', 396 | branch: 'master' 397 | } 398 | } 399 | }, 400 | 401 | // Run some tasks in parallel to speed up the build process 402 | concurrent: { 403 | server: [ 404 | 'sass', 405 | ], 406 | test: [ 407 | 'sass', 408 | ], 409 | debug: { 410 | tasks: [ 411 | 'nodemon', 412 | 'node-inspector' 413 | ], 414 | options: { 415 | logConcurrentOutput: true 416 | } 417 | }, 418 | dist: [ 419 | 'sass', 420 | 'imagemin', 421 | 'svgmin' 422 | ] 423 | }, 424 | 425 | // Test settings 426 | karma: { 427 | unit: { 428 | configFile: 'karma.conf.js', 429 | singleRun: true 430 | } 431 | }, 432 | protractor: { 433 | options: { 434 | configFile: 'protractor.conf.js' 435 | }, 436 | chrome: { 437 | options: { 438 | args: { 439 | browser: 'chrome' 440 | } 441 | } 442 | } 443 | }, 444 | 445 | env: { 446 | test: { 447 | NODE_ENV: 'test' 448 | }, 449 | prod: { 450 | NODE_ENV: 'production' 451 | }, 452 | all: localConfig 453 | }, 454 | 455 | // Compiles Sass to CSS 456 | sass: { 457 | server: { 458 | options: { 459 | loadPath: [ 460 | '<%= yeoman.client %>/bower_components', 461 | '<%= yeoman.client %>/app', 462 | '<%= yeoman.client %>/components' 463 | ], 464 | compass: false 465 | }, 466 | files: { 467 | '.tmp/app/app.css' : '<%= yeoman.client %>/app/app.scss' 468 | } 469 | } 470 | }, 471 | 472 | injector: { 473 | options: { 474 | 475 | }, 476 | // Inject application script files into index.html (doesn't include bower) 477 | scripts: { 478 | options: { 479 | transform: function(filePath) { 480 | filePath = filePath.replace('/client/', ''); 481 | filePath = filePath.replace('/.tmp/', ''); 482 | return ''; 483 | }, 484 | starttag: '', 485 | endtag: '' 486 | }, 487 | files: { 488 | '<%= yeoman.client %>/index.html': [ 489 | [ 490 | 491 | '{.tmp,<%= yeoman.client %>}/{app,components}/**/*.js', 492 | 493 | '!{.tmp,<%= yeoman.client %>}/app/app.js', 494 | '!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.spec.js', 495 | '!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.mock.js' 496 | ] 497 | ] 498 | } 499 | }, 500 | 501 | // Inject component scss into app.scss 502 | sass: { 503 | options: { 504 | transform: function(filePath) { 505 | filePath = filePath.replace('/client/app/', ''); 506 | filePath = filePath.replace('/client/components/', ''); 507 | return '@import \'' + filePath + '\';'; 508 | }, 509 | starttag: '// injector', 510 | endtag: '// endinjector' 511 | }, 512 | files: { 513 | '<%= yeoman.client %>/app/app.scss': [ 514 | '<%= yeoman.client %>/{app,components}/**/*.{scss,sass}', 515 | '!<%= yeoman.client %>/app/app.{scss,sass}' 516 | ] 517 | } 518 | }, 519 | 520 | // Inject component css into index.html 521 | css: { 522 | options: { 523 | transform: function(filePath) { 524 | filePath = filePath.replace('/client/', ''); 525 | filePath = filePath.replace('/.tmp/', ''); 526 | return ''; 527 | }, 528 | starttag: '', 529 | endtag: '' 530 | }, 531 | files: { 532 | '<%= yeoman.client %>/index.html': [ 533 | '<%= yeoman.client %>/{app,components}/**/*.css' 534 | ] 535 | } 536 | } 537 | }, 538 | }); 539 | 540 | // Used for delaying livereload until after server has restarted 541 | grunt.registerTask('wait', function () { 542 | grunt.log.ok('Waiting for server reload...'); 543 | 544 | var done = this.async(); 545 | 546 | setTimeout(function () { 547 | grunt.log.writeln('Done waiting!'); 548 | done(); 549 | }, 1500); 550 | }); 551 | 552 | grunt.registerTask('express-keepalive', 'Keep grunt running', function() { 553 | this.async(); 554 | }); 555 | 556 | grunt.registerTask('serve', function (target) { 557 | if (target === 'dist') { 558 | return grunt.task.run(['build', 'env:all', 'env:prod', 'express:prod', 'wait', 'open', 'express-keepalive']); 559 | } 560 | 561 | if (target === 'debug') { 562 | return grunt.task.run([ 563 | 'clean:server', 564 | 'env:all', 565 | 'injector:sass', 566 | 'concurrent:server', 567 | 'injector', 568 | 'wiredep', 569 | 'autoprefixer', 570 | 'concurrent:debug' 571 | ]); 572 | } 573 | 574 | grunt.task.run([ 575 | 'clean:server', 576 | 'env:all', 577 | 'injector:sass', 578 | 'concurrent:server', 579 | 'injector', 580 | 'wiredep', 581 | 'autoprefixer', 582 | 'express:dev', 583 | 'wait', 584 | 'open', 585 | 'watch' 586 | ]); 587 | }); 588 | 589 | grunt.registerTask('server', function () { 590 | grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.'); 591 | grunt.task.run(['serve']); 592 | }); 593 | 594 | grunt.registerTask('test', function(target) { 595 | if (target === 'server') { 596 | return grunt.task.run([ 597 | 'env:all', 598 | 'env:test' 599 | ]); 600 | } 601 | 602 | else if (target === 'client') { 603 | return grunt.task.run([ 604 | 'clean:server', 605 | 'env:all', 606 | 'injector:sass', 607 | 'concurrent:test', 608 | 'injector', 609 | 'autoprefixer', 610 | 'karma' 611 | ]); 612 | } 613 | 614 | else if (target === 'e2e') { 615 | return grunt.task.run([ 616 | 'clean:server', 617 | 'env:all', 618 | 'env:test', 619 | 'injector:sass', 620 | 'concurrent:test', 621 | 'injector', 622 | 'wiredep', 623 | 'autoprefixer', 624 | 'express:dev', 625 | 'protractor' 626 | ]); 627 | } 628 | 629 | else grunt.task.run([ 630 | 'test:server', 631 | 'test:client' 632 | ]); 633 | }); 634 | 635 | grunt.registerTask('build', [ 636 | 'clean:dist', 637 | 'injector:sass', 638 | 'concurrent:dist', 639 | 'injector', 640 | 'wiredep', 641 | 'useminPrepare', 642 | 'autoprefixer', 643 | 'ngtemplates', 644 | 'concat', 645 | 'ngAnnotate', 646 | 'copy:dist', 647 | 'cdnify', 648 | 'cssmin', 649 | 'uglify', 650 | 'rev', 651 | 'usemin' 652 | ]); 653 | 654 | grunt.registerTask('default', [ 655 | 'newer:jshint', 656 | 'test', 657 | 'build' 658 | ]); 659 | }; 660 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular-js-seed — ready to start application for Angularjs 2 | 3 | This project is based on angular 1.x concept and wrap around usefull utility for angular application development. 4 | 5 | The seed contains a sample AngularJS application and is pre-configured to install the Angular 6 | framework and a bunch of development and testing tools for instant web development. 7 | 8 | 9 | 10 | ## Getting Started 11 | 12 | You can clone repository [angular-js-seed](https://github.com/princesoni1989/angular-js-seed) and install dependency. 13 | 14 | ### Prerequisites 15 | We have number of server and build tools preconfigured in application, to use them you need to pre-install below dependency 16 | * [Node](https://nodejs.org/en/download/) 17 | * [Sass](http://sass-lang.com/install) 18 | * [Grunt](http://gruntjs.com/installing-grunt) 19 | * [Bower](http://bower.io/) 20 | 21 | ### Clone angular-js-seed 22 | 23 | Clone the angular-seed repository using [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git): 24 | 25 | ``` 26 | git https://github.com/princesoni1989/angular-js-seed 27 | cd angular-js-seed 28 | ``` 29 | 30 | 31 | ### Install Dependencies 32 | 33 | We have two kinds of dependencies in this project: tools and angular framework code. The tools help 34 | us manage and test the application. 35 | 36 | ``` 37 | bower install 38 | 39 | npm install 40 | ``` 41 | 42 | You should find that you have two new 43 | folders in your project. 44 | 45 | * `node_modules` - contains the npm packages for the tools we need 46 | * `client/bower_components` - contains the angular framework files 47 | 48 | 49 | ### Run the Application 50 | 51 | We have preconfigured the project with a simple development web server. The simplest way to start 52 | this server is: 53 | 54 | ``` 55 | grunt serve 56 | ``` 57 | 58 | Now browse to the app at `http://localhost:9000`.This will start server in development mode and enable you do fast development. you can make changes and it will deploy automatically. 59 | 60 | You can also use: 61 | ``` 62 | npm start 63 | ``` 64 | 65 | 66 | 67 | ## Pre configured application 68 | 69 | This app comes with preconfigured configuration. You can find below listed functionality already imaplemented - 70 | * login 71 | * logout 72 | * signup 73 | * forgot password 74 | * change password 75 | * home screen 76 | * pop up management(Modal) 77 | * loader service 78 | * constants 79 | * configuration 80 | * server 81 | * build management 82 | * Initial test cases 83 | * test coverage and configuration 84 | 85 | 86 | 87 | ## Directory Layout 88 | ![](https://cloud.githubusercontent.com/assets/10917279/15777703/09eba91e-29af-11e6-8bda-c7c3e93492cc.png) 89 | 90 | ![](https://cloud.githubusercontent.com/assets/10917279/15777710/11128ad2-29af-11e6-8e66-c242d065e966.png) 91 | 92 | 93 | ## Testing 94 | 95 | There are two kinds of tests in the angular-seed application: Unit tests and End to End tests. 96 | 97 | ### Running Unit Tests 98 | 99 | The angular-seed app comes preconfigured with unit tests. These are written in 100 | Jasmine, which we run with the Karma Test Runner. We provide a Karma 101 | configuration file to run them. 102 | 103 | * Configuration is found at `karma.conf.js` 104 | * Test an be dound under client/test 105 | 106 | 107 | The easiest way to run the unit tests is to use the supplied grunt configuration: 108 | 109 | ``` 110 | grunt test or grunt test:client 111 | ``` 112 | 113 | This script will start the Karma test runner to execute the unit tests. Moreover, Karma will sit and 114 | watch the source and test files for changes and then re-run the tests whenever any of them change. 115 | This is the recommended strategy; if your unit tests are being run every time you save a file then 116 | you receive instant feedback on any changes that break the expected code functionality. 117 | 118 | We have used karma code coverage tool preconfigured which will give percentage of code coverage.It will generate new folder having all coverage information 119 | 120 | ``` 121 | /coverage 122 | ``` 123 | 124 | 125 | 126 | ### End to end testing 127 | 128 | The angular-js-seed app comes with end-to-end tests, again written in Jasmine. These tests 129 | are run with the Protractor End-to-End test runner. It uses native events and has 130 | special features for Angular applications. 131 | 132 | * the configuration is found at `protractor-conf.js` 133 | * the end-to-end tests are found in `/e2e` 134 | 135 | Protractor simulates interaction with our web app and verifies that the application responds 136 | correctly. Therefore, our web server needs to be serving up the application, so that Protractor 137 | can interact with it. 138 | 139 | ## Updating Angular 140 | 141 | Angular framework library code and tools are acquired through package managers (npm and 142 | bower) you can use these tools instead to update the dependencies. 143 | 144 | You can update the tool dependencies by running: 145 | 146 | ``` 147 | npm update 148 | ``` 149 | 150 | This will find the latest versions that match the version ranges specified in the `package.json` file. 151 | 152 | You can update the Angular dependencies by running: 153 | 154 | ``` 155 | bower update 156 | ``` 157 | 158 | This will find the latest versions that match the version ranges specified in the `bower.json` file. 159 | 160 | 161 | ## Serving the Application Files 162 | 163 | ``` 164 | grunt serve 165 | ``` 166 | 167 | if you want to create build and move to production - 168 | 169 | ``` 170 | grunt clean 171 | grunt build 172 | cd dist 173 | npm start 174 | ``` 175 | This will minify all the file and will create production ready build. 176 | 177 | 178 | 179 | ## Continuous Integration 180 | 181 | ### Travis CI 182 | 183 | Travis CI is a continuous integration service, which can monitor GitHub for new commits 184 | to your repository and execute scripts such as building the app or running tests. The angular-seed 185 | project contains a Travis configuration file, `.travis.yml`, which will cause Travis to run your 186 | tests when you push to GitHub. 187 | 188 | You will need to enable the integration between Travis and GitHub. See the Travis website for more 189 | instruction on how to do this. 190 | 191 | ### Initial structure of this project is based on angular-fullstack generator. 192 | 193 | ## Contact 194 | 195 | For more information on AngularJS please check out http://angularjs.org/ 196 | 197 | [git](http://git-scm.com/)
198 | [bower](http://bower.io)
199 | [npm](https://www.npmjs.org/)
200 | [node](http://nodejs.org)
201 | [protractor](https://github.com/angular/protractor)
202 | [jasmine](http://jasmine.github.io)
203 | [karma](http://karma-runner.github.io)
204 | [travis](https://travis-ci.org/)
205 | 206 | 207 | ## LICENSE 208 | 209 | angular-js-seed is released under the ISC License Copyright (c) 2016 Prince Soni 210 | 211 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 212 | 213 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 214 | 215 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 216 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-js-seed", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "angular": ">=1.3.*", 6 | "json3": "~3.3.1", 7 | "es5-shim": "~3.0.1", 8 | "bootstrap-sass-official": "~3.1.1", 9 | "bootstrap": "~3.1.1", 10 | "angular-resource": ">=1.2.*", 11 | "angular-cookies": ">=1.2.*", 12 | "angular-sanitize": ">=1.2.*", 13 | "angular-bootstrap": "~0.11.0", 14 | "font-awesome": ">=4.1.0", 15 | "lodash": "~2.4.1", 16 | "angular-ui-router": "~0.2.15", 17 | "angular-route": "^1.5.6" 18 | }, 19 | "devDependencies": { 20 | "angular-mocks": ">=1.2.*", 21 | "angular-scenario": ">=1.2.*" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /client/.htaccess: -------------------------------------------------------------------------------- 1 | # Apache Configuration File 2 | 3 | # (!) Using `.htaccess` files slows down Apache, therefore, if you have access 4 | # to the main server config file (usually called `httpd.conf`), you should add 5 | # this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html. 6 | 7 | # ############################################################################## 8 | # # CROSS-ORIGIN RESOURCE SHARING (CORS) # 9 | # ############################################################################## 10 | 11 | # ------------------------------------------------------------------------------ 12 | # | Cross-domain AJAX requests | 13 | # ------------------------------------------------------------------------------ 14 | 15 | # Enable cross-origin AJAX requests. 16 | # http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity 17 | # http://enable-cors.org/ 18 | 19 | # 20 | # Header set Access-Control-Allow-Origin "*" 21 | # 22 | 23 | # ------------------------------------------------------------------------------ 24 | # | CORS-enabled images | 25 | # ------------------------------------------------------------------------------ 26 | 27 | # Send the CORS header for images when browsers request it. 28 | # https://developer.mozilla.org/en/CORS_Enabled_Image 29 | # http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html 30 | # http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/ 31 | 32 | 33 | 34 | 35 | SetEnvIf Origin ":" IS_CORS 36 | Header set Access-Control-Allow-Origin "*" env=IS_CORS 37 | 38 | 39 | 40 | 41 | # ------------------------------------------------------------------------------ 42 | # | Web fonts access | 43 | # ------------------------------------------------------------------------------ 44 | 45 | # Allow access from all domains for web fonts 46 | 47 | 48 | 49 | Header set Access-Control-Allow-Origin "*" 50 | 51 | 52 | 53 | 54 | # ############################################################################## 55 | # # ERRORS # 56 | # ############################################################################## 57 | 58 | # ------------------------------------------------------------------------------ 59 | # | 404 error prevention for non-existing redirected folders | 60 | # ------------------------------------------------------------------------------ 61 | 62 | # Prevent Apache from returning a 404 error for a rewrite if a directory 63 | # with the same name does not exist. 64 | # http://httpd.apache.org/docs/current/content-negotiation.html#multiviews 65 | # http://www.webmasterworld.com/apache/3808792.htm 66 | 67 | Options -MultiViews 68 | 69 | # ------------------------------------------------------------------------------ 70 | # | Custom error messages / pages | 71 | # ------------------------------------------------------------------------------ 72 | 73 | # You can customize what Apache returns to the client in case of an error (see 74 | # http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.: 75 | 76 | ErrorDocument 404 /404.html 77 | 78 | 79 | # ############################################################################## 80 | # # INTERNET EXPLORER # 81 | # ############################################################################## 82 | 83 | # ------------------------------------------------------------------------------ 84 | # | Better website experience | 85 | # ------------------------------------------------------------------------------ 86 | 87 | # Force IE to render pages in the highest available mode in the various 88 | # cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf. 89 | 90 | 91 | Header set X-UA-Compatible "IE=edge" 92 | # `mod_headers` can't match based on the content-type, however, we only 93 | # want to send this header for HTML pages and not for the other resources 94 | 95 | Header unset X-UA-Compatible 96 | 97 | 98 | 99 | # ------------------------------------------------------------------------------ 100 | # | Cookie setting from iframes | 101 | # ------------------------------------------------------------------------------ 102 | 103 | # Allow cookies to be set from iframes in IE. 104 | 105 | # 106 | # Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" 107 | # 108 | 109 | # ------------------------------------------------------------------------------ 110 | # | Screen flicker | 111 | # ------------------------------------------------------------------------------ 112 | 113 | # Stop screen flicker in IE on CSS rollovers (this only works in 114 | # combination with the `ExpiresByType` directives for images from below). 115 | 116 | # BrowserMatch "MSIE" brokenvary=1 117 | # BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1 118 | # BrowserMatch "Opera" !brokenvary 119 | # SetEnvIf brokenvary 1 force-no-vary 120 | 121 | 122 | # ############################################################################## 123 | # # MIME TYPES AND ENCODING # 124 | # ############################################################################## 125 | 126 | # ------------------------------------------------------------------------------ 127 | # | Proper MIME types for all files | 128 | # ------------------------------------------------------------------------------ 129 | 130 | 131 | 132 | # Audio 133 | AddType audio/mp4 m4a f4a f4b 134 | AddType audio/ogg oga ogg 135 | 136 | # JavaScript 137 | # Normalize to standard type (it's sniffed in IE anyways): 138 | # http://tools.ietf.org/html/rfc4329#section-7.2 139 | AddType application/javascript js jsonp 140 | AddType application/json json 141 | 142 | # Video 143 | AddType video/mp4 mp4 m4v f4v f4p 144 | AddType video/ogg ogv 145 | AddType video/webm webm 146 | AddType video/x-flv flv 147 | 148 | # Web fonts 149 | AddType application/font-woff woff 150 | AddType application/vnd.ms-fontobject eot 151 | 152 | # Browsers usually ignore the font MIME types and sniff the content, 153 | # however, Chrome shows a warning if other MIME types are used for the 154 | # following fonts. 155 | AddType application/x-font-ttf ttc ttf 156 | AddType font/opentype otf 157 | 158 | # Make SVGZ fonts work on iPad: 159 | # https://twitter.com/FontSquirrel/status/14855840545 160 | AddType image/svg+xml svg svgz 161 | AddEncoding gzip svgz 162 | 163 | # Other 164 | AddType application/octet-stream safariextz 165 | AddType application/x-chrome-extension crx 166 | AddType application/x-opera-extension oex 167 | AddType application/x-shockwave-flash swf 168 | AddType application/x-web-app-manifest+json webapp 169 | AddType application/x-xpinstall xpi 170 | AddType application/xml atom rdf rss xml 171 | AddType image/webp webp 172 | AddType image/x-icon ico 173 | AddType text/cache-manifest appcache manifest 174 | AddType text/vtt vtt 175 | AddType text/x-component htc 176 | AddType text/x-vcard vcf 177 | 178 | 179 | 180 | # ------------------------------------------------------------------------------ 181 | # | UTF-8 encoding | 182 | # ------------------------------------------------------------------------------ 183 | 184 | # Use UTF-8 encoding for anything served as `text/html` or `text/plain`. 185 | AddDefaultCharset utf-8 186 | 187 | # Force UTF-8 for certain file formats. 188 | 189 | AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml 190 | 191 | 192 | 193 | # ############################################################################## 194 | # # URL REWRITES # 195 | # ############################################################################## 196 | 197 | # ------------------------------------------------------------------------------ 198 | # | Rewrite engine | 199 | # ------------------------------------------------------------------------------ 200 | 201 | # Turning on the rewrite engine and enabling the `FollowSymLinks` option is 202 | # necessary for the following directives to work. 203 | 204 | # If your web host doesn't allow the `FollowSymlinks` option, you may need to 205 | # comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the 206 | # performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks 207 | 208 | # Also, some cloud hosting services require `RewriteBase` to be set: 209 | # http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site 210 | 211 | 212 | Options +FollowSymlinks 213 | # Options +SymLinksIfOwnerMatch 214 | RewriteEngine On 215 | # RewriteBase / 216 | 217 | 218 | # ------------------------------------------------------------------------------ 219 | # | Suppressing / Forcing the "www." at the beginning of URLs | 220 | # ------------------------------------------------------------------------------ 221 | 222 | # The same content should never be available under two different URLs especially 223 | # not with and without "www." at the beginning. This can cause SEO problems 224 | # (duplicate content), therefore, you should choose one of the alternatives and 225 | # redirect the other one. 226 | 227 | # By default option 1 (no "www.") is activated: 228 | # http://no-www.org/faq.php?q=class_b 229 | 230 | # If you'd prefer to use option 2, just comment out all the lines from option 1 231 | # and uncomment the ones from option 2. 232 | 233 | # IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME! 234 | 235 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 236 | 237 | # Option 1: rewrite www.example.com → example.com 238 | 239 | 240 | RewriteCond %{HTTPS} !=on 241 | RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] 242 | RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] 243 | 244 | 245 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 246 | 247 | # Option 2: rewrite example.com → www.example.com 248 | 249 | # Be aware that the following might not be a good idea if you use "real" 250 | # subdomains for certain parts of your website. 251 | 252 | # 253 | # RewriteCond %{HTTPS} !=on 254 | # RewriteCond %{HTTP_HOST} !^www\..+$ [NC] 255 | # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] 256 | # 257 | 258 | 259 | # ############################################################################## 260 | # # SECURITY # 261 | # ############################################################################## 262 | 263 | # ------------------------------------------------------------------------------ 264 | # | Content Security Policy (CSP) | 265 | # ------------------------------------------------------------------------------ 266 | 267 | # You can mitigate the risk of cross-site scripting and other content-injection 268 | # attacks by setting a Content Security Policy which whitelists trusted sources 269 | # of content for your site. 270 | 271 | # The example header below allows ONLY scripts that are loaded from the current 272 | # site's origin (no inline scripts, no CDN, etc). This almost certainly won't 273 | # work as-is for your site! 274 | 275 | # To get all the details you'll need to craft a reasonable policy for your site, 276 | # read: http://html5rocks.com/en/tutorials/security/content-security-policy (or 277 | # see the specification: http://w3.org/TR/CSP). 278 | 279 | # 280 | # Header set Content-Security-Policy "script-src 'self'; object-src 'self'" 281 | # 282 | # Header unset Content-Security-Policy 283 | # 284 | # 285 | 286 | # ------------------------------------------------------------------------------ 287 | # | File access | 288 | # ------------------------------------------------------------------------------ 289 | 290 | # Block access to directories without a default document. 291 | # Usually you should leave this uncommented because you shouldn't allow anyone 292 | # to surf through every directory on your server (which may includes rather 293 | # private places like the CMS's directories). 294 | 295 | 296 | Options -Indexes 297 | 298 | 299 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 300 | 301 | # Block access to hidden files and directories. 302 | # This includes directories used by version control systems such as Git and SVN. 303 | 304 | 305 | RewriteCond %{SCRIPT_FILENAME} -d [OR] 306 | RewriteCond %{SCRIPT_FILENAME} -f 307 | RewriteRule "(^|/)\." - [F] 308 | 309 | 310 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 311 | 312 | # Block access to backup and source files. 313 | # These files may be left by some text editors and can pose a great security 314 | # danger when anyone has access to them. 315 | 316 | 317 | Order allow,deny 318 | Deny from all 319 | Satisfy All 320 | 321 | 322 | # ------------------------------------------------------------------------------ 323 | # | Secure Sockets Layer (SSL) | 324 | # ------------------------------------------------------------------------------ 325 | 326 | # Rewrite secure requests properly to prevent SSL certificate warnings, e.g.: 327 | # prevent `https://www.example.com` when your certificate only allows 328 | # `https://secure.example.com`. 329 | 330 | # 331 | # RewriteCond %{SERVER_PORT} !^443 332 | # RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L] 333 | # 334 | 335 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 336 | 337 | # Force client-side SSL redirection. 338 | 339 | # If a user types "example.com" in his browser, the above rule will redirect him 340 | # to the secure version of the site. That still leaves a window of opportunity 341 | # (the initial HTTP connection) for an attacker to downgrade or redirect the 342 | # request. The following header ensures that browser will ONLY connect to your 343 | # server via HTTPS, regardless of what the users type in the address bar. 344 | # http://www.html5rocks.com/en/tutorials/security/transport-layer-security/ 345 | 346 | # 347 | # Header set Strict-Transport-Security max-age=16070400; 348 | # 349 | 350 | # ------------------------------------------------------------------------------ 351 | # | Server software information | 352 | # ------------------------------------------------------------------------------ 353 | 354 | # Avoid displaying the exact Apache version number, the description of the 355 | # generic OS-type and the information about Apache's compiled-in modules. 356 | 357 | # ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`! 358 | 359 | # ServerTokens Prod 360 | 361 | 362 | # ############################################################################## 363 | # # WEB PERFORMANCE # 364 | # ############################################################################## 365 | 366 | # ------------------------------------------------------------------------------ 367 | # | Compression | 368 | # ------------------------------------------------------------------------------ 369 | 370 | 371 | 372 | # Force compression for mangled headers. 373 | # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping 374 | 375 | 376 | SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding 377 | RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding 378 | 379 | 380 | 381 | # Compress all output labeled with one of the following MIME-types 382 | # (for Apache versions below 2.3.7, you don't need to enable `mod_filter` 383 | # and can remove the `` and `` lines 384 | # as `AddOutputFilterByType` is still in the core directives). 385 | 386 | AddOutputFilterByType DEFLATE application/atom+xml \ 387 | application/javascript \ 388 | application/json \ 389 | application/rss+xml \ 390 | application/vnd.ms-fontobject \ 391 | application/x-font-ttf \ 392 | application/x-web-app-manifest+json \ 393 | application/xhtml+xml \ 394 | application/xml \ 395 | font/opentype \ 396 | image/svg+xml \ 397 | image/x-icon \ 398 | text/css \ 399 | text/html \ 400 | text/plain \ 401 | text/x-component \ 402 | text/xml 403 | 404 | 405 | 406 | 407 | # ------------------------------------------------------------------------------ 408 | # | Content transformations | 409 | # ------------------------------------------------------------------------------ 410 | 411 | # Prevent some of the mobile network providers from modifying the content of 412 | # your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5. 413 | 414 | # 415 | # Header set Cache-Control "no-transform" 416 | # 417 | 418 | # ------------------------------------------------------------------------------ 419 | # | ETag removal | 420 | # ------------------------------------------------------------------------------ 421 | 422 | # Since we're sending far-future expires headers (see below), ETags can 423 | # be removed: http://developer.yahoo.com/performance/rules.html#etags. 424 | 425 | # `FileETag None` is not enough for every server. 426 | 427 | Header unset ETag 428 | 429 | 430 | FileETag None 431 | 432 | # ------------------------------------------------------------------------------ 433 | # | Expires headers (for better cache control) | 434 | # ------------------------------------------------------------------------------ 435 | 436 | # The following expires headers are set pretty far in the future. If you don't 437 | # control versioning with filename-based cache busting, consider lowering the 438 | # cache time for resources like CSS and JS to something like 1 week. 439 | 440 | 441 | 442 | ExpiresActive on 443 | ExpiresDefault "access plus 1 month" 444 | 445 | # CSS 446 | ExpiresByType text/css "access plus 1 year" 447 | 448 | # Data interchange 449 | ExpiresByType application/json "access plus 0 seconds" 450 | ExpiresByType application/xml "access plus 0 seconds" 451 | ExpiresByType text/xml "access plus 0 seconds" 452 | 453 | # Favicon (cannot be renamed!) 454 | ExpiresByType image/x-icon "access plus 1 week" 455 | 456 | # HTML components (HTCs) 457 | ExpiresByType text/x-component "access plus 1 month" 458 | 459 | # HTML 460 | ExpiresByType text/html "access plus 0 seconds" 461 | 462 | # JavaScript 463 | ExpiresByType application/javascript "access plus 1 year" 464 | 465 | # Manifest files 466 | ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds" 467 | ExpiresByType text/cache-manifest "access plus 0 seconds" 468 | 469 | # Media 470 | ExpiresByType audio/ogg "access plus 1 month" 471 | ExpiresByType image/gif "access plus 1 month" 472 | ExpiresByType image/jpeg "access plus 1 month" 473 | ExpiresByType image/png "access plus 1 month" 474 | ExpiresByType video/mp4 "access plus 1 month" 475 | ExpiresByType video/ogg "access plus 1 month" 476 | ExpiresByType video/webm "access plus 1 month" 477 | 478 | # Web feeds 479 | ExpiresByType application/atom+xml "access plus 1 hour" 480 | ExpiresByType application/rss+xml "access plus 1 hour" 481 | 482 | # Web fonts 483 | ExpiresByType application/font-woff "access plus 1 month" 484 | ExpiresByType application/vnd.ms-fontobject "access plus 1 month" 485 | ExpiresByType application/x-font-ttf "access plus 1 month" 486 | ExpiresByType font/opentype "access plus 1 month" 487 | ExpiresByType image/svg+xml "access plus 1 month" 488 | 489 | 490 | 491 | # ------------------------------------------------------------------------------ 492 | # | Filename-based cache busting | 493 | # ------------------------------------------------------------------------------ 494 | 495 | # If you're not using a build process to manage your filename version revving, 496 | # you might want to consider enabling the following directives to route all 497 | # requests such as `/css/style.12345.css` to `/css/style.css`. 498 | 499 | # To understand why this is important and a better idea than `*.css?v231`, read: 500 | # http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring 501 | 502 | # 503 | # RewriteCond %{REQUEST_FILENAME} !-f 504 | # RewriteCond %{REQUEST_FILENAME} !-d 505 | # RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L] 506 | # 507 | 508 | # ------------------------------------------------------------------------------ 509 | # | File concatenation | 510 | # ------------------------------------------------------------------------------ 511 | 512 | # Allow concatenation from within specific CSS and JS files, e.g.: 513 | # Inside of `script.combined.js` you could have 514 | # 515 | # 516 | # and they would be included into this single file. 517 | 518 | # 519 | # 520 | # Options +Includes 521 | # AddOutputFilterByType INCLUDES application/javascript application/json 522 | # SetOutputFilter INCLUDES 523 | # 524 | # 525 | # Options +Includes 526 | # AddOutputFilterByType INCLUDES text/css 527 | # SetOutputFilter INCLUDES 528 | # 529 | # 530 | 531 | # ------------------------------------------------------------------------------ 532 | # | Persistent connections | 533 | # ------------------------------------------------------------------------------ 534 | 535 | # Allow multiple requests to be sent over the same TCP connection: 536 | # http://httpd.apache.org/docs/current/en/mod/core.html#keepalive. 537 | 538 | # Enable if you serve a lot of static content but, be aware of the 539 | # possible disadvantages! 540 | 541 | # 542 | # Header set Connection Keep-Alive 543 | # 544 | -------------------------------------------------------------------------------- /client/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "latedef": true, 11 | "newcap": true, 12 | "noarg": true, 13 | "quotmark": "single", 14 | "undef": true, 15 | "unused": true, 16 | "strict": true, 17 | "trailing": true, 18 | "smarttabs": true, 19 | "globals": { 20 | "jQuery": true, 21 | "angular": true, 22 | "console": true, 23 | "$": true, 24 | "_": true, 25 | "moment": true, 26 | "describe": true, 27 | "beforeEach": true, 28 | "module": true, 29 | "inject": true, 30 | "it": true, 31 | "expect": true, 32 | "browser": true, 33 | "element": true, 34 | "by": true 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /client/app/account/account.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | angular.module('angularJsSeedApp') 5 | .config(function configuration($stateProvider) { 6 | $stateProvider 7 | .state('login', { 8 | url: '/login', 9 | templateUrl: 'app/account/login/login.html', 10 | controller: 'LoginCtrl', 11 | controllerAs: 'Login' 12 | }) 13 | .state('logout', { 14 | url: '/logout?referrer', 15 | referrer: 'login', 16 | template: '', 17 | controller: function($state, Auth) { 18 | var referrer = $state.params.referrer || 'login'; 19 | Auth.logout(); 20 | $state.go(referrer); 21 | } 22 | }) 23 | .state('signUp', { 24 | url: '/signup', 25 | templateUrl: 'app/account/signUp/signUp.html', 26 | controller: 'SignUpCtrl', 27 | controllerAs: 'SignUp' 28 | }) 29 | .state('forgotPassword', { 30 | url: '/forgotpassword', 31 | templateUrl: 'app/account/forgotPassword/forgotPassword.html', 32 | controller: 'ForgotPasswordCtrl', 33 | controllerAs: 'ForgotPassword' 34 | }) 35 | .state('changePassword', { 36 | url: '/changepassword', 37 | templateUrl: 'app/account/changePassword/changePassword.html', 38 | controller: 'ChangePasswordCtrl', 39 | controllerAs: 'ChangePassword', 40 | authenticate: true 41 | }); 42 | }) 43 | .run(function($rootScope) { 44 | $rootScope.$on('$stateChangeStart', function(event, next, nextParams, current) { 45 | if (next.name === 'logout' && current && current.name && !current.authenticate) { 46 | next.referrer = current.name; 47 | } 48 | }); 49 | }); 50 | 51 | })(angular) 52 | -------------------------------------------------------------------------------- /client/app/account/changePassword/changePassword.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | 5 | function changePassword(Auth) { 6 | 7 | var ChangePassword = this; 8 | ChangePassword.errors = {}; 9 | 10 | ChangePassword.reset = function (form) { 11 | ChangePassword.submitted = true; 12 | if (form.$valid) { 13 | Auth.changePassword(ChangePassword.user.oldPassword, ChangePassword.user.newPassword) 14 | .then(function () { 15 | ChangePassword.message = 'Password successfully changed.'; 16 | }) 17 | .catch(function () { 18 | ChangePassword.errors.other = 'Incorrect password'; 19 | ChangePassword.message = 'Unable to change password, Please check current password'; 20 | }); 21 | } 22 | }; 23 | }; 24 | 25 | angular.module('angularJsSeedApp') 26 | .controller('ChangePasswordCtrl', changePassword) 27 | })(angular); 28 | -------------------------------------------------------------------------------- /client/app/account/changePassword/changePassword.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 | 34 |
35 |
36 |
37 | -------------------------------------------------------------------------------- /client/app/account/forgotPassword/forgotPassword.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | 5 | function forgotPassword(User, $state) { 6 | 7 | /** 8 | * Controller variables 9 | */ 10 | var ForgotPassword = this; 11 | ForgotPassword.sent = false; 12 | ForgotPassword.posting = false; 13 | ForgotPassword.error = ''; 14 | ForgotPassword.form = { 15 | email: '' 16 | }; 17 | ForgotPassword.requestFailed = false; 18 | ForgotPassword.submitted = false; 19 | 20 | /** 21 | * Serves to submit a request for set password for provided user emailId. 22 | * @param form - formObject 23 | */ 24 | ForgotPassword.forgotPassword = function (form) { 25 | ForgotPassword.submitted = true; 26 | if (form.$valid) { 27 | ForgotPassword.posting = true; 28 | ForgotPassword.requestFailed = false; 29 | User.forgotPassword({email: ForgotPassword.form.email}, function (data) { 30 | ForgotPassword.sent = true; 31 | ForgotPassword.goToLogin(); 32 | }, function (err) { 33 | ForgotPassword.posting = false; 34 | ForgotPassword.error = err.code; 35 | ForgotPassword.requestFailed = true; 36 | }) 37 | } 38 | }; 39 | 40 | /** 41 | * navigate to login page 42 | */ 43 | ForgotPassword.goToLogin = function () { 44 | $state.go('login'); 45 | }; 46 | }; 47 | 48 | angular.module('angularJsSeedApp') 49 | .controller('ForgotPasswordCtrl', forgotPassword); 50 | 51 | })(angular); 52 | -------------------------------------------------------------------------------- /client/app/account/forgotPassword/forgotPassword.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 23 | 24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /client/app/account/login/login.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | 5 | function loginCtrl($scope, Auth, $state, $window) { 6 | 7 | /** 8 | * controller variables 9 | */ 10 | var Login = this; 11 | Login.user = {}; 12 | Login.errors = {}; 13 | 14 | /** 15 | *Login user 16 | * @param form 17 | */ 18 | Login.login = function(form) { 19 | Login.submitted = true; 20 | 21 | if (form.$valid) { 22 | Auth.login({ 23 | email: Login.user.email, 24 | password: Login.user.password 25 | }) 26 | .then(function() { 27 | // Logged in, redirect to home 28 | $state.go('home'); 29 | }) 30 | .catch(function(error) { 31 | Login.errors.other = error.message; 32 | }); 33 | } 34 | }; 35 | 36 | } 37 | 38 | angular.module('angularJsSeedApp') 39 | .controller('LoginCtrl',loginCtrl); 40 | })(angular); 41 | -------------------------------------------------------------------------------- /client/app/account/login/login.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 | 44 |
45 |
46 |
47 | -------------------------------------------------------------------------------- /client/app/account/signUp/signUp.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | 5 | function signUpCtrl($scope, Auth, $state, $window) { 6 | 7 | /** 8 | * controller varibles 9 | */ 10 | var SignUp = this; 11 | SignUp.user = {}; 12 | SignUp.errors = {}; 13 | 14 | /** 15 | * Signup user 16 | * @param form 17 | */ 18 | SignUp.register = function (form) { 19 | SignUp.submitted = true; 20 | 21 | if (form.$valid) { 22 | Auth.createUser(SignUp.user) 23 | .then(function () { 24 | // Account created, redirect to home 25 | $state.go('home'); 26 | }) 27 | .catch(function (err) { 28 | err = err.data; 29 | $scope.errors = {}; 30 | 31 | }); 32 | } 33 | }; 34 | 35 | } 36 | 37 | angular.module('angularJsSeedApp') 38 | .controller('SignUpCtrl', signUpCtrl);; 39 | 40 | })(angular) 41 | -------------------------------------------------------------------------------- /client/app/account/signUp/signUp.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 | 65 |
66 |
67 |
68 |
69 | -------------------------------------------------------------------------------- /client/app/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | 5 | angular.module('angularJsSeedApp', [ 6 | 'ngCookies', 7 | 'ngResource', 8 | 'ngSanitize', 9 | 'ui.router', 10 | 'ui.bootstrap' 11 | ]) 12 | .config(function ($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider) { 13 | $urlRouterProvider 14 | .otherwise('/'); 15 | 16 | $locationProvider.html5Mode(true); 17 | $httpProvider.interceptors.push('authInterceptor'); 18 | }) 19 | .run(['$rootScope', '$state', 'Auth', function ($rootScope, $state, Auth) { 20 | // Redirect to login if route requires auth and the user is not logged in 21 | $rootScope.$on('$stateChangeStart', function(event, next) { 22 | Auth.isLoggedInAsync(function(loggedIn) { 23 | //check for user authentication 24 | if (!loggedIn && next.authenticate) { 25 | event.preventDefault(); 26 | $state.go('login'); 27 | } 28 | 29 | //navigate to home if user is already authenticated 30 | if(loggedIn && (next.name === 'login' || next.name === 'landing')){ 31 | event.preventDefault(); 32 | $state.go('home'); 33 | } 34 | 35 | if (next.authenticate && !loggedIn) { 36 | event.preventDefault(); 37 | $state.go('login'); 38 | } 39 | }); 40 | }); 41 | }]); 42 | 43 | })(angular); 44 | -------------------------------------------------------------------------------- /client/app/app.scss: -------------------------------------------------------------------------------- 1 | $icon-font-path: "/bower_components/bootstrap-sass-official/vendor/assets/fonts/bootstrap/"; 2 | $fa-font-path: "/bower_components/font-awesome/fonts"; 3 | 4 | @import 'bootstrap-sass-official/vendor/assets/stylesheets/bootstrap'; 5 | @import 'font-awesome/scss/font-awesome'; 6 | 7 | /** 8 | * App-wide Styles 9 | */ 10 | 11 | /* navbar */ 12 | #nav-main 13 | { 14 | background-color: #2c3e50; 15 | margin-bottom: 0; 16 | border:none; 17 | } 18 | #nav-main a.navbar-brand 19 | { 20 | font-size: 20px; 21 | font-weight: 500; 22 | color: #fff; 23 | } 24 | #nav-main li a 25 | { 26 | text-transform: uppercase; 27 | font-size: 16px; 28 | color: #fff; 29 | font-weight: bold; 30 | &:focus 31 | { 32 | background: transparent; 33 | color:#18bc9c; 34 | } 35 | &:hover 36 | { 37 | background: transparent; 38 | color:#18bc9c; 39 | } 40 | } 41 | #nav-main li.active a 42 | { 43 | background: #18bc9c; 44 | color: #fff; 45 | } 46 | .navbar-nav .admin-welcome 47 | { 48 | color: #18bc9c; 49 | } 50 | /* navbar */ 51 | 52 | /* body */ 53 | .jumbotron.jumbo-main 54 | { 55 | text-align: center; 56 | h1 57 | { 58 | text-align: center; 59 | color: #2c3e50; 60 | text-transform: uppercase; 61 | } 62 | } 63 | /* body */ 64 | 65 | /* Login Form */ 66 | 67 | .login-form 68 | { 69 | border: 1px solid #ddd; 70 | padding: 20px 20px 40px 20px; 71 | margin-top: 50px; 72 | background: #f5f5f5; 73 | h2 74 | { 75 | text-align: center; 76 | font-size: 20px; 77 | } 78 | .btn-login,.btn-register,.btn-danger,.btn-success 79 | { 80 | display: block; 81 | width: 100%; 82 | background-color: #47a447; 83 | border-color: #398439; 84 | color: #fff; 85 | border-radius: 0; 86 | height: 35px; 87 | line-height: 35px; 88 | padding: 0; 89 | margin-top: 15px; 90 | font-size: 14px; 91 | } 92 | .btn-register 93 | { 94 | background-color: #428bca; 95 | border-color: #357ebd; 96 | } 97 | .forgot-password { 98 | margin-top: 8px; 99 | display: block; 100 | } 101 | .btn-danger 102 | { 103 | background-color: #d9534f; 104 | border-color: #d43f3a; 105 | } 106 | } 107 | /* Login Form */ 108 | /* footer */ 109 | footer 110 | { 111 | position: absolute; 112 | bottom: 0; 113 | width: 100%; 114 | height: 60px; 115 | background-color: #233140; 116 | h3 117 | { 118 | margin: 0; 119 | text-align: center; 120 | color: #fff; 121 | font-size: 16px; 122 | line-height: 60px; 123 | } 124 | } 125 | /* footer */ 126 | .browsehappy { 127 | margin: 0.2em 0; 128 | background: #ccc; 129 | color: #000; 130 | padding: 0.2em 0; 131 | } 132 | 133 | // Component styles are injected through grunt 134 | // injector 135 | // endinjector 136 | -------------------------------------------------------------------------------- /client/app/home/home.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | 5 | function homeCtrl(ModalService) { 6 | /** 7 | * Controller variables 8 | */ 9 | var Home = this; 10 | 11 | /** 12 | *Show popup 13 | */ 14 | Home.openPopUp = function (){ 15 | ModalService.successPopup(); 16 | } 17 | }; 18 | 19 | angular.module('angularJsSeedApp') 20 | .controller('HomeCtrl', homeCtrl) 21 | 22 | })(angular) 23 | -------------------------------------------------------------------------------- /client/app/home/home.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Hello, world!

5 | Pop Up » 6 |
7 |
8 | 9 | -------------------------------------------------------------------------------- /client/app/home/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | 5 | angular.module('angularJsSeedApp') 6 | .config(function($stateProvider) { 7 | $stateProvider 8 | .state('home', { 9 | url: '/home', 10 | templateUrl: 'app/home/home.html', 11 | controller: 'HomeCtrl', 12 | controllerAs: 'Home', 13 | authenticate: true 14 | }) 15 | }); 16 | 17 | })(angular) 18 | -------------------------------------------------------------------------------- /client/app/interceptor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function(angular) { 4 | 5 | angular.module('angularJsSeedApp') 6 | .factory('authInterceptor', function ($rootScope, $q, $cookies, $injector) { 7 | var state; 8 | return { 9 | // Add authorization token to headers 10 | request: function(config) { 11 | config.headers = config.headers || {}; 12 | if ($cookies && $cookies.get('token')) { 13 | config.headers.Authorization = 'Bearer ' + JSON.parse($cookies.get('token')); 14 | } 15 | return config; 16 | }, 17 | 18 | // Intercept 401s and redirect you sto login 19 | responseError: function(response) { 20 | if (response.status === 401) { 21 | (state || (state = $injector.get('$state'))).go('login'); 22 | // remove any stale tokens 23 | $cookies.remove('token'); 24 | return $q.reject(response); 25 | } 26 | else { 27 | return $q.reject(response); 28 | } 29 | } 30 | }; 31 | 32 | }) 33 | 34 | })(angular); 35 | -------------------------------------------------------------------------------- /client/app/landing/landing.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | 5 | function landingCtrl(ModalService) { 6 | 7 | /** 8 | * Controller variables 9 | */ 10 | var Landing = this; 11 | 12 | } 13 | 14 | angular.module('angularJsSeedApp') 15 | .controller('LandingCtrl', landingCtrl); 16 | 17 | })(angular) 18 | -------------------------------------------------------------------------------- /client/app/landing/landing.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Hello, world!

5 |
6 |
7 | 8 | -------------------------------------------------------------------------------- /client/app/landing/landing.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | 5 | angular.module('angularJsSeedApp') 6 | .config(function($stateProvider) { 7 | $stateProvider 8 | .state('landing', { 9 | url: '/', 10 | templateUrl: 'app/landing/landing.html', 11 | controller: 'LandingCtrl', 12 | controllerAs: 'landing' 13 | }); 14 | }); 15 | 16 | })(angular) 17 | -------------------------------------------------------------------------------- /client/assets/images/yeoman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princesoni1989/angular-js-seed/2e8338075f6c03f6204dab1a73f357a48d16da5e/client/assets/images/yeoman.png -------------------------------------------------------------------------------- /client/components/api-services/user/user.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | 5 | angular.module('angularJsSeedApp') 6 | .factory('User', ['$resource', 'Config', function ($resource, Config) { 7 | return $resource(Config.getHost() + '/api/users/:id/:controller', { 8 | id: '@_id' 9 | }, 10 | { 11 | get: { 12 | method: 'GET', 13 | params: { 14 | id:'me' 15 | } 16 | }, 17 | forgotPassword: { 18 | method: 'POST', 19 | params: { 20 | id:'forgotPassword' 21 | } 22 | }, 23 | changePassword: { 24 | method: 'PUT', 25 | params: { 26 | controller:'password' 27 | } 28 | } 29 | }); 30 | }]); 31 | 32 | })(angular); 33 | -------------------------------------------------------------------------------- /client/components/auth/auth.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | (function (angular) { 3 | 4 | 5 | function Auth($http, User, $cookieStore, $q, Config) { 6 | var currentUser = {}; 7 | var token = $cookieStore && $cookieStore.get('token'); 8 | if (token) { 9 | currentUser = User.get(); 10 | } 11 | 12 | return { 13 | 14 | /** 15 | * Authenticate user and save token 16 | * 17 | * @param {Object} user - login info 18 | * @param {Function} callback - optional 19 | * @return {Promise} 20 | */ 21 | login: function (user, callback) { 22 | var cb = callback || angular.noop; 23 | var deferred = $q.defer(); 24 | 25 | $http.post(Config.getHost() + '/auth/local', { 26 | email: user.email, 27 | password: user.password 28 | }). 29 | success(function (data) { 30 | $cookieStore.put('token', data.token); 31 | currentUser = User.get(); 32 | deferred.resolve(data); 33 | return cb(); 34 | }). 35 | error(function (err) { 36 | this.logout(); 37 | deferred.reject(err); 38 | return cb(err); 39 | }.bind(this)); 40 | 41 | return deferred.promise; 42 | }, 43 | 44 | /** 45 | * Delete access token and user info 46 | * 47 | * @param {Function} 48 | */ 49 | logout: function () { 50 | $cookieStore.remove('token'); 51 | currentUser = {}; 52 | }, 53 | 54 | /** 55 | * Create a new user 56 | * 57 | * @param {Object} user - user info 58 | * @param {Function} callback - optional 59 | * @return {Promise} 60 | */ 61 | createUser: function (user, callback) { 62 | var cb = callback || angular.noop; 63 | 64 | return User.save(user, 65 | function (data) { 66 | $cookieStore.put('token', data.token); 67 | currentUser = User.get(); 68 | return cb(user); 69 | }, 70 | function (err) { 71 | this.logout(); 72 | return cb(err); 73 | }.bind(this)).$promise; 74 | }, 75 | 76 | /** 77 | * Change password 78 | * 79 | * @param {String} oldPassword 80 | * @param {String} newPassword 81 | * @param {Function} callback - optional 82 | * @return {Promise} 83 | */ 84 | changePassword: function (oldPassword, newPassword, callback) { 85 | var cb = callback || angular.noop; 86 | 87 | return User.changePassword({id: currentUser._id}, { 88 | oldPassword: oldPassword, 89 | newPassword: newPassword 90 | }, function (user) { 91 | return cb(user); 92 | }, function (err) { 93 | return cb(err); 94 | }).$promise; 95 | }, 96 | 97 | /** 98 | * Gets all available info on authenticated user 99 | * 100 | * @return {Object} user 101 | */ 102 | getCurrentUser: function () { 103 | return currentUser; 104 | }, 105 | 106 | /** 107 | * Check if a user is logged in 108 | * 109 | * @return {Boolean} 110 | */ 111 | isLoggedIn: function () { 112 | return currentUser.hasOwnProperty('role'); 113 | }, 114 | 115 | /** 116 | * Waits for currentUser to resolve before checking if user is logged in 117 | */ 118 | isLoggedInAsync: function (cb) { 119 | if (currentUser.hasOwnProperty('$promise')) { 120 | currentUser.$promise.then(function () { 121 | cb(true); 122 | }).catch(function () { 123 | cb(false); 124 | }); 125 | } else if (currentUser.hasOwnProperty('role')) { 126 | cb(true); 127 | } else { 128 | cb(false); 129 | } 130 | }, 131 | 132 | /** 133 | * Check if a user is an admin 134 | * 135 | * @return {Boolean} 136 | */ 137 | isAdmin: function () { 138 | return currentUser.role === 'admin'; 139 | }, 140 | 141 | /** 142 | * Get auth token 143 | */ 144 | getToken: function () { 145 | return $cookieStore.get('token'); 146 | } 147 | }; 148 | }; 149 | 150 | angular.module('angularJsSeedApp') 151 | .factory('Auth', Auth) 152 | })(angular); 153 | -------------------------------------------------------------------------------- /client/components/config/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by princesoni on 11/13/15. 3 | */ 4 | 'use strict'; 5 | 6 | (function (angular) { 7 | //configuration object 8 | function config() { 9 | var configuration = { 10 | ENV: 'DEVELOPMENT', 11 | 12 | ENVIRONMENTS: { 13 | 14 | STAGING: { 15 | URL: 'http://localhost:9000' 16 | }, 17 | 18 | PRODUCTION: { 19 | URL: 'http://localhost:9000' 20 | }, 21 | 22 | DEVELOPMENT: { 23 | URL: 'https://frozen-tundra-93174.herokuapp.com' 24 | } 25 | }, 26 | 27 | getHost: function () { 28 | return this.ENVIRONMENTS[this.ENV] && this.ENVIRONMENTS[this.ENV].URL; 29 | } 30 | 31 | } 32 | return configuration; 33 | } 34 | 35 | //factory declaration 36 | angular.module('angularJsSeedApp') 37 | .factory('Config', config) 38 | 39 | })(angular); 40 | -------------------------------------------------------------------------------- /client/components/constants/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by princesoni on 11/13/15. 3 | */ 4 | 'use strict'; 5 | 6 | (function (angular) { 7 | 8 | function constants() { 9 | 10 | return { 11 | //User roles 12 | roles: { 13 | BUSINESS_USER: 'businessUser', 14 | USER: 'user' 15 | }, 16 | popup: { 17 | SUCCESS: 'SUCCESS', 18 | ERROR: 'ERROR' 19 | } 20 | } 21 | } 22 | 23 | //factory declaration 24 | angular.module('angularJsSeedApp') 25 | .factory('Constants', constants); 26 | 27 | })(angular); 28 | -------------------------------------------------------------------------------- /client/components/footer/footer.directive.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | 5 | angular.module('angularJsSeedApp') 6 | .directive('footer', function () { 7 | return { 8 | templateUrl: 'components/footer/footer.html', 9 | restrict: 'E', 10 | link: function (scope, element) { 11 | element.addClass('footer'); 12 | } 13 | }; 14 | }); 15 | 16 | })(angular); 17 | -------------------------------------------------------------------------------- /client/components/footer/footer.html: -------------------------------------------------------------------------------- 1 |
2 |

Footer

3 |
4 | -------------------------------------------------------------------------------- /client/components/header/header.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | 5 | function header($scope, Auth) { 6 | var Header = this; 7 | Header.menu = [{ 8 | 'title': 'Home', 9 | 'state': 'home' 10 | }]; 11 | 12 | Header.isCollapsed = true; 13 | Header.isLoggedIn = Auth.isLoggedIn; 14 | Header.isAdmin = Auth.isAdmin; 15 | Header.getCurrentUser = Auth.getCurrentUser; 16 | } 17 | 18 | //controller declaration 19 | angular.module('angularJsSeedApp') 20 | .controller('HeaderCtrl', header); 21 | 22 | })(angular) 23 | -------------------------------------------------------------------------------- /client/components/header/header.directive.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | 5 | angular.module('angularJsSeedApp') 6 | .directive('header', function () { 7 | return { 8 | templateUrl: 'components/header/header.html', 9 | restrict: 'E', 10 | controller: 'HeaderCtrl' 11 | }; 12 | }); 13 | 14 | })(angular) 15 | 16 | -------------------------------------------------------------------------------- /client/components/header/header.html: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /client/components/loader/loader.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | //Loader service 5 | function loaderService() { 6 | return { 7 | show: function (ele) { 8 | if (ele) { 9 | angular.element(ele).css('display', 'block'); 10 | } else { 11 | angular.element('.loader').css('display', 'block'); 12 | } 13 | }, 14 | 15 | hide: function (ele) { 16 | if (ele) { 17 | angular.element(ele).css('display', 'none'); 18 | } else { 19 | angular.element('.loader').css('display', 'none'); 20 | } 21 | } 22 | } 23 | }; 24 | 25 | angular.module('angularJsSeedApp') 26 | .factory('LoaderService', loaderService) 27 | 28 | })(angular); 29 | -------------------------------------------------------------------------------- /client/components/messages/messages.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | //Message constants 5 | function Constants() { 6 | return { 7 | api: { 8 | error: 'Error in getting data from server, Please try again later' 9 | } 10 | }; 11 | }; 12 | 13 | angular.module('angularJsSeedApp') 14 | .factory('Messages', Constants) 15 | })(angular) 16 | -------------------------------------------------------------------------------- /client/components/modal/modal.controller.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | (function (angular) { 4 | 5 | function ModalCtrl($modalInstance, $state, modalView, popupData, Constants, Messages, Helper, $timeout) { 6 | /** 7 | * Controller variable 8 | */ 9 | var ctrl = this; 10 | ctrl.view = modalView; 11 | ctrl.popupData = popupData; 12 | 13 | /** 14 | * close modal 15 | * @param reload 16 | */ 17 | ctrl.closeModal = function (reload) { 18 | $modalInstance.dismiss("cancel"); 19 | }; 20 | } 21 | 22 | angular.module('angularJsSeedApp') 23 | .controller('ModalCtrl', ModalCtrl); 24 | })(angular); 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /client/components/modal/modal.css: -------------------------------------------------------------------------------- 1 | .modal-primary .modal-header, 2 | .modal-info .modal-header, 3 | .modal-success .modal-header, 4 | .modal-warning .modal-header, 5 | .modal-danger .modal-header { 6 | color: #fff; 7 | border-radius: 5px 5px 0 0; 8 | } 9 | .modal-primary .modal-header { 10 | background: #428bca; 11 | } 12 | .modal-info .modal-header { 13 | background: #5bc0de; 14 | } 15 | .modal-success .modal-header { 16 | background: #5cb85c; 17 | } 18 | .modal-warning .modal-header { 19 | background: #f0ad4e; 20 | } 21 | .modal-danger .modal-header { 22 | background: #d9534f; 23 | } 24 | -------------------------------------------------------------------------------- /client/components/modal/modal.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | (function (angular) { 3 | /** 4 | * Modal service 5 | * @param $modal 6 | * @param Constants 7 | * @param $timeout 8 | * @param $state 9 | * @returns {{successPopup: Function, errorPopup: Function}} 10 | * @constructor 11 | */ 12 | function ModalService($modal, Constants,$timeout, $state) { 13 | function openModal(success, dismiss, view, data, sizeClass, controller) { 14 | controller = controller && controller + ' as popUp' || ''; 15 | var modalInstance = $modal.open({ 16 | templateUrl: 'components/modal/popUp.html', 17 | controller: controller || 'ModalCtrl as popUp', 18 | windowClass: 'modal-container', 19 | size: sizeClass, 20 | backdrop: "static", 21 | keyboard: false, 22 | resolve: { 23 | modalView: function () { 24 | return view; 25 | }, 26 | popupData: function () { 27 | return data; 28 | } 29 | } 30 | }); 31 | modalInstance.result.then(success, dismiss || angular.noop); 32 | } 33 | 34 | return { 35 | successPopup: function (data, successCallback, dismissCallback) { 36 | openModal(successCallback, dismissCallback, Constants.popup.SUCCESS, data, "md"); 37 | }, 38 | errorPopup: function (data, successCallback, dismissCallback) { 39 | openModal(successCallback, dismissCallback, Constants.popup.ERROR, data, "md"); 40 | } 41 | } 42 | } 43 | 44 | angular.module("angularJsSeedApp") 45 | .factory("ModalService", ModalService); 46 | })(angular); 47 | 48 | -------------------------------------------------------------------------------- /client/components/modal/popUp.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | -------------------------------------------------------------------------------- /client/components/modal/views/error.html: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /client/components/modal/views/success.html: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /client/components/util/helper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by princesoni on 11/13/15. 3 | */ 4 | 'use strict'; 5 | 6 | /** 7 | * Helper service 8 | */ 9 | (function (angular) { 10 | function Helper() { 11 | return { 12 | 13 | /** 14 | * 15 | * @param obj 16 | * @param keys 17 | * @returns {{}} 18 | */ 19 | filterObject: function (obj, keys) { 20 | var result = {}; 21 | for (var type in obj) 22 | if (keys.indexOf(type) > -1) 23 | result[type] = obj[type]; 24 | return result; 25 | }, 26 | 27 | /** 28 | * 29 | * @param text 30 | * @returns {*} 31 | */ 32 | trim: function (text) { 33 | return text.replace(/\s+/, "") 34 | }, 35 | 36 | /** 37 | * 38 | * @param arr 39 | * @returns {{}} 40 | */ 41 | sortByKey: function (arr) { 42 | var ordered = {}; 43 | Object.keys(arr).sort().forEach(function (key) { 44 | ordered[key] = arr[key]; 45 | }); 46 | return ordered 47 | } 48 | } 49 | } 50 | 51 | angular.module('angularJsSeedApp') 52 | .factory('Helper', Helper) 53 | }) 54 | (angular); 55 | -------------------------------------------------------------------------------- /client/favicon.ico: -------------------------------------------------------------------------------- 1 |   �( @   -2Op"=p�Jt��Jt��b���������������������������������������������������b���Jt��Jt��"=p�Op-2O`O�O�O�O�O�O�O� $\�Jt��������������v���v���������������Jt�� $\�O�O�O�O�O�O�O�O`O�O�O�O�O�O�O�O�O�O� ;n�s���>���>���>���>���s��� ;n�O�O�O�O�O�O�O�O�O�O�O`O�O�O�O�O�O�O�O�O�O� $\�]���^n��^n��]��� $\�O�O�O�O�O�O�O�O�O�O�O`O�O�O�O�O�O�O�O�O�O�O�n�*��*��n�O�O�O�O�O�O�O�O�O�O�O�  O�O�O�O�O�O�O�O�O�O�O�5>Y�5>Y�O�O�O�O�O�O�O�O�O�O�O�  -2O�O�O�O�O�O�O�O�O�O�&6e�&6e�O�O�O�O�O�O�O�O�O�O�-25r�4���E��� $\�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O� $\�E���4���5r�5r�E���M���M���v���0\��O�O�O�O�O�O�O� $\� $\�O�O�O�O�O�O�O�0\��v���M���M���E���5r�)��p&��p��&��������������b���Jt��Jt��Jt��0\��#i��.r��.r��#i��0\��Jt��Jt��Jt��b���������������&��p��&��)��p4���&��-���_������������������]���]�������7���p�����������p���7�������]���]�������������������_��-���-���4���qֈp��p��p����������������������p���7���#i��p�����������p���#i��7���p�����������������������p��&��-���qֈ8��(p��p��I���v���v���]���7���n���v���p���#i��]���v���v���]���#i��p���v���n���7���]���v���v���I���-���-���8��(;��`-���M���7���7���7���.r��R��E��R��E��7���7���7���7���E��R��E��R��.r��7���7���7���M���M���;��`���������������������������z��������������������������� 2 | �  ��� 3 | � 9� 9� 9� 9� 9� 9� 9� 9� 4 |  �n�n� 5 |  � 9� 9� 9� 9� 9� 9� 9� 9� 6 | ����*�x*��*��*��*��*��*��*��n�&��#��&��&��n�*��*��*��*��*��*��*��*�x*ݟ*��*��*��*��*��*��!��#��&��#��&��*��!��!��*��*��*��*��*��*��*ݟ*ݿ*��*��*��*��*��*��n�*��*�� 9� 9�*��*���*��*��*��*��*��*��*ݿ*��*��*��*��*��*��*��!��#��&��&��&��*��#��!��*��*��*��*��*��*��*��  ��������I�&��&��&��&��I���������  U��������� 7 |  �n�n� 8 |  ����������-2z����������������������z������������������������ 9 | ����������������������� 10 | ������������������������� 11 | ������������������������-2����������������������U�������������������z5r������������������-25r�U�����������z  ������������������������������?��� -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 |
32 | 33 | 34 | 43 | 44 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /client/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /client/test/account/forgotPassword/forgotPassword.controller.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: ForgotCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('angularJsSeedApp')); 7 | 8 | var ForgotCtrl, scope, User, fakeState, q; 9 | 10 | // Initialize the controller and a mock scope 11 | beforeEach(inject(function ($controller, $rootScope, $q, $httpBackend) { 12 | scope = $rootScope.$new(); 13 | q = $q; 14 | $httpBackend.whenGET('app/user/login/login.html').respond(200); 15 | User = jasmine.createSpyObj('User', ['setPasswordRequest']); 16 | fakeState = { 17 | go: function (url) { 18 | fakeState.redirect = url; 19 | } 20 | }; 21 | ForgotCtrl = $controller('ForgotPasswordCtrl', { 22 | $scope: scope, 23 | $q: q, 24 | User: User, 25 | $state: fakeState 26 | }); 27 | })); 28 | 29 | //This is success 30 | it('should have a ForgotPasswordCtrl controller', function () { 31 | expect('practiceRetrieverApp.ForgotPasswordCtrl').toBeDefined(); 32 | }); 33 | describe('Function:forgotPassword', function () { 34 | var fakeForm = {}; 35 | it('password should be sent successfully when User.setPasswordRequest calls success', function () { 36 | User.setPasswordRequest.andCallFake(function (data, success, failure) { 37 | success({userData: {}}); 38 | }); 39 | ForgotCtrl.form.email = 'lancomTest@cloud.com'; 40 | fakeForm.$valid = true; 41 | ForgotCtrl.forgotPassword(fakeForm); 42 | scope.$apply(); 43 | expect(ForgotCtrl.sent).toBeTruthy(); 44 | }); 45 | it('should show error message and request failed when User.setPasswordRequest promise calls failure ', function () { 46 | User.setPasswordRequest.andCallFake(function (data, success, failure) { 47 | failure({data: 'Email id not correct'}); 48 | }); 49 | ForgotCtrl.form.email = 'lancomTest@cloud.com'; 50 | fakeForm.$valid = true; 51 | ForgotCtrl.forgotPassword(fakeForm); 52 | scope.$apply(); 53 | expect(ForgotCtrl.requestFailed).toBeTruthy(); 54 | expect(ForgotCtrl.sent).toBeFalsy(); 55 | }); 56 | it('should not call User.setPasswordRequest function when submitted form is invalid', function () { 57 | fakeForm.$valid = false; 58 | ForgotCtrl.forgotPassword(fakeForm); 59 | scope.$apply(); 60 | expect(ForgotCtrl.posting).toBeFalsy(); 61 | expect(User.setPasswordRequest).not.toHaveBeenCalled(); 62 | }); 63 | }); 64 | describe('Function:goToLogin', function () { 65 | it('should redirect to login when goToLogin function is called', function () { 66 | ForgotCtrl.goToLogin(); 67 | scope.$apply(); 68 | expect(fakeState.redirect).toEqual('login'); 69 | }); 70 | }); 71 | 72 | }); 73 | -------------------------------------------------------------------------------- /client/test/account/login/login.controller.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: LoginCtrl', function () { 4 | var LoginCtrl, q, rootScope, scope, fakeLocation, Auth, $state, fakeForm; 5 | beforeEach(module('angularJsSeedApp')); 6 | 7 | beforeEach(inject(function ($q, $controller, $rootScope, $httpBackend) { 8 | q = $q, 9 | rootScope = $rootScope, 10 | scope = rootScope.$new(); 11 | $httpBackend.whenGET('app/account/login/login.html') 12 | .respond(200); 13 | Auth = jasmine.createSpyObj('Auth', ['login']); 14 | $state = jasmine.createSpyObj('$state', ['go']); 15 | $state.go.andCallFake(function (url) { 16 | 17 | }); 18 | $state.params = { 19 | loginSuccess: false, 20 | sessionExpired: false 21 | }; 22 | fakeForm = { 23 | '$valid': true 24 | }; 25 | 26 | LoginCtrl = $controller('LoginCtrl', { 27 | $scope: scope, 28 | $q: q, 29 | Auth: Auth, 30 | $state: $state 31 | //$location: fakeLocation, 32 | //$routeParams: routeParams 33 | }); 34 | rootScope.$digest(); 35 | 36 | } 37 | )); 38 | 39 | //This is success 40 | it('should have a LoginCtrl controller', function () { 41 | console.log("LoginCtrl",LoginCtrl) 42 | //expect('angularJsSeedApp.LoginCtrl').toBeDefined(); 43 | }); 44 | 45 | xdescribe('Function:login', function () { 46 | it('should redirect to home Auth.login promise get resolved ', function () { 47 | Auth.login.andCallFake(function (data) { 48 | var deferred = q.defer(); 49 | deferred.resolve({userData: {}}); 50 | //deferred.$promise = deferred.promise; 51 | return deferred.promise; 52 | }); 53 | LoginCtrl.user.email = 'lancomTest@cloud.com'; 54 | LoginCtrl.user.password = 'lancomTest'; 55 | LoginCtrl.login(fakeForm); 56 | scope.$apply(); 57 | expect($state.go).toHaveBeenCalledWith('home'); 58 | }); 59 | it('should show error message when Auth.login promise get rejected with message "notActive"', function () { 60 | Auth.login.andCallFake(function (data) { 61 | var deferred = q.defer(); 62 | deferred.reject({message: 'notActive'}); 63 | return deferred.promise; 64 | }); 65 | LoginCtrl.user.email = 'lancomTest@cloud.com'; 66 | LoginCtrl.user.password = 'lancomTest'; 67 | LoginCtrl.login(fakeForm); 68 | scope.$apply(); 69 | expect(LoginCtrl.notActivated).toEqual(true); 70 | }); 71 | it('should show error message when Auth.login promise get rejected ', function () { 72 | Auth.login.andCallFake(function (data) { 73 | var deferred = q.defer(); 74 | deferred.reject({message: 'Authentication failure'}); 75 | return deferred.promise; 76 | }); 77 | LoginCtrl.user.email = 'lancomTest@cloud.com'; 78 | LoginCtrl.user.password = 'lancomTest'; 79 | LoginCtrl.login(fakeForm); 80 | scope.$apply(); 81 | expect(LoginCtrl.errors.other).toEqual('Authentication failure'); 82 | }); 83 | }); 84 | xdescribe('Function:signUp', function () { 85 | it('should redirect to signUp when signUp is called ', function () { 86 | LoginCtrl.signUp(); 87 | scope.$apply(); 88 | expect($state.go).toHaveBeenCalledWith('signup'); 89 | }); 90 | }); 91 | xdescribe('Function:forgotPassword', function () { 92 | it('should redirect to forgotPassword when forgotPassword is called ', function () { 93 | LoginCtrl.forgotPassword(); 94 | scope.$apply(); 95 | expect($state.go).toHaveBeenCalledWith('forgotPassword'); 96 | }); 97 | }); 98 | }); 99 | -------------------------------------------------------------------------------- /client/test/account/setPassword/setPassword.controller.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: SetPasswordCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('angularJsSeedApp')); 7 | var SetPasswordCtrl, scope, fakeLocation, User, q, $state; 8 | 9 | /* describe('When token is not a string', function () { 10 | // Initialize the controller and a mock scope 11 | beforeEach(inject(function ($controller, $rootScope, $q, $httpBackend) { 12 | scope = $rootScope.$new(); 13 | $httpBackend.whenGET('app/user/login/login.html').respond(200); 14 | fakeLocation = { 15 | search: function () { 16 | return {token: undefined}; 17 | } 18 | }; 19 | q = $q; 20 | User = jasmine.createSpyObj('User', ['setPassword', 'verifyToken']); 21 | User.verifyToken.andCallFake(function (data, success, failure) { 22 | success({email: 'lancomTest@cloud.com'}); 23 | }); 24 | $state = jasmine.createSpyObj('$state', ['go']); 25 | $state.go.andCallFake(function (url, params, options) { 26 | 27 | }); 28 | SetPasswordCtrl = $controller('SetPasswordCtrl', { 29 | $scope: scope, 30 | $location: fakeLocation, 31 | $q: q, 32 | User: User, 33 | $state: $state 34 | }); 35 | })); 36 | });*/ 37 | 38 | describe('When token is a string but verifyToken calls failure', function () { 39 | // Initialize the controller and a mock scope 40 | beforeEach(inject(function ($controller, $rootScope, $q, $httpBackend) { 41 | scope = $rootScope.$new(); 42 | $httpBackend.whenGET('app/user/login/login.html').respond(200); 43 | fakeLocation = { 44 | search: function () { 45 | return {token: 'string'}; 46 | } 47 | }; 48 | q = $q; 49 | User = jasmine.createSpyObj('User', ['setPassword', 'verifyToken']); 50 | User.verifyToken.andCallFake(function (data, success, failure) { 51 | failure({data: {message: 'Invalid token'}}); 52 | }); 53 | $state = jasmine.createSpyObj('$state', ['go']); 54 | $state.go.andCallFake(function (url, params, options) { 55 | 56 | }); 57 | SetPasswordCtrl = $controller('SetPasswordCtrl', { 58 | $scope: scope, 59 | $location: fakeLocation, 60 | $q: q, 61 | User: User, 62 | $state: $state 63 | }); 64 | })); 65 | it('should have a SetPasswordCtrl controller', function () { 66 | expect('angularJsSeedApp.SetPasswordCtrl').toBeDefined(); 67 | }); 68 | }); 69 | 70 | describe('When token is a string but verifyToken calls success', function () { 71 | // Initialize the controller and a mock scope 72 | beforeEach(inject(function ($controller, $rootScope, $q, $httpBackend) { 73 | scope = $rootScope.$new(); 74 | $httpBackend.whenGET('app/user/login/login.html').respond(200); 75 | fakeLocation = { 76 | search: function () { 77 | return {token: 'string'}; 78 | } 79 | }; 80 | q = $q; 81 | User = jasmine.createSpyObj('User', ['setPassword', 'verifyToken']); 82 | User.verifyToken.andCallFake(function (data, success, failure) { 83 | success({email: 'lancomTest@cloud.com'}); 84 | }); 85 | $state = jasmine.createSpyObj('$state', ['go','transitionTo']); 86 | $state.go.andCallFake(function (url, params, options) { 87 | 88 | }); 89 | SetPasswordCtrl = $controller('SetPasswordCtrl', { 90 | $scope: scope, 91 | $location: fakeLocation, 92 | $q: q, 93 | User: User, 94 | $state: $state 95 | }); 96 | $state.transitionTo.andCallFake(function (name, param) { 97 | 98 | }) 99 | })); 100 | it('should have a SetPasswordCtrl controller', function () { 101 | expect('angularJsSeedApp.SetPasswordCtrl').toBeDefined(); 102 | }); 103 | xdescribe('Function:setPassword', function () { 104 | var fakeForm = {}; 105 | it('password should be set successfully when User.setPassword promise get resolved ', function () { 106 | User.setPassword.andCallFake(function (data, success, failure) { 107 | success({userData: {}}); 108 | }); 109 | fakeForm.$valid = true; 110 | SetPasswordCtrl.setPassword(fakeForm); 111 | scope.$apply(); 112 | expect($state.go).toHaveBeenCalled(); 113 | }); 114 | it('should show error when User.setPassword promise get rejected ', function () { 115 | User.setPassword.andCallFake(function (data, success, failure) { 116 | failure({data: {error: 'promise rejected'}}); 117 | }); 118 | fakeForm.$valid = true; 119 | SetPasswordCtrl.setPassword(fakeForm); 120 | scope.$apply(); 121 | expect(SetPasswordCtrl.error).toBe('promise rejected'); 122 | }); 123 | it('should not call User.setPassword function when submitted form is invalid', function () { 124 | fakeForm.$valid = false; 125 | SetPasswordCtrl.setPassword(fakeForm); 126 | scope.$apply(); 127 | expect(SetPasswordCtrl.posting).toBeFalsy(); 128 | expect(User.setPassword).not.toHaveBeenCalled(); 129 | }); 130 | it('should not call User.setPassword function when password and confirmPassword do not match', function () { 131 | fakeForm.$valid = true; 132 | User.setPassword.andCallFake(function (data, success, failure) { 133 | 134 | }); 135 | SetPasswordCtrl.password = 'password'; 136 | SetPasswordCtrl.confirmPassword = 'confirm password'; 137 | SetPasswordCtrl.setPassword(fakeForm); 138 | scope.$apply(); 139 | expect(SetPasswordCtrl.error).toBe('Passwords do not match'); 140 | expect(User.setPassword).not.toHaveBeenCalled(); 141 | }); 142 | }); 143 | describe('Function:goToLogin', function () { 144 | it('should redirect to login when goToLogin function is called', function () { 145 | SetPasswordCtrl.goToLogin(); 146 | scope.$apply(); 147 | expect($state.go).toHaveBeenCalledWith('login'); 148 | }); 149 | }); 150 | }); 151 | }); 152 | -------------------------------------------------------------------------------- /client/test/account/signup/signup.controller.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: SignupCtrl', function () { 4 | 5 | 6 | // load the controller's module 7 | beforeEach(module('angularJsSeedApp')); 8 | 9 | var SignupCtrl, scope, User, q, $location, $window, $httpBackend, fakeLocation; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function (_$controller_, _$rootScope_, _$q_, _$location_, _$window_, _$httpBackend_) { 13 | scope = _$rootScope_.$new(); 14 | fakeLocation = { 15 | path: function (url) { 16 | return url; 17 | } 18 | }; 19 | q = _$q_; 20 | $location = fakeLocation; 21 | $window = _$window_; 22 | $httpBackend = _$httpBackend_; 23 | User = jasmine.createSpyObj('User', ['createWooCommerceUser']); 24 | $httpBackend.expectGET('app/user/login/login.html').respond(200); 25 | $httpBackend.flush(); 26 | SignupCtrl = _$controller_('SignupCtrl', { 27 | $scope: scope, 28 | User: User, 29 | $location: $location, 30 | $window: $window 31 | }); 32 | })); 33 | 34 | //This is success 35 | describe('Should be defined', function () { 36 | it('$location should exist', function () { 37 | expect($location).toBeDefined(); 38 | }); 39 | it('$window should exist', function () { 40 | expect($window).toBeDefined(); 41 | }); 42 | it('User should exist', function () { 43 | expect(User).toBeDefined(); 44 | }); 45 | it('SignupCtrl should exist', function () { 46 | expect(SignupCtrl).toBeDefined(); 47 | }); 48 | }); 49 | 50 | xdescribe('Unit: function scope.loginOauth()', function () { 51 | it('scope.loginOauth should exist and be a function', function () { 52 | expect(scope.loginOauth).toBeDefined(); 53 | expect(typeof scope.loginOauth).toEqual('function'); 54 | }); 55 | 56 | it('Should invoked', function () { 57 | scope.loginOauth('facebook'); 58 | scope.$digest(); 59 | expect($window.location.href).toBeDefined(); 60 | }); 61 | }); 62 | 63 | describe('Unit: function scope.register()', function () { 64 | it('scope.register should exist and be a function', function () { 65 | expect(scope.register).toBeDefined(); 66 | expect(typeof scope.register).toEqual('function'); 67 | }); 68 | 69 | it('Should invoke User.createUser success', function () { 70 | User.createUser.andCallFake(function (obj, success, failure) { 71 | success(); 72 | }); 73 | var fakeForm = {$valid: true}; 74 | scope.user = { 75 | firstName: 'Rahul', 76 | lastName: 'Dev', 77 | email: 'rahul_dev@xyz.com', 78 | totalAccountPurchased: 0, 79 | phoneNumber: 1236547890, 80 | address1: 'Main Road', 81 | address2: 'Near hospital', 82 | city: 'Delhi', 83 | country: {code: 'IND'}, 84 | state: 'Delhi', 85 | zip: 96110085, 86 | practiceName: 'abc', 87 | primaryContactPerson: 'xyz', 88 | practicePrimaryEmail: 'abc@gmail.com', 89 | practiceType: 'yyy', 90 | doctorNames: ['asd', 'fgh'] 91 | }; 92 | 93 | scope.register(fakeForm); 94 | scope.$digest(); 95 | expect($location.path('/login')).toEqual('/login'); 96 | }); 97 | 98 | it('Should invoke User.createUser failure', function () { 99 | User.createUser.andCallFake(function (obj, success, failure) { 100 | failure({data: {errors: []}, message: 'Internal Server Error'}); 101 | }); 102 | var fakeForm = {$valid: true}; 103 | scope.user = { 104 | firstName: 'Rahul', 105 | lastName: 'Dev', 106 | email: 'rahul_dev@xyz.com', 107 | totalAccountPurchased: 0, 108 | phoneNumber: 1236547890, 109 | address1: 'Main Road', 110 | address2: 'Near hospital', 111 | city: 'Delhi', 112 | country: {code: 'IND'}, 113 | state: 'Delhi', 114 | zip: 96110085, 115 | practiceName: 'abc', 116 | primaryContactPerson: 'xyz', 117 | practicePrimaryEmail: 'abc@gmail.com', 118 | practiceType: 'yyy', 119 | doctorNames: ['asd', 'fgh'] 120 | }; 121 | scope.register(fakeForm); 122 | scope.$digest(); 123 | expect(scope.errors).not.toBeUndefined(); 124 | }); 125 | }); 126 | 127 | }); 128 | -------------------------------------------------------------------------------- /client/test/home/home.controller.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: LoginCtrl', function () { 4 | var LoginCtrl, q, rootScope, scope, fakeLocation, Auth, $state, fakeForm; 5 | beforeEach(module('angularJsSeedApp')); 6 | beforeEach(inject(function ($q, $controller, $rootScope, $httpBackend) { 7 | q = $q, 8 | rootScope = $rootScope, 9 | scope = rootScope.$new(); 10 | $httpBackend.whenGET('app/user/login/login.html') 11 | .respond(200); 12 | Auth = jasmine.createSpyObj('Auth', ['login']); 13 | $state = jasmine.createSpyObj('$state', ['go']); 14 | $state.go.andCallFake(function (url) { 15 | 16 | }); 17 | $state.params = { 18 | loginSuccess: false, 19 | sessionExpired: false 20 | }; 21 | fakeForm = { 22 | '$valid': true 23 | }; 24 | 25 | LoginCtrl = $controller('LoginCtrl', { 26 | $scope: scope, 27 | $q: q, 28 | Auth: Auth, 29 | $state: $state 30 | //$location: fakeLocation, 31 | //$routeParams: routeParams 32 | }); 33 | rootScope.$digest(); 34 | } 35 | )); 36 | //This is success 37 | it('should have a LoginCtrl controller', function () { 38 | expect('angularJsSeedApp.LoginCtrl').toBeDefined(); 39 | }); 40 | 41 | describe('Function:login', function () { 42 | it('should redirect to home Auth.login promise get resolved ', function () { 43 | Auth.login.andCallFake(function (data) { 44 | var deferred = q.defer(); 45 | deferred.resolve({userData: {}}); 46 | //deferred.$promise = deferred.promise; 47 | return deferred.promise; 48 | }); 49 | LoginCtrl.user.email = 'lancomTest@cloud.com'; 50 | LoginCtrl.user.password = 'lancomTest'; 51 | LoginCtrl.login(fakeForm); 52 | scope.$apply(); 53 | expect($state.go).toHaveBeenCalledWith('home'); 54 | }); 55 | it('should show error message when Auth.login promise get rejected with message "notActive"', function () { 56 | Auth.login.andCallFake(function (data) { 57 | var deferred = q.defer(); 58 | deferred.reject({message: 'notActive'}); 59 | return deferred.promise; 60 | }); 61 | LoginCtrl.user.email = 'lancomTest@cloud.com'; 62 | LoginCtrl.user.password = 'lancomTest'; 63 | LoginCtrl.login(fakeForm); 64 | scope.$apply(); 65 | expect(LoginCtrl.notActivated).toEqual(true); 66 | }); 67 | it('should show error message when Auth.login promise get rejected ', function () { 68 | Auth.login.andCallFake(function (data) { 69 | var deferred = q.defer(); 70 | deferred.reject({message: 'Authentication failure'}); 71 | return deferred.promise; 72 | }); 73 | LoginCtrl.user.email = 'lancomTest@cloud.com'; 74 | LoginCtrl.user.password = 'lancomTest'; 75 | LoginCtrl.login(fakeForm); 76 | scope.$apply(); 77 | expect(LoginCtrl.errors.other).toEqual('Authentication failure'); 78 | }); 79 | }); 80 | describe('Function:signUp', function () { 81 | it('should redirect to signUp when signUp is called ', function () { 82 | LoginCtrl.signUp(); 83 | scope.$apply(); 84 | expect($state.go).toHaveBeenCalledWith('signup'); 85 | }); 86 | }); 87 | describe('Function:forgotPassword', function () { 88 | it('should redirect to forgotPassword when forgotPassword is called ', function () { 89 | LoginCtrl.forgotPassword(); 90 | scope.$apply(); 91 | expect($state.go).toHaveBeenCalledWith('forgotPassword'); 92 | }); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /client/test/landing/landing.controller.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: LoginCtrl', function () { 4 | var LoginCtrl, q, rootScope, scope, fakeLocation, Auth, $state, fakeForm; 5 | beforeEach(module('angularJsSeedApp')); 6 | beforeEach(inject(function ($q, $controller, $rootScope, $httpBackend) { 7 | q = $q, 8 | rootScope = $rootScope, 9 | scope = rootScope.$new(); 10 | $httpBackend.whenGET('app/user/login/login.html') 11 | .respond(200); 12 | Auth = jasmine.createSpyObj('Auth', ['login']); 13 | $state = jasmine.createSpyObj('$state', ['go']); 14 | $state.go.andCallFake(function (url) { 15 | 16 | }); 17 | $state.params = { 18 | loginSuccess: false, 19 | sessionExpired: false 20 | }; 21 | fakeForm = { 22 | '$valid': true 23 | }; 24 | 25 | LoginCtrl = $controller('LoginCtrl', { 26 | $scope: scope, 27 | $q: q, 28 | Auth: Auth, 29 | $state: $state 30 | //$location: fakeLocation, 31 | //$routeParams: routeParams 32 | }); 33 | rootScope.$digest(); 34 | } 35 | )); 36 | //This is success 37 | it('should have a LoginCtrl controller', function () { 38 | expect('angularJsSeedApp.LoginCtrl').toBeDefined(); 39 | }); 40 | 41 | describe('Function:login', function () { 42 | it('should redirect to home Auth.login promise get resolved ', function () { 43 | Auth.login.andCallFake(function (data) { 44 | var deferred = q.defer(); 45 | deferred.resolve({userData: {}}); 46 | //deferred.$promise = deferred.promise; 47 | return deferred.promise; 48 | }); 49 | LoginCtrl.user.email = 'lancomTest@cloud.com'; 50 | LoginCtrl.user.password = 'lancomTest'; 51 | LoginCtrl.login(fakeForm); 52 | scope.$apply(); 53 | expect($state.go).toHaveBeenCalledWith('home'); 54 | }); 55 | it('should show error message when Auth.login promise get rejected with message "notActive"', function () { 56 | Auth.login.andCallFake(function (data) { 57 | var deferred = q.defer(); 58 | deferred.reject({message: 'notActive'}); 59 | return deferred.promise; 60 | }); 61 | LoginCtrl.user.email = 'lancomTest@cloud.com'; 62 | LoginCtrl.user.password = 'lancomTest'; 63 | LoginCtrl.login(fakeForm); 64 | scope.$apply(); 65 | expect(LoginCtrl.notActivated).toEqual(true); 66 | }); 67 | it('should show error message when Auth.login promise get rejected ', function () { 68 | Auth.login.andCallFake(function (data) { 69 | var deferred = q.defer(); 70 | deferred.reject({message: 'Authentication failure'}); 71 | return deferred.promise; 72 | }); 73 | LoginCtrl.user.email = 'lancomTest@cloud.com'; 74 | LoginCtrl.user.password = 'lancomTest'; 75 | LoginCtrl.login(fakeForm); 76 | scope.$apply(); 77 | expect(LoginCtrl.errors.other).toEqual('Authentication failure'); 78 | }); 79 | }); 80 | describe('Function:signUp', function () { 81 | it('should redirect to signUp when signUp is called ', function () { 82 | LoginCtrl.signUp(); 83 | scope.$apply(); 84 | expect($state.go).toHaveBeenCalledWith('signup'); 85 | }); 86 | }); 87 | describe('Function:forgotPassword', function () { 88 | it('should redirect to forgotPassword when forgotPassword is called ', function () { 89 | LoginCtrl.forgotPassword(); 90 | scope.$apply(); 91 | expect($state.go).toHaveBeenCalledWith('forgotPassword'); 92 | }); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /client/test/mocked.data.js: -------------------------------------------------------------------------------- 1 | angular.module('MockedJson', []) 2 | .value('AccountMocked', { 3 | data: { 4 | _id: 'lancomTest@cloud.com', 5 | groupId: 'groupId', 6 | adminUser: 'active', 7 | activeUsers: 'active', 8 | role: 'appAdmin', 9 | active: true, 10 | users: [{userId: '123', role: 'superAdmin'}], 11 | accounts: [{userId: '123', role: 'superAdmin'}], 12 | total:1 13 | } 14 | } 15 | ) 16 | .value('UserMocked', { 17 | data: { 18 | _id: 'lancomTest@cloud.com', 19 | groupId: 'groupId', 20 | adminUser: 'active', 21 | activeUsers: 'active', 22 | role: 'appAdmin', 23 | active: true, 24 | users: [{userId: '123', role: 'superAdmin'}], 25 | total:1 26 | } 27 | } 28 | ); 29 | 'use strict'; 30 | -------------------------------------------------------------------------------- /e2e/main/main.po.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file uses the Page Object pattern to define the main page for tests 3 | * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ 4 | */ 5 | 6 | 'use strict'; 7 | 8 | var MainPage = function() { 9 | this.heroEl = element(by.css('.hero-unit')); 10 | this.h1El = this.heroEl.element(by.css('h1')); 11 | this.imgEl = this.heroEl.element(by.css('img')); 12 | }; 13 | 14 | module.exports = new MainPage(); 15 | 16 | -------------------------------------------------------------------------------- /e2e/main/main.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Main View', function() { 4 | var page; 5 | 6 | beforeEach(function() { 7 | browser.get('/'); 8 | page = require('./main.po'); 9 | }); 10 | 11 | it('should include jumbotron with correct data', function() { 12 | expect(page.h1El.getText()).toBe('\'Allo, \'Allo!'); 13 | expect(page.imgEl.getAttribute('src')).toMatch(/assets\/images\/yeoman.png$/); 14 | expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.10/config/configuration-file.html 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | // base path, that will be used to resolve files and exclude 7 | basePath: '', 8 | 9 | // testing framework to use (jasmine/mocha/qunit/...) 10 | frameworks: ['jasmine'], 11 | 12 | // list of files / patterns to load in the browser 13 | files: [ 14 | 'client/bower_components/jquery/dist/jquery.js', 15 | 'client/bower_components/angular/angular.js', 16 | 'client/bower_components/angular-mocks/angular-mocks.js', 17 | 'client/bower_components/angular-resource/angular-resource.js', 18 | 'client/bower_components/angular-cookies/angular-cookies.js', 19 | 'client/bower_components/angular-sanitize/angular-sanitize.js', 20 | 'client/bower_components/angular-route/angular-route.js', 21 | 'client/bower_components/angular-bootstrap/ui-bootstrap-tpls.js', 22 | 'client/bower_components/lodash/dist/lodash.compat.js', 23 | 'client/bower_components/angular-ui-router/release/angular-ui-router.js', 24 | 'client/app/app.js', 25 | 'client/app/**/*.js', 26 | 'client/test/**/*.js', 27 | 'client/components/**/*.js', 28 | 'client/app/**/*.html', 29 | 'client/components/**/*.html' 30 | ], 31 | 32 | preprocessors: { 33 | '**/*.jade': 'ng-jade2js', 34 | '**/*.html': 'html2js' 35 | }, 36 | 37 | plugins: [ 38 | 'karma-phantomjs-launcher', 39 | 'karma-jasmine', 40 | 'karma-junit-reporter', 41 | 'karma-coverage' 42 | ], 43 | 44 | ngHtml2JsPreprocessor: { 45 | stripPrefix: 'client/' 46 | }, 47 | 48 | ngJade2JsPreprocessor: { 49 | stripPrefix: 'client/' 50 | }, 51 | 52 | 53 | 54 | // list of files / patterns to exclude 55 | exclude: [], 56 | 57 | // web server port 58 | port: 8080, 59 | 60 | // level of logging 61 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 62 | logLevel: config.LOG_INFO, 63 | 64 | 65 | // enable / disable watching file and executing tests whenever any file changes 66 | autoWatch: false, 67 | 68 | 69 | // Start these browsers, currently available: 70 | // - Chrome 71 | // - ChromeCanary 72 | // - Firefox 73 | // - Opera 74 | // - Safari (only Mac) 75 | // - PhantomJS 76 | // - IE (only Windows) 77 | browsers: ['PhantomJS'], 78 | 79 | 80 | // Continuous Integration mode 81 | // if true, it capture browsers, run tests and exit 82 | singleRun: false 83 | }); 84 | }; 85 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-js-seed", 3 | "description": "Seed project for angular js application", 4 | "version": "1.0.4", 5 | "main": "server/app.js", 6 | "dependencies": { 7 | "express": "~4.9.0", 8 | "serve-favicon": "~2.0.1" 9 | }, 10 | "author": { 11 | "name": "Prince Soni", 12 | "email": "1989prince.soni@gmail.com" 13 | }, 14 | "scripts": { 15 | "start": "node server/app.js", 16 | "test": "grunt test:client" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/princesoni1989/angular-js-seed.git" 21 | }, 22 | "keywords": [ 23 | "angular", 24 | "angular seed", 25 | "angular-js-seed", 26 | "angularjs", 27 | "angularseed", 28 | "seed" 29 | ], 30 | "license": "ISC", 31 | "bugs": { 32 | "url": "https://github.com/princesoni1989/angular-js-seed/issues" 33 | }, 34 | "homepage": "https://github.com/princesoni1989/angular-js-seed#readme", 35 | "devDependencies": { 36 | "connect-livereload": "~0.4.0", 37 | "grunt": "~0.4.4", 38 | "grunt-angular-templates": "^0.5.4", 39 | "grunt-autoprefixer": "~0.7.2", 40 | "grunt-build-control": "~0.4.0", 41 | "grunt-concurrent": "~0.5.0", 42 | "grunt-contrib-clean": "~0.5.0", 43 | "grunt-contrib-concat": "~0.4.0", 44 | "grunt-contrib-copy": "~0.5.0", 45 | "grunt-contrib-cssmin": "~0.9.0", 46 | "grunt-contrib-htmlmin": "~0.2.0", 47 | "grunt-contrib-imagemin": "~0.7.1", 48 | "grunt-contrib-jshint": "~0.10.0", 49 | "grunt-contrib-sass": "^0.7.3", 50 | "grunt-contrib-uglify": "~0.4.0", 51 | "grunt-contrib-watch": "~0.6.1", 52 | "grunt-dom-munger": "^3.4.0", 53 | "grunt-env": "~0.4.1", 54 | "grunt-express-server": "~0.4.17", 55 | "grunt-google-cdn": "~0.4.0", 56 | "grunt-injector": "~0.5.4", 57 | "grunt-karma": "~0.8.2", 58 | "grunt-mocha-test": "~0.10.2", 59 | "grunt-newer": "~0.7.0", 60 | "grunt-ng-annotate": "^0.2.3", 61 | "grunt-node-inspector": "~0.1.5", 62 | "grunt-nodemon": "~0.2.0", 63 | "grunt-open": "~0.2.3", 64 | "grunt-protractor-runner": "^3.2.0", 65 | "grunt-protractor-webdriver": "^0.2.5", 66 | "grunt-rev": "~0.1.0", 67 | "grunt-svgmin": "~0.4.0", 68 | "grunt-usemin": "~2.1.1", 69 | "grunt-wiredep": "~1.8.0", 70 | "jit-grunt": "^0.5.0", 71 | "jshint-stylish": "~0.1.5", 72 | "karma": "~0.12.9", 73 | "karma-chrome-launcher": "~0.1.3", 74 | "karma-coffee-preprocessor": "~0.2.1", 75 | "karma-coverage": "^0.5.0", 76 | "karma-firefox-launcher": "~0.1.3", 77 | "karma-html2js-preprocessor": "~0.1.0", 78 | "karma-jade-preprocessor": "0.0.11", 79 | "karma-jasmine": "~0.1.5", 80 | "karma-ng-html2js-preprocessor": "~0.1.0", 81 | "karma-ng-jade2js-preprocessor": "^0.1.2", 82 | "karma-ng-scenario": "~0.1.0", 83 | "karma-phantomjs-launcher": "~0.2.0", 84 | "karma-requirejs": "~0.2.1", 85 | "karma-script-launcher": "~0.1.0", 86 | "open": "~0.0.4", 87 | "protractor": "^3.3.0", 88 | "requirejs": "~2.1.11", 89 | "should": "~3.3.1", 90 | "supertest": "~0.11.0", 91 | "time-grunt": "~0.3.1", 92 | "phantomjs": "^1.9.18" 93 | }, 94 | "engines": { 95 | "node": ">=0.10.0" 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration 2 | // https://github.com/angular/protractor/blob/master/referenceConf.js 3 | 4 | 'use strict'; 5 | 6 | exports.config = { 7 | // The timeout for each script run on the browser. This should be longer 8 | // than the maximum time your application needs to stabilize between tasks. 9 | allScriptsTimeout: 110000, 10 | 11 | // A base URL for your application under test. Calls to protractor.get() 12 | // with relative paths will be prepended with this. 13 | baseUrl: 'http://localhost:' + (process.env.PORT || '9000'), 14 | 15 | // If true, only chromedriver will be started, not a standalone selenium. 16 | // Tests for browsers other than chrome will not run. 17 | chromeOnly: false, 18 | 19 | // list of files / patterns to load in the browser 20 | specs: [ 21 | 'e2e/**/*.spec.js' 22 | ], 23 | 24 | // Patterns to exclude. 25 | exclude: [], 26 | 27 | // ----- Capabilities to be passed to the webdriver instance ---- 28 | // 29 | // For a full list of available capabilities, see 30 | // https://code.google.com/p/selenium/wiki/DesiredCapabilities 31 | // and 32 | // https://code.google.com/p/selenium/source/browse/javascript/webdriver/capabilities.js 33 | capabilities: { 34 | 'browserName': 'chrome' 35 | }, 36 | 37 | // ----- The test framework ----- 38 | // 39 | // Jasmine and Cucumber are fully supported as a test and assertion framework. 40 | // Mocha has limited beta support. You will need to include your own 41 | // assertion framework if working with mocha. 42 | framework: 'jasmine', 43 | 44 | // ----- Options to be passed to minijasminenode ----- 45 | // 46 | // See the full list at https://github.com/juliemr/minijasminenode 47 | jasmineNodeOpts: { 48 | defaultTimeoutInterval: 30000 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /server/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Main application file 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var express = require('express'); 8 | var path = require('path'); 9 | var favicon = require('serve-favicon'); 10 | var cluster = require('cluster'); 11 | var http = require('http'); 12 | var clusterCount = 1; 13 | var env; 14 | 15 | // Change FRONT-END PORT here 16 | var PORT = 9000; 17 | 18 | // Change directory "public" here 19 | var FolderName = '../' + 'client'; 20 | 21 | /*Command line arguments 22 | * Command node app.js [PORT] [PUBLIC-DIR-PATH]*/ 23 | if (process.argv.length > 1) { 24 | env = process.argv[2] || 'development' 25 | PORT = (process.argv[3] && !isNaN(process.argv[3]) && +process.argv[3] ) || PORT; 26 | FolderName = process.argv[4] || FolderName; 27 | } 28 | 29 | /** 30 | * Cluster setting 31 | */ 32 | if (cluster.isMaster) { 33 | var numReqs = 0; 34 | 35 | // Start workers and listen for messages containing notifyRequest 36 | var numCPUs = require('os').cpus().length; 37 | for (var i = 0; i < numCPUs; i++) { 38 | cluster.fork(); 39 | } 40 | 41 | Object.keys(cluster.workers).forEach(function (id) { 42 | cluster.workers[id].on('message', function messageHandler(msg) { 43 | if (msg.cmd && msg.cmd == 'notifyRequest') { 44 | numReqs += 1; 45 | } 46 | }); 47 | }); 48 | 49 | } else { 50 | // Worker processes have a http server. 51 | // Setup server 52 | var app = express(); 53 | var server = require('http').createServer(app); 54 | 55 | //app.use(favicon(path.join(__dirname, FolderName, 'favicon.ico'))); 56 | if (env === 'development') { 57 | app.use(require('connect-livereload')()); 58 | } 59 | app.use(favicon(path.join(__dirname, FolderName, 'favicon.ico'))); 60 | app.use(express.static(path.join(__dirname, '../.tmp'))); 61 | app.use(express.static(path.join(__dirname, FolderName))); 62 | app.set('appPath', path.join(__dirname, FolderName)); 63 | 64 | // Insert routes below 65 | // All undefined asset or api routes should return a 404 66 | app.route('/:url(api|components|app|bower_components|assets)/*') 67 | .get(function (req, res) { 68 | res.send("Error 404"); 69 | }); 70 | 71 | // All other routes should redirect to the index.html 72 | app.route('/*') 73 | .get(function (req, res) { 74 | res.sendFile(app.get('appPath') + '/index.html'); 75 | }); 76 | 77 | // Start server 78 | server.listen(PORT, function () { 79 | console.log('Server Listening To PORT: %d ', PORT); 80 | clusterCount = clusterCount + 1; 81 | }); 82 | } 83 | 84 | module.exports = app; 85 | --------------------------------------------------------------------------------