├── src
└── app
│ ├── assets
│ └── .gitkeep
│ ├── app.scss
│ ├── todo
│ ├── todo.scss
│ ├── todo.module.js
│ ├── todo.html
│ ├── todo.route.js
│ └── todo.controller.js
│ ├── styles
│ ├── _material.scss
│ ├── _base.scss
│ └── _mixins.scss
│ ├── core
│ ├── 404.html
│ ├── core.constants.js
│ ├── core.module.js
│ ├── dataservice.js
│ ├── core.route.js
│ └── core.config.js
│ ├── chat
│ ├── chat.module.js
│ ├── chat.scss
│ ├── chat.controller.js
│ ├── chat.scroll.directive.js
│ ├── chat.screen.directive.js
│ ├── chat.html
│ └── chat.route.js
│ ├── about
│ ├── about.module.js
│ ├── about.controller.js
│ ├── about.route.js
│ └── about.html
│ ├── blocks
│ ├── logger
│ │ ├── logger.module.js
│ │ └── logger.js
│ ├── exception
│ │ ├── exception.module.js
│ │ ├── exception.js
│ │ └── exceptionHandler.provider.js
│ └── router
│ │ ├── router.module.js
│ │ └── routerHelper.provider.js
│ ├── config
│ ├── config.json_example
│ └── template.ejs
│ ├── layout
│ ├── layout.module.js
│ ├── layout.footer.controller.js
│ ├── footer.html
│ ├── layout.route.js
│ ├── layout.header-controller.js
│ └── header.html
│ ├── auth
│ ├── login
│ │ ├── login.module.js
│ │ ├── login.scss
│ │ ├── login.html
│ │ ├── login.route.js
│ │ └── login.controller.js
│ ├── services
│ │ ├── services.module.js
│ │ └── auth.js
│ ├── auth.module.js
│ └── auth.route.js
│ ├── app.module.js
│ └── index.html
├── .bowerrc
├── qr.png
├── .gitignore
├── .csslintrc
├── .editorconfig
├── bower.json
├── LICENSE.md
├── .jshintrc
├── package.json
├── karma.conf.js
├── README.md
└── gulpfile.js
/src/app/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/app.scss:
--------------------------------------------------------------------------------
1 | @import "styles/_base";
2 |
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower_components"
3 | }
4 |
--------------------------------------------------------------------------------
/qr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarlepp/AngularJS-Firebase-Material-Demo/HEAD/qr.png
--------------------------------------------------------------------------------
/src/app/todo/todo.scss:
--------------------------------------------------------------------------------
1 | .todo-done {
2 | .md-list-item-text {
3 | opacity: .3;
4 | text-decoration: line-through;
5 | }
6 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | src/app/config/config.js
2 | src/app/config/config.json
3 | node_modules/
4 | bower_components/
5 | dist/*
6 | .tmp/*
7 | .idea
8 |
--------------------------------------------------------------------------------
/src/app/styles/_material.scss:
--------------------------------------------------------------------------------
1 | header {
2 | .md-list-item-text {
3 | flex: none !important;
4 |
5 | .md-button {
6 | margin-top: 5px;
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/app/core/404.html:
--------------------------------------------------------------------------------
1 |
404 - page not found
2 |
3 |
4 | Damn gerbils have stopped running again! Someone has been dispatched to poke them with a sharp stick.
5 |
6 |
--------------------------------------------------------------------------------
/src/app/styles/_base.scss:
--------------------------------------------------------------------------------
1 | @import "mixins";
2 | @import "material";
3 |
4 | html {
5 | font-family: "RobotoDraft", "Roboto", sans-serif;
6 | overflow-x: hidden;
7 | }
8 |
9 | body {
10 | @include noSelect();
11 | }
--------------------------------------------------------------------------------
/.csslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "box-model": false,
3 | "fallback-colors": false,
4 | "box-sizing": false,
5 | "compatible-vendor-prefixes": false,
6 | "gradients": false,
7 | "adjoining-classes": false,
8 | "outline-none" : false
9 | }
10 |
--------------------------------------------------------------------------------
/src/app/chat/chat.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Initialization of firebaseDemo.chat module.
6 | *
7 | * @namespace Modules
8 | */
9 | angular
10 | .module('firebaseDemo.chat', [])
11 | ;
12 | })();
13 |
--------------------------------------------------------------------------------
/src/app/todo/todo.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Initialization of firebaseDemo.todo module.
6 | *
7 | * @namespace Modules
8 | */
9 | angular
10 | .module('firebaseDemo.todo', [])
11 | ;
12 | })();
13 |
--------------------------------------------------------------------------------
/src/app/about/about.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Initialization of firebaseDemo.about module.
6 | *
7 | * @namespace Modules
8 | */
9 | angular
10 | .module('firebaseDemo.about', [])
11 | ;
12 | })();
13 |
--------------------------------------------------------------------------------
/src/app/blocks/logger/logger.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Initialization of blocks.logger module.
6 | *
7 | * @namespace Modules
8 | */
9 | angular
10 | .module('blocks.logger', [])
11 | ;
12 | })();
13 |
--------------------------------------------------------------------------------
/src/app/config/config.json_example:
--------------------------------------------------------------------------------
1 | /**
2 | * Copy this file to 'config.json' and define _your_ firebaseUrl + remember remove this comment block too :D
3 | */
4 | {
5 | "config": {
6 | "firebaseUrl": "https://CHANGETHISTOMATCHYOURFIREBASEURL.firebaseio.com/"
7 | }
8 | }
--------------------------------------------------------------------------------
/src/app/layout/layout.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Initialization of firebaseDemo.layout module.
6 | *
7 | * @namespace Modules
8 | */
9 | angular
10 | .module('firebaseDemo.layout', [])
11 | ;
12 | })();
13 |
--------------------------------------------------------------------------------
/src/app/auth/login/login.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Initialization of firebaseDemo.auth.login module.
6 | *
7 | * @namespace Modules
8 | */
9 | angular
10 | .module('firebaseDemo.auth.login', [])
11 | ;
12 | }());
13 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/src/app/auth/services/services.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Initialization of firebaseDemo.auth.services module.
6 | *
7 | * @namespace Modules
8 | */
9 | angular
10 | .module('firebaseDemo.auth.services', [])
11 | ;
12 | })();
13 |
--------------------------------------------------------------------------------
/src/app/blocks/exception/exception.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Initialization of blocks.exception module.
6 | *
7 | * @namespace Modules
8 | */
9 | angular
10 | .module('blocks.exception', [
11 | 'blocks.logger'
12 | ])
13 | ;
14 | })();
15 |
--------------------------------------------------------------------------------
/src/app/blocks/router/router.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Initialization of blocks.router module.
6 | *
7 | * @namespace Modules
8 | */
9 | angular
10 | .module('blocks.router', [
11 | 'ui.router',
12 | 'blocks.logger'
13 | ])
14 | ;
15 | })();
16 |
--------------------------------------------------------------------------------
/src/app/auth/auth.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Initialization of firebaseDemo.auth module.
6 | *
7 | * @namespace Modules
8 | */
9 | angular
10 | .module('firebaseDemo.auth', [
11 | 'firebaseDemo.auth.login',
12 | 'firebaseDemo.auth.services'
13 | ])
14 | ;
15 | })();
16 |
--------------------------------------------------------------------------------
/src/app/app.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | angular
5 | .module('firebaseDemo', [
6 | 'firebaseDemo.about',
7 | 'firebaseDemo.auth',
8 | 'firebaseDemo.chat',
9 | 'firebaseDemo.config',
10 | 'firebaseDemo.core',
11 | 'firebaseDemo.layout',
12 | 'firebaseDemo.todo'
13 | ])
14 | ;
15 | })();
16 |
--------------------------------------------------------------------------------
/src/app/core/core.constants.js:
--------------------------------------------------------------------------------
1 | /* global moment:false, Firebase: false */
2 | (function() {
3 | 'use strict';
4 |
5 | /**
6 | * Specify core constant values
7 | *
8 | * @namespace Constants
9 | * @memberOf Core
10 | */
11 | angular
12 | .module('firebaseDemo.core')
13 | .constant('moment', moment)
14 | .constant('Firebase', Firebase)
15 | ;
16 | })();
17 |
--------------------------------------------------------------------------------
/src/app/auth/login/login.scss:
--------------------------------------------------------------------------------
1 | .form-login {
2 | form {
3 | width: 300px;
4 | margin: 4em auto;
5 | padding: 0 2em 2em 2em;
6 | background: #fafafa;
7 | border: 1px solid #ebebeb;
8 | box-shadow: rgba(0, 0, 0, 0.14902) 0 1px 1px 0, rgba(0, 0, 0, 0.09804) 0 1px 2px 0;
9 | text-align: center;
10 |
11 | button {
12 | font-size: 30px;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/app/config/template.ejs:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT THIS FILE (config.js), THIS IS CREATED BY GULP TASK
2 | // jshint ignore: start
3 | (function() {
4 | 'use strict';
5 |
6 | angular.module("<%- moduleName %>"<% if (deps) { %>, <%= JSON.stringify(deps) %><% } %>)
7 | <% constants.forEach(function(constant) { %>
8 | .constant("<%- constant.name %>", <%= constant.value %>)
9 | <% }) %>
10 | ;
11 | })();
12 |
--------------------------------------------------------------------------------
/src/app/core/core.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Initialization of firebaseDemo.core module.
6 | *
7 | * @namespace Modules
8 | */
9 | angular
10 | .module('firebaseDemo.core', [
11 | 'ngAnimate', 'ngMaterial', 'ngMessages', 'ngSanitize',
12 | 'ui.router',
13 | 'firebase', 'toastr',
14 | 'firebase-demo-templates',
15 | 'blocks.exception', 'blocks.logger', 'blocks.router'
16 | ]);
17 | })();
18 |
--------------------------------------------------------------------------------
/src/app/about/about.controller.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify controller for firebaseDemo.about module.
6 | *
7 | * @namespace Controllers
8 | */
9 | angular
10 | .module('firebaseDemo.about')
11 | .controller('AboutController', AboutController)
12 | ;
13 |
14 | /**
15 | * @desc Controller implementation for /about route.
16 | * @namespace About
17 | * @memberOf Controllers
18 | * @ngInject
19 | *
20 | * @constructor
21 | */
22 | function AboutController() {}
23 | })();
24 |
--------------------------------------------------------------------------------
/src/app/layout/layout.footer.controller.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify controller for firebaseDemo.layout module.
6 | *
7 | * @namespace Controllers
8 | */
9 | angular
10 | .module('firebaseDemo.layout')
11 | .controller('FooterController', FooterController)
12 | ;
13 |
14 | /**
15 | * @desc Controller implementation for all routes
16 | * @namespace Layout
17 | * @memberOf Controllers
18 | * @ngInject
19 | *
20 | * @constructor
21 | */
22 | function FooterController() {}
23 | })();
24 |
--------------------------------------------------------------------------------
/src/app/auth/services/auth.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Auth factory.
6 | *
7 | * @namespace Factories
8 | */
9 | angular
10 | .module('firebaseDemo.auth.services')
11 | .factory('Auth', Auth)
12 | ;
13 |
14 | /**
15 | * @desc Service class to return a firebaseAuth service to configured Firebase instance.
16 | * @namespace Auth
17 | * @memberOf Factories
18 | * @ngInject
19 | *
20 | * @param {Factories.Dataservice} dataservice
21 | * @param {AngularFireAuthService} $firebaseAuth
22 | * @returns {AngularFireAuth}
23 | * @constructor
24 | */
25 | function Auth(dataservice, $firebaseAuth) {
26 | return $firebaseAuth(dataservice.getReference());
27 | }
28 | })();
29 |
--------------------------------------------------------------------------------
/src/app/layout/footer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 | GitHub
9 |
10 |
11 |
14 |
15 |
16 | Issues
17 |
18 |
19 |
22 |
23 |
24 | Tarmo Leppänen
25 |
26 |
27 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "firebase-demo",
3 | "main": [
4 | "/dist/firebase-demo.min.js",
5 | "/dist/firebase-demo.min.css"
6 | ],
7 | "version": "0.1.0",
8 | "description": "TODO",
9 | "authors": [
10 | "Tarmo Leppänen"
11 | ],
12 | "ignore": [
13 | "**/.*",
14 | "package.json",
15 | "klei.json",
16 | "gulpfile.js",
17 | "node_modules",
18 | "bower_components",
19 | "src"
20 | ],
21 | "dependencies": {
22 | "angular": "1.4.1",
23 | "angular-animate": "1.4.1",
24 | "angular-loading-bar": "0.7.1",
25 | "angular-material": "0.9.8",
26 | "angular-messages": "1.4.1",
27 | "angular-sanitize": "1.4.1",
28 | "angular-ui-router": "0.2.15",
29 | "angularfire": "1.1.1",
30 | "mdi": "1.1.34",
31 | "moment": "2.10.3",
32 | "angular-toastr": "1.4.1"
33 | },
34 | "devDependencies": {},
35 | "resolutions": {
36 | "angular": "1.4.1"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/app/chat/chat.scss:
--------------------------------------------------------------------------------
1 | .chat-container {
2 | main.md-padding {
3 | padding: 0;
4 | }
5 |
6 | md-input-container {
7 | padding-bottom: 0;
8 | padding-left: 10px;
9 | }
10 | }
11 |
12 | .chat {
13 | margin: 0;
14 | padding: 0;
15 |
16 | .messages {
17 | border-top: none;
18 | border-bottom: none;
19 | overflow-y: scroll;
20 | overflow-x: hidden;
21 | height: 100vh;
22 | padding: 0 10px;
23 |
24 | code {
25 | background-color: transparent;
26 | }
27 |
28 | .time {
29 | position: absolute;
30 | left: 5px;
31 | }
32 |
33 | .message {
34 | margin-left: 155px;
35 | }
36 | }
37 |
38 | .input-group {
39 | .form-control,
40 | .input-group-addon {
41 | &:first-child {
42 | border-top-left-radius: 0;
43 | }
44 | }
45 |
46 | .input-group-btn:last-child > .btn {
47 | border-top-right-radius: 0;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/app/auth/auth.route.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify run block for firebaseDemo.auth module.
6 | *
7 | * @namespace Routes
8 | */
9 | angular
10 | .module('firebaseDemo.auth')
11 | .run(moduleRun)
12 | ;
13 |
14 | /**
15 | * @desc Run block for firebaseDemo.auth module.
16 | * @namespace Auth
17 | * @memberOf Routes
18 | * @ngInject
19 | *
20 | * @param {Providers.RouterHelper} routerHelper
21 | */
22 | function moduleRun(routerHelper) {
23 | routerHelper.configureStates(getStates());
24 | }
25 |
26 | /**
27 | * @name getStates
28 | * @desc Getter method for firebaseDemo.auth module route definitions.
29 | * @memberOf Routes.Auth
30 | *
31 | * @returns {*[]}
32 | */
33 | function getStates() {
34 | return [
35 | {
36 | state: 'auth',
37 | config: {
38 | abstract: true,
39 | parent: 'firebaseDemo'
40 | }
41 | }
42 | ];
43 | }
44 | })();
45 |
--------------------------------------------------------------------------------
/src/app/blocks/exception/exception.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Exception factory.
6 | *
7 | * @namespace Factories
8 | */
9 | angular
10 | .module('blocks.exception')
11 | .factory('exception', exception)
12 | ;
13 |
14 | /**
15 | * @desc Application wide exception handler.
16 | * @namespace Exception
17 | * @memberOf Factories
18 | * @ngInject
19 | *
20 | * @param {Factories.Logger} logger
21 | * @returns {{
22 | * catcher: Factories.Exception.catcher
23 | * }}
24 | */
25 | function exception(logger) {
26 | return {
27 | catcher: catcher
28 | };
29 |
30 | ////////////////////
31 |
32 | /**
33 | * @name catcher
34 | * @desc Catcher method for exception factory.
35 | * @memberOf Factories.Exception
36 | *
37 | * @param {string} message
38 | * @returns {function}
39 | */
40 | function catcher(message) {
41 | return function(reason) {
42 | logger.error(message, reason);
43 | };
44 | }
45 | }
46 | })();
47 |
--------------------------------------------------------------------------------
/src/app/core/dataservice.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Dataservice factory.
6 | *
7 | * @namespace Factories
8 | */
9 | angular
10 | .module('firebaseDemo.core')
11 | .factory('dataservice', dataservice)
12 | ;
13 |
14 | /**
15 | * @desc Application wide dataservice.
16 | * @namespace Dataservice
17 | * @memberOf Factories
18 | * @ngInject
19 | *
20 | * @param {Firebase} Firebase
21 | * @param {object} config
22 | * @returns {{
23 | * getReference: Factories.Dataservice.getReference
24 | * }}
25 | */
26 | function dataservice(Firebase, config) {
27 | return {
28 | getReference: getReference
29 | };
30 |
31 | ////////////////////
32 |
33 | /**
34 | * @name getReference
35 | * @desc Getter method for Firebase reference.
36 | * @memberOf Factories.Dataservice
37 | *
38 | * @param {string} [identifier]
39 | * @returns {Firebase}
40 | */
41 | function getReference(identifier) {
42 | identifier = identifier || '';
43 |
44 | return new Firebase(config.firebaseUrl + identifier);
45 | }
46 | }
47 | })();
48 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | =====================
3 |
4 | Copyright (c) 2015 Tarmo Leppänen
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "bitwise": true,
3 | "camelcase": true,
4 | "curly": true,
5 | "eqeqeq": true,
6 | "es3": false,
7 | "forin": true,
8 | "freeze": true,
9 | "immed": true,
10 | "indent": 4,
11 | "latedef": "nofunc",
12 | "newcap": true,
13 | "noarg": true,
14 | "noempty": true,
15 | "nonbsp": true,
16 | "nonew": true,
17 | "plusplus": false,
18 | "quotmark": "single",
19 | "undef": true,
20 | "unused": false,
21 | "strict": false,
22 | "maxparams": 10,
23 | "maxdepth": 5,
24 | "maxstatements": 40,
25 | "maxcomplexity": 8,
26 | "maxlen": 120,
27 |
28 | "asi": false,
29 | "boss": false,
30 | "debug": false,
31 | "eqnull": true,
32 | "esnext": false,
33 | "evil": false,
34 | "expr": false,
35 | "funcscope": false,
36 | "globalstrict": false,
37 | "iterator": false,
38 | "lastsemic": false,
39 | "laxbreak": false,
40 | "laxcomma": false,
41 | "loopfunc": true,
42 | "maxerr": false,
43 | "moz": false,
44 | "multistr": false,
45 | "notypeof": false,
46 | "proto": false,
47 | "scripturl": false,
48 | "shadow": false,
49 | "sub": true,
50 | "supernew": false,
51 | "validthis": false,
52 | "noyield": false,
53 |
54 | "browser": true,
55 | "node": true,
56 |
57 | "globals": {
58 | "angular": false,
59 | "$": false
60 | }
61 | }
--------------------------------------------------------------------------------
/src/app/about/about.route.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify run block for firebaseDemo.about module.
6 | *
7 | * @namespace Routes
8 | */
9 | angular
10 | .module('firebaseDemo.about')
11 | .run(moduleRun)
12 | ;
13 |
14 | /**
15 | * @desc Run block for firebaseDemo.about module.
16 | * @namespace About
17 | * @memberOf Routes
18 | * @ngInject
19 | *
20 | * @param {Providers.RouterHelper} routerHelper
21 | */
22 | function moduleRun(routerHelper) {
23 | routerHelper.configureStates(getStates());
24 | }
25 |
26 | /**
27 | * @name getStates
28 | * @desc Getter method for firebaseDemo.about module route definitions.
29 | * @memberOf Routes.About
30 | *
31 | * @returns {*[]}
32 | */
33 | function getStates() {
34 | return [
35 | {
36 | state: 'about',
37 | config: {
38 | url: '/',
39 | parent: 'firebaseDemo',
40 | title: 'About',
41 | containerClass: 'about-container',
42 | views: {
43 | 'content@': {
44 | templateUrl: '/firebase-demo/about/about.html',
45 | controller: 'AboutController',
46 | controllerAs: 'vm'
47 | }
48 | }
49 | }
50 | }
51 | ];
52 | }
53 | })();
54 |
--------------------------------------------------------------------------------
/src/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/app/layout/layout.route.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify run block for firebaseDemo.layout module.
6 | *
7 | * @namespace Routes
8 | */
9 | angular
10 | .module('firebaseDemo.layout')
11 | .run(moduleRun)
12 | ;
13 |
14 | /**
15 | * @desc Run block for firebaseDemo.layout module.
16 | * @namespace Layout
17 | * @memberOf Routes
18 | * @ngInject
19 | *
20 | * @param {Providers.RouterHelper} routerHelper
21 | */
22 | function moduleRun(routerHelper) {
23 | routerHelper.configureStates(getStates());
24 | }
25 |
26 | /**
27 | * @name getStates
28 | * @desc Getter method for module route definitions.
29 | * @memberOf Routes.Layout
30 | *
31 | * @returns {*[]}
32 | */
33 | function getStates() {
34 | return [
35 | {
36 | state: 'firebaseDemo',
37 | config: {
38 | abstract: true,
39 | views: {
40 | header: {
41 | templateUrl: '/firebase-demo/layout/header.html',
42 | controller: 'HeaderController',
43 | controllerAs: 'vm'
44 | },
45 | footer: {
46 | templateUrl: '/firebase-demo/layout/footer.html',
47 | controller: 'FooterController',
48 | controllerAs: 'vm'
49 | }
50 | }
51 | }
52 | }
53 | ];
54 | }
55 | })();
56 |
--------------------------------------------------------------------------------
/src/app/auth/login/login.html:
--------------------------------------------------------------------------------
1 |
2 |
45 |
46 |
--------------------------------------------------------------------------------
/src/app/chat/chat.controller.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify controller for firebaseDemo.chat module.
6 | *
7 | * @namespace Controllers
8 | */
9 | angular
10 | .module('firebaseDemo.chat')
11 | .controller('ChatController', ChatController)
12 | ;
13 |
14 | /**
15 | * @desc Controller implementation for /chat route.
16 | * @namespace Chat
17 | * @memberOf Controllers
18 | * @ngInject
19 | *
20 | * @param {logger} logger
21 | * @param {{}} _user
22 | * @param {FirebaseArray} _messages
23 | * @constructor
24 | */
25 | function ChatController(
26 | logger,
27 | _user, _messages
28 | ) {
29 | var vm = this;
30 |
31 | vm.messages = _messages;
32 | vm.message = '';
33 | vm.form = {};
34 |
35 | // Method to send new message
36 | vm.send = function() {
37 | // Define new item
38 | var message = {
39 | message: vm.message,
40 | stamp: new Date().getTime(),
41 | user: {
42 | name: _user[_user.provider].displayName,
43 | email: _user[_user.provider].email || '',
44 | image: _user[_user.provider].profileImageURL
45 | }
46 | };
47 |
48 | // Add new message
49 | vm.messages
50 | .$add(message)
51 | .then(function onSuccess() {
52 | vm.message = '';
53 | vm.form.$setUntouched();
54 |
55 | logger.log('message stored');
56 | })
57 | ;
58 | };
59 | }
60 | })();
61 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "firebase-demo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "gulp watch",
7 | "test": "gulp test"
8 | },
9 | "dependencies": {
10 | },
11 | "devDependencies": {
12 | "connect-history-api-fallback": "1.1.0",
13 | "event-stream": "3.3.2",
14 | "gulp": "3.9.0",
15 | "gulp-angular-filesort": "1.1.1",
16 | "gulp-bower-files": "0.2.7",
17 | "gulp-cached": "1.1.0",
18 | "gulp-clean": "0.3.1",
19 | "gulp-concat": "2.6.0",
20 | "gulp-csslint": "0.2.0",
21 | "gulp-embedlr": "0.5.2",
22 | "gulp-filter": "3.0.1",
23 | "gulp-header": "1.7.1",
24 | "gulp-htmlmin": "1.2.0",
25 | "gulp-inject": "3.0.0",
26 | "gulp-jshint": "1.12.0",
27 | "gulp-karma": "0.0.5",
28 | "gulp-livereload": "3.8.1",
29 | "gulp-load-plugins": "1.0.0",
30 | "gulp-minify-css": "1.2.1",
31 | "gulp-ng-annotate": "1.1.0",
32 | "gulp-ng-constant": "1.1.0",
33 | "gulp-ng-html2js": "0.2.0",
34 | "gulp-rename": "1.2.2",
35 | "gulp-replace-task": "0.11.0",
36 | "gulp-sass": "2.1.0",
37 | "gulp-serve": "1.2.0",
38 | "gulp-uglify": "1.4.2",
39 | "gulp-util": "3.0.7",
40 | "jshint-stylish": "2.0.1",
41 | "karma": "0.13.14",
42 | "karma-chai": "0.1.0",
43 | "karma-chrome-launcher": "0.2.1",
44 | "karma-firefox-launcher": "0.1.6",
45 | "karma-mocha": "0.2.0",
46 | "karma-phantomjs-launcher": "0.2.1",
47 | "karma-script-launcher": "0.1.0",
48 | "lazypipe": "1.0.1",
49 | "sort-stream": "1.0.1",
50 | "streamqueue": "1.1.1",
51 | "tiny-lr": "0.2.1"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/app/chat/chat.scroll.directive.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify 'chatScroll' directive for firebaseDemo.chat module.
6 | *
7 | * @namespace Directives
8 | */
9 | angular
10 | .module('firebaseDemo.chat')
11 | .directive('chatScroll', chatScroll)
12 | ;
13 |
14 | /**
15 | * @desc 'chatScroll' directive implementation
16 | * @namespace chatScroll
17 | * @memberOf Directives
18 | * @ngInject
19 | *
20 | * @param {$window} $window
21 | * @param {$timeout} $timeout
22 | * @returns {{
23 | * link: function,
24 | * restrict: string
25 | * }}
26 | */
27 | function chatScroll($window, $timeout) {
28 | return {
29 | link: link,
30 | restrict: 'A'
31 | };
32 |
33 | /**
34 | * Linker function for 'chatScroll' directive.
35 | *
36 | * @param {$scope} scope Current scope
37 | * @param {$element} element Element object
38 | * @param {object} attributes Element attributes
39 | */
40 | function link(scope, element, attributes) {
41 | // Function to make actual scroll
42 | function scroll() {
43 | $timeout(function onTimeout() {
44 | element[0].scrollTop = element[0].scrollHeight;
45 | });
46 | }
47 |
48 | // Watch message collection and whenever it changes scroll bottom
49 | scope.$watchCollection(attributes.chatScroll, function onEvent() {
50 | scroll();
51 | });
52 |
53 | // Also bind scroll to window resize event
54 | angular.element($window).bind('resize', function onEvent() {
55 | scroll();
56 | });
57 | }
58 | }
59 | })();
60 |
--------------------------------------------------------------------------------
/src/app/todo/todo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
30 |
34 |
35 |
36 |
{{todo.label}}
37 |
38 |
39 |
42 |
43 | Remove item
44 |
45 |
46 | clear
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/app/styles/_mixins.scss:
--------------------------------------------------------------------------------
1 | @mixin border-radius($radius) {
2 | -webkit-border-radius: $radius;
3 | -moz-border-radius: $radius;
4 | -ms-border-radius: $radius;
5 | border-radius: $radius;
6 | }
7 |
8 | @mixin noSelect() {
9 | -webkit-touch-callout: none;
10 | -webkit-user-select: none;
11 | -khtml-user-select: none;
12 | -moz-user-select: none;
13 | -ms-user-select: none;
14 | user-select: none;
15 | }
16 |
17 | @mixin box-shadow($top, $left, $blur, $color: $colorBoxShadow, $inset:"") {
18 | -webkit-box-shadow: $top $left $blur $color #{$inset};
19 | -moz-box-shadow: $top $left $blur $color #{$inset};
20 | box-shadow: $top $left $blur $color #{$inset};
21 | }
22 |
23 | @mixin text-shadow($top, $left, $blur, $color) {
24 | text-shadow: $top $left $blur $color;
25 | }
26 |
27 | @mixin linear-gradient($fromColor, $toColor) {
28 | background-color: $toColor; /* Fallback Color */
29 | background-image: -webkit-gradient(linear, left top, left bottom, from($fromColor), to($toColor)); /* Saf4+, Chrome */
30 | background-image: -webkit-linear-gradient(top, $fromColor, $toColor); /* Chrome 10+, Saf5.1+, iOS 5+ */
31 | background-image: -moz-linear-gradient(top, $fromColor, $toColor); /* FF3.6 */
32 | background-image: -ms-linear-gradient(top, $fromColor, $toColor); /* IE10 */
33 | background-image: -o-linear-gradient(top, $fromColor, $toColor); /* Opera 11.10+ */
34 | background-image: linear-gradient(top, $fromColor, $toColor);
35 | filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#{$fromColor}', EndColorStr='#{$toColor}');
36 | }
37 |
38 | @mixin border-box() {
39 | -webkit-box-sizing: border-box;
40 | -moz-box-sizing: border-box;
41 | box-sizing: border-box;
42 | }
43 |
--------------------------------------------------------------------------------
/src/app/auth/login/login.route.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify run block for firebaseDemo.auth.login module.
6 | *
7 | * @namespace Routes
8 | */
9 | angular
10 | .module('firebaseDemo.auth.login')
11 | .run(moduleRun)
12 | ;
13 |
14 | /**
15 | * @desc Run block for firebaseDemo.auth.login module.
16 | * @namespace Auth.Login
17 | * @memberOf Routes
18 | * @ngInject
19 | *
20 | * @param {Providers.RouterHelper} routerHelper
21 | */
22 | function moduleRun(routerHelper) {
23 | routerHelper.configureStates(getStates());
24 | }
25 |
26 | /**
27 | * @name getStates
28 | * @desc Getter method for firebaseDemo.auth.login module route definitions.
29 | * @memberOf Routes.Auth.Login
30 | *
31 | * @returns {*[]}
32 | */
33 | function getStates() {
34 | return [
35 | {
36 | state: 'auth.login',
37 | config: {
38 | url: '/login',
39 | title: 'Login',
40 | containerClass: 'login-container',
41 | views: {
42 | 'content@': {
43 | templateUrl: '/firebase-demo/auth/login/login.html',
44 | controller: 'LoginController',
45 | controllerAs: 'vm',
46 | resolve: {
47 | _user: _user
48 | }
49 | }
50 | }
51 | }
52 | }
53 | ];
54 | }
55 |
56 | /**
57 | * @name _user
58 | * @desc '_user' resolve implementation.
59 | * @memberOf Routes.Auth.Login
60 | * @ngInject
61 | *
62 | * @param {AngularFireAuth} Auth
63 | * @returns {ng.IPromise|*}
64 | * @private
65 | */
66 | function _user(Auth) {
67 | return Auth.$waitForAuth();
68 | }
69 | })();
70 |
--------------------------------------------------------------------------------
/src/app/chat/chat.screen.directive.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify 'chatScreen' directive for firebaseDemo.chat module.
6 | *
7 | * @namespace Directives
8 | */
9 | angular
10 | .module('firebaseDemo.chat')
11 | .directive('chatScreen', chatScreen)
12 | ;
13 |
14 | /**
15 | * @desc 'chatScreen' directive implementation
16 | * @namespace chatScroll
17 | * @memberOf Directives
18 | * @ngInject
19 | *
20 | * @param {$window} $window
21 | * @param {$timeout} $timeout
22 | * @returns {{
23 | * link: function,
24 | * restrict: string
25 | * }}
26 | */
27 | function chatScreen($window, $timeout) {
28 | return {
29 | link: link,
30 | restrict: 'A'
31 | };
32 |
33 | //noinspection JSUnusedLocalSymbols
34 | /**
35 | * Linker function for 'chatScreen' directive.
36 | *
37 | * @param {$scope} scope
38 | * @param {$element} element
39 | */
40 | function link(scope, element) {
41 | // Initialize height values
42 | var heightHeader = 0;
43 | var heightFooter = 0;
44 | var heightTotal = 0;
45 |
46 | // function to make actual chat screen resize
47 | function resize() {
48 | $timeout(function onTimeout() {
49 | heightHeader = document.getElementById('header').offsetHeight;
50 | heightFooter = document.getElementById('footer').offsetHeight;
51 | heightTotal = $window.innerHeight - heightHeader - heightFooter - 70;
52 |
53 | angular.element(element).css('height', heightTotal + 'px');
54 | });
55 | }
56 |
57 | // Bind resize to window resize event
58 | angular.element($window).bind('resize', function onEvent() {
59 | resize();
60 | });
61 |
62 | // And on initialize resize screen
63 | resize();
64 | }
65 | }
66 | })();
67 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function ( karma ) {
3 | process.env.PHANTOMJS_BIN = 'node_modules/karma-phantomjs-launcher/node_modules/.bin/phantomjs';
4 |
5 | karma.set({
6 | /**
7 | * From where to look for files, starting with the location of this file.
8 | */
9 | basePath: './',
10 |
11 | /**
12 | * Filled by the task `gulp karma-conf`
13 | */
14 | files: [
15 | ],
16 |
17 | frameworks: [ 'mocha', 'chai' ],
18 | plugins: [ 'karma-mocha', 'karma-chai', 'karma-phantomjs-launcher' ],
19 |
20 | /**
21 | * How to report, by default.
22 | */
23 | reporters: 'progress',
24 |
25 | /**
26 | * Show colors in output?
27 | */
28 | colors: true,
29 |
30 | /**
31 | * On which port should the browser connect, on which port is the test runner
32 | * operating, and what is the URL path for the browser to use.
33 | */
34 | port: 9099,
35 | runnerPort: 9100,
36 | urlRoot: '/',
37 |
38 | /**
39 | * Disable file watching by default.
40 | */
41 | autoWatch: false,
42 |
43 | /**
44 | * The list of browsers to launch to test on. This includes only "Firefox" by
45 | * default, but other browser names include:
46 | * Chrome, ChromeCanary, Firefox, Opera, Safari, PhantomJS
47 | *
48 | * Note that you can also use the executable name of the browser, like "chromium"
49 | * or "firefox", but that these vary based on your operating system.
50 | *
51 | * You may also leave this blank and manually navigate your browser to
52 | * http://localhost:9099/ when you're running tests. The window/tab can be left
53 | * open and the tests will automatically occur there during the build. This has
54 | * the aesthetic advantage of not launching a browser every time you save.
55 | */
56 | browsers: [
57 | 'PhantomJS'
58 | ]
59 | });
60 | };
61 |
--------------------------------------------------------------------------------
/src/app/layout/layout.header-controller.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify controller for firebaseDemo.layout module.
6 | *
7 | * @namespace Controllers
8 | */
9 | angular
10 | .module('firebaseDemo.layout')
11 | .controller('HeaderController', HeaderController)
12 | ;
13 |
14 | /**
15 | * @desc Controller implementation.
16 | * @namespace Layout
17 | * @memberOf Controllers
18 | * @ngInject
19 | *
20 | * @param {ui.router.state.$state} $state
21 | * @param {AngularFireAuth} Auth
22 | * @constructor
23 | * @ngInject
24 | */
25 | function HeaderController(
26 | $state,
27 | Auth
28 | ) {
29 | var vm = this;
30 |
31 | // Initialize user object
32 | vm.user = {};
33 |
34 | /**
35 | * Method to get provider class for currently authenticated user.
36 | *
37 | * @param {string} provider
38 | * @returns {string}
39 | */
40 | vm.getProviderClass = function getProviderClass(provider) {
41 | var output = '';
42 |
43 | switch (provider) {
44 | case 'facebook':
45 | output = 'mdi-facebook-box';
46 | break;
47 | case 'twitter':
48 | output = 'mdi-twitter-box';
49 | break;
50 | case 'github':
51 | output = 'mdi-github-box';
52 | break;
53 | case 'google':
54 | output = 'mdi-google-plus-box';
55 | break;
56 | }
57 |
58 | return output;
59 | };
60 |
61 | /**
62 | * Method to make logout action.
63 | *
64 | * @param {Event} $event
65 | */
66 | vm.logout = function logout($event) {
67 | $event.preventDefault();
68 | $event.stopPropagation();
69 |
70 | Auth.$unauth();
71 |
72 | $state.go('about');
73 | };
74 |
75 | // Watcher for auth status
76 | Auth.$onAuth(function onAuth(user) {
77 | vm.user = user;
78 | });
79 | }
80 | })();
81 |
--------------------------------------------------------------------------------
/src/app/core/core.route.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify run block for module
6 | *
7 | * @namespace Routes
8 | */
9 | angular
10 | .module('firebaseDemo.core')
11 | .run(appRun)
12 | ;
13 |
14 | /**
15 | * @desc Actual run block.
16 | * @namespace Core
17 | * @memberOf Routes
18 | * @ngInject
19 | *
20 | * @param {ng.IRootScopeService|{containerClass: string}} $rootScope
21 | * @param {Providers.RouterHelper} routerHelper
22 | * @constructor
23 | */
24 | function appRun(
25 | $rootScope,
26 | routerHelper
27 | ) {
28 | // Set default state to be 404 page
29 | routerHelper.configureStates(getStates(), '/404');
30 |
31 | // Add success handler for route change
32 | $rootScope.$on('$stateChangeSuccess', stateChangeSuccess);
33 |
34 | //noinspection JSUnusedLocalSymbols
35 | /**
36 | * Success state change helper function
37 | *
38 | * @param {object} event
39 | * @param {IState|{containerClass: string}} toState
40 | * @param {object} toParams
41 | * @param {IState|{containerClass: string}} fromState
42 | * @param {object} fromParams
43 | */
44 | function stateChangeSuccess(event, toState, toParams, fromState, fromParams) {
45 | $rootScope.containerClass = toState.containerClass;
46 | }
47 | }
48 |
49 | /**
50 | * @name getStates
51 | * @desc Getter method for module route definitions.
52 | * @memberOf Routes.Core
53 | *
54 | * @returns {*[]}
55 | */
56 | function getStates() {
57 | return [
58 | {
59 | state: '404',
60 | config: {
61 | url: '/404',
62 | title: '404',
63 | parent: 'firebaseDemo',
64 | views: {
65 | 'content@': {
66 | templateUrl: '/firebase-demo/core/404.html'
67 | }
68 | }
69 | }
70 | }
71 | ];
72 | }
73 | })();
74 |
--------------------------------------------------------------------------------
/src/app/todo/todo.route.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify run block for firebaseDemo.todo module.
6 | *
7 | * @namespace Routes
8 | */
9 | angular
10 | .module('firebaseDemo.todo')
11 | .run(moduleRun)
12 | ;
13 |
14 | /**
15 | * @desc Run block for firebaseDemo.todo module.
16 | * @namespace Todo
17 | * @memberOf Routes
18 | * @ngInject
19 | *
20 | * @param {Providers.RouterHelper} routerHelper
21 | */
22 | function moduleRun(routerHelper) {
23 | routerHelper.configureStates(getStates());
24 | }
25 |
26 | /**
27 | * @name getStates
28 | * @desc Getter method for module route definitions.
29 | * @memberOf Routes.Todo
30 | *
31 | * @returns {*[]}
32 | */
33 | function getStates() {
34 | return [
35 | {
36 | state: 'todo',
37 | config: {
38 | url: '/todo',
39 | parent: 'firebaseDemo',
40 | title: 'Todo',
41 | containerClass: 'todo-container',
42 | views: {
43 | 'content@': {
44 | templateUrl: '/firebase-demo/todo/todo.html',
45 | controller: 'TodoController',
46 | controllerAs: 'vm',
47 | resolve: {
48 | _todos: _todos
49 | }
50 | }
51 | }
52 | }
53 | }
54 | ];
55 | }
56 |
57 | /**
58 | * @name _todos
59 | * @desc '_todos' resolve implementation.
60 | * @memberOf Routes.Todo
61 | * @ngInject
62 | *
63 | * @param {AngularFireArrayService} $firebaseArray
64 | * @param {AngularFireAuth} Auth
65 | * @param {Factories.Dataservice} dataservice
66 | * @returns {ng.IPromise}
67 | * @private
68 | */
69 | function _todos($firebaseArray, Auth, dataservice) {
70 | return Auth
71 | .$requireAuth()
72 | .then(getItems)
73 | ;
74 |
75 | function getItems(user) {
76 | return $firebaseArray(dataservice.getReference('todos/' + user.auth.uid));
77 | }
78 | }
79 | })();
80 |
--------------------------------------------------------------------------------
/src/app/chat/chat.html:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 | {{message.user.name}}
11 |
12 |
{{message.message}}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
42 |
43 |
44 |
45 |
46 |
63 |
--------------------------------------------------------------------------------
/src/app/layout/header.html:
--------------------------------------------------------------------------------
1 |
2 |
70 |
71 |
--------------------------------------------------------------------------------
/src/app/chat/chat.route.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify run block for firebaseDemo.chat module.
6 | *
7 | * @namespace Routes
8 | */
9 | angular
10 | .module('firebaseDemo.chat')
11 | .run(moduleRun)
12 | ;
13 |
14 | /**
15 | * @desc Run block for firebaseDemo.chat module.
16 | * @namespace Chat
17 | * @memberOf Routes
18 | * @ngInject
19 | *
20 | * @param {Providers.RouterHelper} routerHelper
21 | */
22 | function moduleRun(routerHelper) {
23 | routerHelper.configureStates(getStates());
24 | }
25 |
26 | /**
27 | * @name getStates
28 | * @desc Getter method for firebaseDemo.chat module route definitions.
29 | * @memberOf Routes.Chat
30 | *
31 | * @returns {*[]}
32 | */
33 | function getStates() {
34 | return [
35 | {
36 | state: 'chat',
37 | config: {
38 | url: '/chat',
39 | parent: 'firebaseDemo',
40 | title: 'Chat',
41 | containerClass: 'chat-container',
42 | views: {
43 | 'content@': {
44 | templateUrl: '/firebase-demo/chat/chat.html',
45 | controller: 'ChatController',
46 | controllerAs: 'vm',
47 | resolve: {
48 | _user: _user,
49 | _messages: _messages
50 | }
51 | }
52 | }
53 | }
54 | }
55 | ];
56 | }
57 |
58 | /**
59 | * @name _user
60 | * @desc '_user' resolve implementation.
61 | * @memberOf Routes.Chat
62 | * @ngInject
63 | *
64 | * @param {AngularFireAuth} Auth
65 | * @returns {ng.IPromise|*}
66 | * @private
67 | */
68 | function _user(Auth) {
69 | return Auth.$requireAuth();
70 | }
71 |
72 | /**
73 | * @name _messages
74 | * @desc '_messages' resolve function.
75 | * @memberOf Routes.Chat
76 | * @ngInject
77 | *
78 | * @param {AngularFireArrayService} $firebaseArray
79 | * @param {Factories.Dataservice} dataservice
80 | * @returns {ng.IPromise}
81 | * @private
82 | */
83 | function _messages($firebaseArray, dataservice) {
84 | return $firebaseArray(dataservice.getReference('messages'));
85 | }
86 | })();
87 |
--------------------------------------------------------------------------------
/src/app/todo/todo.controller.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify controller for firebaseDemo.todo module.
6 | *
7 | * @namespace Controllers
8 | */
9 | angular
10 | .module('firebaseDemo.todo')
11 | .controller('TodoController', TodoController)
12 | ;
13 |
14 | /**
15 | * @desc Controller implementation.
16 | * @namespace Todo
17 | * @memberOf Controllers
18 | * @ngInject
19 | *
20 | * @param {Factories.Logger} logger
21 | * @param {FirebaseArray} _todos
22 | * @constructor
23 | */
24 | function TodoController(
25 | logger,
26 | _todos
27 | ) {
28 | var vm = this;
29 |
30 | vm.todos = _todos;
31 | vm.label = '';
32 | vm.form = {};
33 |
34 | // Method to add new item
35 | vm.add = function() {
36 | // Define new item
37 | var todo = {
38 | label: vm.label,
39 | dateAdded: new Date().getTime(),
40 | dateDone: null,
41 | isDone: false
42 | };
43 |
44 | // Add new todo item
45 | vm.todos
46 | .$add(todo)
47 | .then(function() {
48 | logger.success('New todo item added!');
49 |
50 | vm.label = '';
51 | vm.form.$setUntouched();
52 | })
53 | ;
54 | };
55 |
56 | /**
57 | * Method to save specified todo item.
58 | *
59 | * @param {{}} todo
60 | */
61 | vm.save = function(todo) {
62 | todo.dateDone = !todo.isDone ? null : new Date().getTime();
63 |
64 | // Save actual item
65 | vm.todos
66 | .$save(todo)
67 | .then(function() {
68 | logger.success('Todo item saved');
69 | })
70 | ;
71 | };
72 |
73 | /**
74 | * Method to remove specified todo item.
75 | *
76 | * @param {Event} event
77 | * @param {{}} todo
78 | */
79 | vm.remove = function(event, todo) {
80 | // We need to stop anything else to happen in this case...
81 | event.preventDefault();
82 | event.stopPropagation();
83 |
84 | // Remove actual item
85 | vm.todos
86 | .$remove(todo)
87 | .then(function() {
88 | logger.success('Todo item removed');
89 | })
90 | ;
91 | };
92 | }
93 | })();
94 |
--------------------------------------------------------------------------------
/src/app/auth/login/login.controller.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify controller for firebaseDemo.auth.login module.
6 | *
7 | * @namespace Controllers
8 | */
9 | angular
10 | .module('firebaseDemo.auth.login')
11 | .controller('LoginController', LoginController)
12 | ;
13 |
14 | /**
15 | * @desc Controller implementation for /login route.
16 | * @namespace Login
17 | * @memberOf Controllers
18 | * @ngInject
19 | *
20 | * @param {ui.router.state.$state} $state
21 | * @param {Factories.Dataservice} dataservice
22 | * @param {Factories.Logger} logger
23 | * @param {object|undefined} _user
24 | * @constructor
25 | */
26 | function LoginController(
27 | $state,
28 | dataservice, logger,
29 | _user
30 | ) {
31 | var vm = this;
32 |
33 | // User has already logged in, so redirect to proper page
34 | if (_user) {
35 | $state.go('todo');
36 | }
37 |
38 | /**
39 | * Method to make actual login via specified provider to Firebase backend.
40 | *
41 | * @param {string} provider Name of the used provider, this is one of following:
42 | * - facebook
43 | * - twitter
44 | * - github
45 | * - google
46 | */
47 | vm.login = function(provider) {
48 | var ref = dataservice.getReference();
49 |
50 | /**
51 | * Login callback function which handles possible login errors and redirection if all is ok.
52 | *
53 | * @param {object} error
54 | * @param {object} authData
55 | */
56 | var callback = function(error, authData) {
57 | if (error) {
58 | logger.error('Login Failed!', error);
59 | } else {
60 | logger.log('auth data', authData);
61 | logger.success('Login successfully!');
62 |
63 | $state.go('todo');
64 | }
65 | };
66 |
67 | // Specify used options for Firebase auth
68 | var options = {
69 | remember: 'sessionOnly',
70 | scope: (provider !== 'github') ? 'email' : 'user:email'
71 | };
72 |
73 | // And make actual user authentication
74 | ref.authWithOAuthPopup(provider, callback, options);
75 | };
76 | }
77 | }());
78 |
--------------------------------------------------------------------------------
/src/app/about/about.html:
--------------------------------------------------------------------------------
1 |
2 | This is a small demo about
3 | Angular.js ,
4 | Angular Material and
5 | Firebase .
6 | Main purpose of this demo application is to show how powerful these components are together.
7 |
8 |
9 |
60 |
--------------------------------------------------------------------------------
/src/app/blocks/exception/exceptionHandler.provider.js:
--------------------------------------------------------------------------------
1 | // Include in index.html so that app level exceptions are handled.
2 | // Exclude from testRunner.html which should run exactly what it wants to run
3 | (function() {
4 | 'use strict';
5 |
6 | /**
7 | * Specify provider and configure it for blocks.exception module
8 | *
9 | * @namespace Providers
10 | */
11 | angular
12 | .module('blocks.exception')
13 | .provider('exceptionHandler', exceptionHandlerProvider)
14 | .config(moduleConfig)
15 | ;
16 |
17 | /**
18 | * @desc Must configure the exception handling.
19 | * @namespace ExceptionHandler
20 | * @memberOf Providers
21 | */
22 | function exceptionHandlerProvider() {
23 | /* jshint validthis:true */
24 | this.config = {
25 | appErrorPrefix: undefined
26 | };
27 |
28 | this.configure = function configure(appErrorPrefix) {
29 | this.config.appErrorPrefix = appErrorPrefix;
30 | };
31 |
32 | this.$get = function $get() {
33 | return {config: this.config};
34 | };
35 | }
36 |
37 | /**
38 | * @desc Configure by setting an optional string value for appErrorPrefix. Accessible via config.appErrorPrefix
39 | * (via config value).
40 | * @namespace ExceptionHandler
41 | * @memberOf Providers
42 | * @ngInject
43 | *
44 | * @param {$provide} $provide
45 | */
46 | function moduleConfig($provide) {
47 | $provide.decorator('$exceptionHandler', extendExceptionHandler);
48 | }
49 |
50 | /**
51 | * @desc Extend the $exceptionHandler service to also display a toast.
52 | * @namespace ExceptionHandler
53 | * @memberOf Providers
54 | * @ngInject
55 | *
56 | * @param {$delegate|*} $delegate
57 | * @param {Providers.ExceptionHandler} exceptionHandler
58 | * @param {Factories.Logger} logger
59 | * @return {function} the decorated $exceptionHandler service
60 | */
61 | function extendExceptionHandler($delegate, exceptionHandler, logger) {
62 | return function(exception, cause) {
63 | var appErrorPrefix = exceptionHandler.config.appErrorPrefix || '';
64 | var errorData = {
65 | exception: exception,
66 | cause: cause
67 | };
68 |
69 | // Create exception message
70 | exception.message = appErrorPrefix + exception.message;
71 |
72 | $delegate(exception, cause);
73 |
74 | /**
75 | * Could add the error to a service's collection, add errors to $rootScope, log errors to remote web server,
76 | * or log locally. Or throw hard. It is entirely up to you. throw exception;
77 | *
78 | * @example
79 | * throw { message: 'error message we added' };
80 | */
81 | logger.error(exception.message, errorData);
82 | };
83 | }
84 | })();
85 |
--------------------------------------------------------------------------------
/src/app/blocks/logger/logger.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Logger factory.
6 | *
7 | * @namespace Factories
8 | */
9 | angular
10 | .module('blocks.logger')
11 | .factory('logger', logger)
12 | ;
13 |
14 | /**
15 | * @desc Application wide logger handler.
16 | * @namespace Logger
17 | * @memberOf Factories
18 | * @ngInject
19 | *
20 | * @param {$log} $log
21 | * @param {$injector} $injector
22 | * @returns {{
23 | * error: Factories.Logger.error,
24 | * info: Factories.Logger.info,
25 | * success: Factories.Logger.success,
26 | * warning: Factories.Logger.warning,
27 | * log: Factories.Logger.log
28 | * }}
29 | */
30 | function logger($log, $injector) {
31 | return {
32 | // toastr implementations
33 | error: error,
34 | info: info,
35 | success: success,
36 | warning: warning,
37 |
38 | // straight to console; bypass toastr
39 | log: log
40 | };
41 |
42 | ////////////////////
43 |
44 | /**
45 | * @name error
46 | * @desc Error method for logger factory.
47 | * @memberOf Factories.Logger
48 | *
49 | * @param {string} message
50 | * @param {object} [data]
51 | * @param {string} [title]
52 | */
53 | function error(message, data, title) {
54 | data = data || {};
55 | title = title || '';
56 |
57 | $injector.get('toastr').error(message, title);
58 |
59 | $log.error('Error: ' + message, data);
60 | }
61 |
62 | /**
63 | * @name info
64 | * @desc Info method for logger factory.
65 | * @memberOf Factories.Logger
66 | *
67 | * @param {string} message
68 | * @param {object} [data]
69 | * @param {string} [title]
70 | */
71 | function info(message, data, title) {
72 | data = data || {};
73 | title = title || '';
74 |
75 | $injector.get('toastr').info(message, title);
76 |
77 | $log.info('Info: ' + message, data);
78 | }
79 |
80 | /**
81 | * @name success
82 | * @desc Success method for logger factory.
83 | * @memberOf Factories.Logger
84 | *
85 | * @param {string} message
86 | * @param {object} [data]
87 | * @param {string} [title]
88 | */
89 | function success(message, data, title) {
90 | data = data || {};
91 | title = title || '';
92 |
93 | $injector.get('toastr').success(message, title);
94 |
95 | $log.info('Success: ' + message, data);
96 | }
97 |
98 | /**
99 | * @name warning
100 | * @desc Warning method for logger factory.
101 | * @memberOf Factories.Logger
102 | *
103 | * @param {string} message
104 | * @param {object} [data]
105 | * @param {string} [title]
106 | */
107 | function warning(message, data, title) {
108 | data = data || {};
109 | title = title || '';
110 |
111 | $injector.get('toastr').warning(message, title);
112 |
113 | $log.warn('Warning: ' + message, data, title);
114 | }
115 |
116 | /**
117 | * @name log
118 | * @desc Default log function.
119 | * @memberOf Factories.Logger
120 | */
121 | function log() {
122 | $log.log(arguments);
123 | }
124 | }
125 | }());
126 |
--------------------------------------------------------------------------------
/src/app/core/core.config.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify application configure values
6 | *
7 | * @type {{
8 | * appErrorPrefix: string,
9 | * appTitle: string
10 | * }}
11 | */
12 | var config = {
13 | appErrorPrefix: 'Angular/Firebase/Material - demo - Error',
14 | appTitle: 'Angular/Firebase/Material - demo'
15 | };
16 |
17 | /**
18 | * Module initialization
19 | *
20 | * @namespace Core
21 | */
22 | angular
23 | .module('firebaseDemo.core')
24 | .value('config', config)
25 | .config(moduleConfig)
26 | ;
27 |
28 | /**
29 | * @desc Actual configure implementation for module.
30 | * @namespace Configure
31 | * @memberOf Core
32 | * @ngInject
33 | *
34 | * @param {$provide} $provide
35 | * @param {$logProvider} $logProvider
36 | * @param {$mdThemingProvider} $mdThemingProvider
37 | * @param {Providers.RouterHelperProvider} routerHelperProvider
38 | * @param {Providers.ExceptionHandler} exceptionHandlerProvider
39 | * @constructor
40 | */
41 | function moduleConfig(
42 | $provide, $logProvider, $mdThemingProvider,
43 | routerHelperProvider, exceptionHandlerProvider
44 | ) {
45 | // Add filename + line number feature to $log component
46 | $provide.decorator('$log', function decorator($delegate) {
47 | var originalFunctions = {};
48 |
49 | // Store the original log functions
50 | angular.forEach($delegate, function iterator(originalFunction, functionName) {
51 | originalFunctions[functionName] = originalFunction;
52 | });
53 |
54 | var functionsToDecorate = ['log', 'info', 'warn', 'error', 'debug'];
55 |
56 | // Apply the decorations
57 | angular.forEach(functionsToDecorate, function iterator(functionName) {
58 | $delegate[functionName] = logDecorator(originalFunctions[functionName]);
59 | });
60 |
61 | return $delegate;
62 | });
63 |
64 | if ($logProvider.debugEnabled) {
65 | $logProvider.debugEnabled(true);
66 | }
67 |
68 | // Configure material design palettes
69 | $mdThemingProvider
70 | .theme('default')
71 | .primaryPalette('blue-grey')
72 | .accentPalette('blue')
73 | ;
74 |
75 | // Configure exception handler provider
76 | exceptionHandlerProvider.configure(config.appErrorPrefix);
77 |
78 | // Configure router helper provider
79 | routerHelperProvider.configure({docTitle: config.appTitle + ': '});
80 | }
81 |
82 | /**
83 | * $log decorator function, this is needed to add filename and line number to each $log command.
84 | *
85 | * @param {function} func
86 | * @returns {function}
87 | */
88 | function logDecorator(func) {
89 | return function anon() {
90 | var args = [].slice.call(arguments);
91 |
92 | // Insert a separator between the existing log message(s) and what we're adding.
93 | args.push(' - ');
94 |
95 | // Use (instance of Error)'s stack to get the current line.
96 | var stack = (new Error()).stack.split('\n').slice(1);
97 |
98 | // Throw away the first item because it is the `$log.fn()` function,
99 | // but we want the code that called `$log.fn()`.
100 | stack.shift();
101 |
102 | // We only want the top line, thanks.
103 | stack = stack.slice(1, 2);
104 |
105 | // Put it on the args stack.
106 | args.push(stack);
107 |
108 | // Call the original function with the new args.
109 | func.apply(func, args);
110 | };
111 | }
112 | })();
113 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | AngularJS/Firebase/Material - demo
2 | ============
3 | [](https://david-dm.org/tarlepp/Angular-Firebase-Material-Demo)
4 | [](https://david-dm.org/tarlepp/Angular-Firebase-Material-Demo#info=devDependencies)
5 |
6 | ## What is this?
7 |
8 | Just a small demo to show how to use [Angular](https://angularjs.org/) + [Firebase](https://www.firebase.com/) +
9 | [Google Material Design](https://www.google.com/design/spec/material-design/introduction.html) together. Currently
10 | this demo application contains following features:
11 |
12 | * Social media login (Facebook, Twitter, Google+ and GitHub)
13 | * Personal 'Todo' item list
14 | * Chat with other users
15 |
16 | ## Demo
17 |
18 | Demo of this application can be found from [https://boiling-fire-2804.firebaseapp.com/](https://boiling-fire-2804.firebaseapp.com/)
19 |
20 | 
21 |
22 | ## Used libraries, guides, etc.
23 |
24 | ### Libraries
25 |
26 | * [AngularJS — Superheroic JavaScript MVW Framework](https://angularjs.org/)
27 | * [Angular Material](https://material.angularjs.org/)
28 | * [AngularFire](https://www.firebase.com/docs/web/libraries/angular/)
29 | * [Material Design icons By Google](https://github.com/google/material-design-icons)
30 | * [Moment.js](http://momentjs.com/)
31 |
32 | ### Guides
33 |
34 | * [Angular Style Guide](https://github.com/johnpapa/angular-styleguide)
35 | * [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript/tree/master/es5)
36 |
37 | ### Other resources
38 |
39 | * [Firebase](https://www.firebase.com/)
40 | * [Material design](https://www.google.com/design/spec/material-design/)
41 |
42 | ## Installation
43 |
44 | First of all you have to install npm and node.js to your box. Installation instructions can
45 | be found [here](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager).
46 |
47 | After that you need to install bower and gulp main packages to make all things to happen.
48 | These can be installed with following commands on your *nix box.
49 |
50 | sudo npm install bower -g
51 | sudo npm install gulp -g
52 |
53 |
54 | And when you have npm and node.js installed to your box, just navigate yourself to root folder
55 | of the app and run following commands:
56 |
57 |
58 | npm install
59 | bower install
60 |
61 |
62 | ### Configuration
63 |
64 | See ```/src/app/config/config.json_example``` file and copy it to ```/src/app/config/config.json``` file and make
65 | necessary changes to it. Note that you need a Firebase account to get that url.
66 |
67 | ### Firebase
68 |
69 | To get Firebase running as it should first you need to make new Firebase application. Which you can create easily from
70 | their website [https://www.firebase.com/](https://www.firebase.com/).
71 |
72 | After you have created new application you need to make some [security rules](https://www.firebase.com/docs/security/quickstart.html)
73 | for the used data storage. Below is configuration that my demo application uses, so you can use the same within your
74 | application.
75 |
76 | ```
77 | {
78 | "rules": {
79 | "messages": {
80 | ".write": "auth !== null",
81 | ".read": "auth !== null"
82 | },
83 | "todos": {
84 | "$uid": {
85 | // grants write access to the owner of this user account whose uid must exactly match the key ($uid)
86 | ".write": "auth !== null && auth.uid === $uid",
87 | // grants read access to any user who is logged in with Facebook
88 | ".read": "auth !== null && auth.uid === $uid"
89 | }
90 | }
91 | }
92 | }
93 | ```
94 |
95 | These rules ensure that 'todo' items are show only to user who made those. Also chat messages requires that user is
96 | logged in to read / write those.
97 |
98 | ## Development
99 |
100 | To start developing in the project run:
101 |
102 | ```bash
103 | gulp serve
104 | ```
105 |
106 | Then head to `http://localhost:3002` in your browser.
107 |
108 | The `serve` tasks starts a static file server, which serves the AngularJS application, and a watch task which watches
109 | all files for changes and lints, builds and injects them into the index.html accordingly.
110 |
111 | ## Tests
112 |
113 | To run tests run:
114 |
115 | ```bash
116 | gulp test
117 | ```
118 |
119 | **Or** first inject all test files into `karma.conf.js` with:
120 |
121 | ```bash
122 | gulp karma-conf
123 | ```
124 |
125 | Then you're able to run Karma directly. Example:
126 |
127 | ```bash
128 | karma start --single-run
129 | ```
130 |
131 | ## Production ready build - a.k.a. dist
132 |
133 | To make the app ready for deploy to production run:
134 |
135 | ```bash
136 | gulp dist
137 | ```
138 |
139 | Now there's a `./dist` folder with all scripts and stylesheets concatenated and minified, also third party libraries
140 | installed with bower will be concatenated and minified into `vendors.min.js` and `vendors.min.css` respectively.
141 |
142 | ### Running production ready build
143 |
144 | To start production ready build in the project run:
145 |
146 | ```bash
147 | gulp production
148 | ```
149 |
150 | ## Author
151 |
152 | Tarmo Leppänen
153 |
154 | ## License
155 |
156 | The MIT License (MIT)
157 |
158 | Copyright (c) 2015 Tarmo Leppänen
159 |
--------------------------------------------------------------------------------
/src/app/blocks/router/routerHelper.provider.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | /**
5 | * Specify provider blocks.router module
6 | *
7 | * @namespace Providers
8 | */
9 | angular
10 | .module('blocks.router')
11 | .provider('routerHelper', routerHelperProvider)
12 | ;
13 |
14 | /**
15 | * @desc Implementation for RouterHelperProvider to help configure the state-base ui.router
16 | * @namespace RouterHelperProvider
17 | * @memberOf Providers
18 | * @ngInject
19 | *
20 | * @param {$locationProvider} $locationProvider
21 | * @param {ng.ui.IStateProvider} $stateProvider
22 | * @param {angular.ui.IUrlRouterProvider} $urlRouterProvider
23 | */
24 | function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) {
25 | var _this = this;
26 |
27 | // Default config for routerHelper
28 | var config = {
29 | docTitle: undefined,
30 | resolveAlways: {}
31 | };
32 |
33 | // We want to use HTML5 mode with routing
34 | $locationProvider.html5Mode(true);
35 |
36 | // Specify configure method
37 | _this.configure = function configure(configOverride) {
38 | angular.extend(config, configOverride);
39 | };
40 |
41 | // Getter for provider service
42 | _this.$get = routerHelper;
43 |
44 | /**
45 | * @desc routerHelper service.
46 | * @namespace RouterHelper
47 | * @memberOf Providers
48 | * @ngInject
49 | *
50 | * @param {$location} $location
51 | * @param {ng.IRootScopeService|{title: string}} $rootScope
52 | * @param {ui.router.state.$state} $state
53 | * @param {Factories.Logger} logger
54 | * @returns {{
55 | * configureStates: Providers.RouterHelper.configureStates,
56 | * getStates: Providers.RouterHelper.getStates,
57 | * stateCounts: {
58 | * errors: number,
59 | * changes: number
60 | * }
61 | * }}
62 | * @constructor
63 | */
64 | function routerHelper(
65 | $location, $rootScope, $state,
66 | logger
67 | ) {
68 | // Initialize used default variables
69 | var handlingStateChangeError = false;
70 | var hasOtherwise = false;
71 | var stateCounts = {
72 | errors: 0,
73 | changes: 0
74 | };
75 |
76 | // Specify service methods
77 | var service = {
78 | configureStates: configureStates,
79 | getStates: getStates,
80 | stateCounts: stateCounts
81 | };
82 |
83 | // Initialize service
84 | _init();
85 |
86 | return service;
87 |
88 | ////////////////////
89 |
90 | /**
91 | * @name configureStates
92 | * @desc Implementation for configureStates method.
93 | * @memberOf Providers.RouterHelper
94 | *
95 | * @param {object[]} states
96 | * @param {string} [otherwisePath]
97 | */
98 | function configureStates(states, otherwisePath) {
99 | // Iterate specified states, add resolves to each one and attach state to router
100 | states.forEach(stateIterator);
101 |
102 | // Set otherwise path
103 | if (otherwisePath && !hasOtherwise) {
104 | hasOtherwise = true;
105 |
106 | $urlRouterProvider.otherwise(otherwisePath);
107 | }
108 |
109 | /**
110 | * State iterator helper function.
111 | *
112 | * @param {*} state
113 | */
114 | function stateIterator(state) {
115 | state.config.resolve = angular.extend(state.config.resolve || {}, config.resolveAlways);
116 |
117 | $stateProvider.state(state.state, state.config);
118 | }
119 | }
120 |
121 | /**
122 | * @name getStates
123 | * @desc Implementation for getStates method.
124 | * @memberOf Providers.RouterHelper
125 | */
126 | function getStates() {
127 | return $state.get();
128 | }
129 |
130 | //////////////////// Private functions for service
131 |
132 | /**
133 | * Service initialize method. This will activate state change error listener and updates current page title to
134 | * match with state.
135 | *
136 | * @private
137 | */
138 | function _init() {
139 | _handleRoutingErrors();
140 | _updateDocumentTitle();
141 | }
142 |
143 | /**
144 | * Route cancellation:
145 | * 1) On routing error, go to the default location (/).
146 | * 2) Provide an exit clause if it tries to do it twice.
147 | *
148 | * @private
149 | */
150 | function _handleRoutingErrors() {
151 | $rootScope.$on('$stateChangeError', onEvent);
152 |
153 | //noinspection JSUnusedLocalSymbols
154 |
155 | /**
156 | * Callback for $stateChangeError event.
157 | *
158 | * @param {object} event
159 | * @param {IState} toState
160 | * @param {object} toParams
161 | * @param {IState} fromState
162 | * @param {object} fromParams
163 | * @param {Error|string} error
164 | */
165 | function onEvent(event, toState, toParams, fromState, fromParams, error) {
166 | // Oh noes error is already activated
167 | if (handlingStateChangeError) {
168 | return;
169 | }
170 |
171 | stateCounts.errors++;
172 | handlingStateChangeError = true;
173 |
174 | // State requires authenticated user.
175 | if (error === 'AUTH_REQUIRED') {
176 | $state.go('auth.login');
177 |
178 | logger.error('Login required');
179 | } else { // Otherwise show error message and redirect user to root (/)
180 | var message = _getErrorMessage(error, toState);
181 |
182 | logger.warning(message, toState);
183 |
184 | $location.path('/');
185 | }
186 | }
187 | }
188 |
189 | /**
190 | * Method that will update current document title to match with state specification.
191 | *
192 | * @private
193 | */
194 | function _updateDocumentTitle() {
195 | $rootScope.$on('$stateChangeSuccess', onEvent);
196 |
197 | //noinspection JSUnusedLocalSymbols
198 |
199 | /**
200 | * Callback for $stateChangeSuccess event.
201 | *
202 | * @param {object} event
203 | * @param {IState|{title: string}} toState
204 | * @param {object} toParams
205 | * @param {IState} fromState
206 | * @param {object} fromParams
207 | * @param {Error} error
208 | */
209 | function onEvent(event, toState, toParams, fromState, fromParams, error) {
210 | stateCounts.changes++;
211 | handlingStateChangeError = false;
212 |
213 | // data bind to
214 | $rootScope.title = config.docTitle + ' ' + (toState.title || '');
215 | }
216 | }
217 |
218 | /**
219 | * Method to determine error message that is shown to user if router error happens.
220 | *
221 | * @param {object} error
222 | * @param {IState} toState
223 | * @returns {string}
224 | * @private
225 | */
226 | function _getErrorMessage(error, toState) {
227 | var destination = _getDestination(toState);
228 |
229 | return 'Error routing to ' + destination + '. ' +
230 | (error.data || '') + '. ' + (error.statusText || '') +
231 | ': ' + (error.status || '')
232 | ;
233 | }
234 |
235 | /**
236 | * Method to get toState destination name.
237 | *
238 | * @param {IState|{title: string, name: string, loadedTemplateUrl: string}} toState
239 | * @returns {*|string}
240 | * @private
241 | */
242 | function _getDestination(toState) {
243 | return (toState && (toState.title || toState.name || toState.loadedTemplateUrl)) || 'unknown target';
244 | }
245 | }
246 | }
247 | })();
248 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | /* jshint node: true */
2 | 'use strict';
3 |
4 | var gulp = require('gulp');
5 | var fs = require('fs');
6 | var g = require('gulp-load-plugins')({lazy: false});
7 | var ngConstant = require('gulp-ng-constant');
8 | var noop = g.util.noop;
9 | var es = require('event-stream');
10 | var queue = require('streamqueue');
11 | var lazypipe = require('lazypipe');
12 | var stylish = require('jshint-stylish');
13 | var bower = require('./bower');
14 | var historyApiFallback = require('connect-history-api-fallback');
15 | var isWatching = false;
16 |
17 | var htmlminOpts = {
18 | removeComments: true,
19 | collapseWhitespace: true,
20 | removeEmptyAttributes: false,
21 | collapseBooleanAttributes: true,
22 | removeRedundantAttributes: true
23 | };
24 |
25 | /**
26 | * JS Hint
27 | */
28 | gulp.task('jshint', function() {
29 | return gulp.src([
30 | './gulpfile.js',
31 | './src/app/**/*.js'
32 | ])
33 | .pipe(g.cached('jshint'))
34 | .pipe(jshint('./.jshintrc'))
35 | .pipe(livereload());
36 | });
37 |
38 | /**
39 | * CSS
40 | */
41 | gulp.task('clean-css', function() {
42 | return gulp.src('./.tmp/css').pipe(g.clean());
43 | });
44 |
45 | gulp.task('styles', ['clean-css'], function() {
46 | return gulp.src([
47 | './src/app/**/*.scss',
48 | '!./src/app/**/_*.scss'
49 | ])
50 | .pipe(g.sass())
51 | .pipe(gulp.dest('./.tmp/css/'))
52 | .pipe(g.cached('built-css'))
53 | .pipe(livereload());
54 | });
55 |
56 | gulp.task('styles-dist', ['styles'], function() {
57 | return cssFiles().pipe(dist('css', bower.name));
58 | });
59 |
60 | gulp.task('csslint', ['styles'], function() {
61 | return cssFiles()
62 | .pipe(g.cached('csslint'))
63 | .pipe(g.csslint('./.csslintrc'))
64 | .pipe(g.csslint.reporter())
65 | ;
66 | });
67 |
68 | /**
69 | * Scripts
70 | */
71 | gulp.task('scripts-dist', ['templates-dist'], function() {
72 | return appFiles().pipe(dist('js', bower.name, {ngAnnotate: true}));
73 | });
74 |
75 | /**
76 | * Templates
77 | */
78 | gulp.task('templates', function() {
79 | return templateFiles().pipe(buildTemplates());
80 | });
81 |
82 | gulp.task('templates-dist', function() {
83 | return templateFiles({min: true}).pipe(buildTemplates());
84 | });
85 |
86 | /**
87 | * Vendors
88 | */
89 | gulp.task('vendors', function() {
90 | var bowerStream = g.bowerFiles();
91 |
92 | return es.merge(
93 | bowerStream.pipe(g.filter('**/*.css')).pipe(dist('css', 'vendors')),
94 | bowerStream.pipe(g.filter('**/*.js')).pipe(dist('js', 'vendors'))
95 | );
96 | });
97 |
98 | /**
99 | * Index
100 | */
101 | gulp.task('index', index);
102 | gulp.task('build-all', ['styles', 'templates'], index);
103 |
104 | function index() {
105 | var opt = {read: false};
106 |
107 | return gulp.src('./src/app/index.html')
108 | .pipe(g.inject(g.bowerFiles(opt), {ignorePath: 'bower_components', starttag: ''}))
109 | .pipe(g.inject(es.merge(appFiles(), cssFiles(opt)), {ignorePath: ['.tmp', 'src/app']}))
110 | .pipe(g.embedlr())
111 | .pipe(gulp.dest('./.tmp/'))
112 | .pipe(livereload())
113 | ;
114 | }
115 |
116 | /**
117 | * Assets
118 | */
119 | gulp.task('assets', function() {
120 | return gulp.src('./src/app/assets/**')
121 | .pipe(gulp.dest('./dist/assets'))
122 | ;
123 | });
124 |
125 | /**
126 | * Partials
127 | */
128 | gulp.task('partials', function() {
129 | return gulp.src('./src/app/partials/**')
130 | .pipe(gulp.dest('./dist/partials'))
131 | ;
132 | });
133 |
134 | /**
135 | * Fonts
136 | */
137 | gulp.task('fonts', function() {
138 | return gulp.src('./bower_components/mdi/fonts/**')
139 | .pipe(gulp.dest('./dist/fonts'))
140 | ;
141 | });
142 |
143 | /**
144 | * Dist
145 | */
146 | gulp.task('dist', ['vendors', 'assets', 'fonts', 'styles-dist', 'scripts-dist'], function() {
147 | return gulp.src('./src/app/index.html')
148 | .pipe(g.inject(gulp.src('./dist/vendors.min.{js,css}'), {
149 | ignorePath: 'dist',
150 | starttag: ''
151 | }))
152 | .pipe(g.inject(gulp.src('./dist/' + bower.name + '.min.{js,css}'), {ignorePath: 'dist'}))
153 | .pipe(g.htmlmin(htmlminOpts))
154 | .pipe(gulp.dest('./dist/'))
155 | ;
156 | });
157 |
158 | /**
159 | * Static file server
160 | */
161 | gulp.task('statics', g.serve({
162 | port: 3002,
163 | root: ['./.tmp', './src/app', './bower_components'],
164 | middleware: historyApiFallback({})
165 | }));
166 |
167 | /**
168 | * Production file server, note remember to run 'gulp dist' first!
169 | */
170 | gulp.task('production', g.serve({
171 | port: 3000,
172 | root: ['./dist'],
173 | middleware: historyApiFallback({})
174 | }));
175 |
176 | /**
177 | * Watch
178 | */
179 | gulp.task('serve', ['config', 'watch']);
180 |
181 | gulp.task('watch', ['statics', 'default'], function() {
182 | isWatching = true;
183 |
184 | // Initiate livereload server:
185 | g.livereload({start: true});
186 |
187 | gulp.watch('./src/app/**/*.js', ['jshint']).on('change', function(evt) {
188 | if (evt.type !== 'changed') {
189 | gulp.start('index');
190 | }
191 | });
192 |
193 | gulp.watch('./src/app/index.html', ['index']);
194 | gulp.watch(['./src/app/**/*.html', '!./src/app/index.html'], ['templates']);
195 | gulp.watch(['./src/app/**/*.scss'], ['csslint']).on('change', function(evt) {
196 | if (evt.type !== 'changed') {
197 | gulp.start('index');
198 | }
199 | });
200 | });
201 |
202 | gulp.task('config', function() {
203 | gulp.src('./src/app/config/config.json')
204 | .pipe(ngConstant({
205 | name: 'firebaseDemo.config',
206 | templatePath: './src/app/config/template.ejs',
207 | space: ' '
208 | }))
209 | // Writes config.js to dist/ folder
210 | .pipe(gulp.dest('./src/app/config/'));
211 | });
212 |
213 | /**
214 | * Default task
215 | */
216 | gulp.task('default', ['lint', 'build-all']);
217 |
218 | /**
219 | * Lint everything
220 | */
221 | gulp.task('lint', ['jshint', 'csslint']);
222 |
223 | /**
224 | * Test
225 | */
226 | gulp.task('test', ['templates'], function() {
227 | return testFiles()
228 | .pipe(g.karma({
229 | configFile: 'karma.conf.js',
230 | action: 'run'
231 | }))
232 | ;
233 | });
234 |
235 | /**
236 | * Inject all files for tests into karma.conf.js
237 | * to be able to run `karma` without gulp.
238 | */
239 | gulp.task('karma-conf', ['templates'], function() {
240 | return gulp.src('./karma.conf.js')
241 | .pipe(g.inject(testFiles(), {
242 | starttag: 'files: [',
243 | endtag: ']',
244 | addRootSlash: false,
245 | transform: function(filepath, file, i, length) {
246 | return ' \'' + filepath + '\'' + (i + 1 < length ? ',' : '');
247 | }
248 | }))
249 | .pipe(gulp.dest('./'))
250 | ;
251 | });
252 |
253 | /**
254 | * Test files
255 | */
256 | function testFiles() {
257 | return new queue({objectMode: true})
258 | .queue(g.bowerFiles().pipe(g.filter('**/*.js')))
259 | .queue(gulp.src('./bower_components/angular-mocks/angular-mocks.js'))
260 | .queue(appFiles())
261 | .queue(gulp.src('./src/app/**/*.spec.js'))
262 | .done()
263 | ;
264 | }
265 |
266 | /**
267 | * All CSS files as a stream
268 | */
269 | function cssFiles(opt) {
270 | return gulp.src('./.tmp/css/**/*.css', opt);
271 | }
272 |
273 | /**
274 | * All AngularJS application files as a stream
275 | */
276 | function appFiles() {
277 | var files = [
278 | './.tmp/' + bower.name + '-templates.js',
279 | './src/app/**/*.js',
280 | '!./src/app/**/*.spec.js'
281 | ];
282 |
283 | return gulp.src(files)
284 | .pipe(g.angularFilesort())
285 | ;
286 | }
287 |
288 | /**
289 | * All AngularJS templates/partials as a stream
290 | */
291 | function templateFiles(opt) {
292 | return gulp.src(['./src/app/**/*.html', '!./src/app/index.html'], opt)
293 | .pipe(opt && opt.min ? g.htmlmin(htmlminOpts) : noop())
294 | ;
295 | }
296 |
297 | /**
298 | * Build AngularJS templates/partials
299 | */
300 | function buildTemplates() {
301 | return lazypipe()
302 | .pipe(g.ngHtml2js, {
303 | moduleName: bower.name + '-templates',
304 | prefix: '/' + bower.name + '/',
305 | stripPrefix: '/src/app'
306 | })
307 | .pipe(g.concat, bower.name + '-templates.js')
308 | .pipe(gulp.dest, './.tmp')
309 | .pipe(livereload)()
310 | ;
311 | }
312 |
313 | /**
314 | * Concat, rename, minify
315 | *
316 | * @param {String} ext
317 | * @param {String} name
318 | * @param {Object} opt
319 | */
320 | function dist(ext, name, opt) {
321 | opt = opt || {};
322 |
323 | return lazypipe()
324 | .pipe(g.concat, name + '.' + ext)
325 | .pipe(gulp.dest, './dist')
326 | .pipe(opt.ngAnnotate ? g.ngAnnotate : noop)
327 | .pipe(opt.ngAnnotate ? g.rename : noop, name + '.annotated.' + ext)
328 | .pipe(opt.ngAnnotate ? gulp.dest : noop, './dist')
329 | .pipe(ext === 'js' ? g.uglify : g.minifyCss)
330 | .pipe(g.rename, name + '.min.' + ext)
331 | .pipe(gulp.dest, './dist')()
332 | ;
333 | }
334 |
335 | /**
336 | * Livereload (or noop if not run by watch)
337 | */
338 | function livereload() {
339 | return lazypipe()
340 | .pipe(isWatching ? g.livereload : noop)()
341 | ;
342 | }
343 |
344 | /**
345 | * Jshint with stylish reporter
346 | */
347 | function jshint(jshintfile) {
348 | // Read JSHint settings, for some reason jshint-stylish won't work on initial load of files
349 | var jshintSettings = JSON.parse(fs.readFileSync(jshintfile, 'utf8'));
350 |
351 | return lazypipe()
352 | .pipe(g.jshint, jshintSettings)
353 | .pipe(g.jshint.reporter, stylish)()
354 | ;
355 | }
356 |
--------------------------------------------------------------------------------