├── mobile
├── .bowerrc
├── www
│ ├── css
│ │ └── style.css
│ ├── js
│ │ ├── controllers
│ │ │ ├── controllers-module.js
│ │ │ ├── get-started
│ │ │ │ ├── welcome-ctrl.js
│ │ │ │ ├── get-started-ctrl.js
│ │ │ │ ├── user-created-ctrl.js
│ │ │ │ └── create-profile-ctrl.js
│ │ │ ├── app-ctrl.js
│ │ │ └── login-ctrl.js
│ │ ├── services
│ │ │ ├── services-module.js
│ │ │ ├── api-service.js
│ │ │ ├── auth-interceptor-service.js
│ │ │ ├── storage-service.js
│ │ │ ├── user-service.js
│ │ │ └── auth-retry-service.js
│ │ ├── directives.js
│ │ └── app.js
│ ├── img
│ │ └── ionic.png
│ ├── lib
│ │ └── ionic
│ │ │ ├── fonts
│ │ │ ├── ionicons.eot
│ │ │ ├── ionicons.ttf
│ │ │ └── ionicons.woff
│ │ │ ├── version.json
│ │ │ ├── scss
│ │ │ ├── _progress.scss
│ │ │ ├── ionicons
│ │ │ │ ├── ionicons.scss
│ │ │ │ ├── _ionicons-font.scss
│ │ │ │ └── _ionicons-animation.scss
│ │ │ ├── _backdrop.scss
│ │ │ ├── ionic.scss
│ │ │ ├── _button-bar.scss
│ │ │ ├── _loading.scss
│ │ │ ├── _slide-box.scss
│ │ │ ├── _menu.scss
│ │ │ ├── _radio.scss
│ │ │ ├── _badge.scss
│ │ │ ├── _action-sheet.scss
│ │ │ ├── _modal.scss
│ │ │ ├── _popup.scss
│ │ │ ├── _list.scss
│ │ │ ├── _select.scss
│ │ │ ├── _range.scss
│ │ │ ├── _grid.scss
│ │ │ ├── _type.scss
│ │ │ ├── _platform.scss
│ │ │ ├── _popover.scss
│ │ │ ├── _toggle.scss
│ │ │ ├── _checkbox.scss
│ │ │ ├── _util.scss
│ │ │ ├── _form.scss
│ │ │ ├── _button.scss
│ │ │ ├── _reset.scss
│ │ │ ├── _scaffolding.scss
│ │ │ ├── _bar.scss
│ │ │ ├── _tabs.scss
│ │ │ └── _mixins.scss
│ │ │ └── js
│ │ │ ├── angular
│ │ │ ├── angular-resource.min.js
│ │ │ ├── angular-sanitize.min.js
│ │ │ └── angular-animate.min.js
│ │ │ └── angular-ui
│ │ │ └── angular-ui-router.min.js
│ ├── templates
│ │ ├── get-started
│ │ │ ├── welcome-view.html
│ │ │ ├── get-started-view.html
│ │ │ ├── user-created-view.html
│ │ │ └── create-profile-view.html
│ │ └── login-view.html
│ └── index.html
├── bower.json
├── .gitignore
├── ionic.project
├── package.json
├── config.xml
├── scss
│ └── ionic.app.scss
├── gulpfile.js
└── hooks
│ ├── after_prepare
│ └── 010_add_platform_class.js
│ └── README.md
├── server
├── .gitignore
├── util
│ ├── error-messages.js
│ └── application-auth
│ │ ├── auth-token.js
│ │ └── route-auth.js
├── api-manifest.js
├── gulpfile.js
├── api
│ └── user
│ │ └── base-user-api.js
├── package.json
├── conf.js
├── models
│ └── user-model.js
├── server.js
└── routes
│ └── user
│ └── base-user-route.js
└── README.md
/mobile/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "www/lib"
3 | }
4 |
--------------------------------------------------------------------------------
/mobile/www/css/style.css:
--------------------------------------------------------------------------------
1 | /* Empty. Add your own CSS if you like */
2 |
--------------------------------------------------------------------------------
/server/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore Files
2 |
3 | npm-debug.log
4 | node_modules/
--------------------------------------------------------------------------------
/mobile/www/js/controllers/controllers-module.js:
--------------------------------------------------------------------------------
1 | angular.module('base-auth.controllers',[]);
--------------------------------------------------------------------------------
/mobile/www/js/services/services-module.js:
--------------------------------------------------------------------------------
1 | angular.module("base-auth.services", ['http-auth-interceptor']);
--------------------------------------------------------------------------------
/mobile/www/img/ionic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sean-hill/ionic-user-auth-express/HEAD/mobile/www/img/ionic.png
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/fonts/ionicons.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sean-hill/ionic-user-auth-express/HEAD/mobile/www/lib/ionic/fonts/ionicons.eot
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/fonts/ionicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sean-hill/ionic-user-auth-express/HEAD/mobile/www/lib/ionic/fonts/ionicons.ttf
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/fonts/ionicons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sean-hill/ionic-user-auth-express/HEAD/mobile/www/lib/ionic/fonts/ionicons.woff
--------------------------------------------------------------------------------
/mobile/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "base-auth",
3 | "private": "true",
4 | "devDependencies": {
5 | "ionic": "driftyco/ionic-bower#1.0.0-beta.13"
6 | }
7 | }
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/version.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0-beta.13",
3 | "codename": "lanthanum-leopard",
4 | "date": "2014-09-24",
5 | "time": "20:17:36"
6 | }
7 |
--------------------------------------------------------------------------------
/mobile/.gitignore:
--------------------------------------------------------------------------------
1 | # Specifies intentionally untracked files to ignore when using Git
2 | # http://git-scm.com/docs/gitignore
3 |
4 | node_modules/
5 | platforms/
6 | plugins/
7 |
--------------------------------------------------------------------------------
/mobile/www/js/controllers/get-started/welcome-ctrl.js:
--------------------------------------------------------------------------------
1 | // Welcome Ctrl
2 |
3 | angular.module('base-auth.controllers')
4 |
5 | .controller('WelcomeCtrl', function($scope, User) {
6 | console.log('WelcomeCtrl');
7 | })
--------------------------------------------------------------------------------
/server/util/error-messages.js:
--------------------------------------------------------------------------------
1 | // A map of error messages
2 |
3 | module.exports = {
4 |
5 | unknown: "Uh ohs, something broke. Please let us know!"
6 | , required: "Please fill in all required fields"
7 |
8 | }
--------------------------------------------------------------------------------
/mobile/www/templates/get-started/welcome-view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Create Profile
4 |
5 |
--------------------------------------------------------------------------------
/mobile/ionic.project:
--------------------------------------------------------------------------------
1 | {
2 | "name": "base-auth",
3 | "app_id": "",
4 | "gulpStartupTasks": [
5 | "sass",
6 | "watch"
7 | ],
8 | "watchPatterns": [
9 | "www/**/*",
10 | "!www/lib/**/*"
11 | ]
12 | }
--------------------------------------------------------------------------------
/server/api-manifest.js:
--------------------------------------------------------------------------------
1 | // Api Manifest
2 |
3 | var express = require('express');
4 | var apiManifest = express.Router();
5 |
6 | apiManifest.use('/user', require('./api/user/base-user-api'));
7 |
8 | module.exports = apiManifest;
--------------------------------------------------------------------------------
/mobile/www/js/controllers/get-started/get-started-ctrl.js:
--------------------------------------------------------------------------------
1 | // Get Started Abstract Ctrl
2 |
3 | angular.module('base-auth.controllers')
4 |
5 | .controller('GetStartedCtrl', function($scope, User) {
6 | console.log('GetStartedCtrl');
7 | })
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_progress.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Progress
4 | * --------------------------------------------------
5 | */
6 |
7 | progress {
8 | display: block;
9 | margin: $progress-margin;
10 | width: $progress-width;
11 | }
12 |
--------------------------------------------------------------------------------
/mobile/www/js/directives.js:
--------------------------------------------------------------------------------
1 | // Directives
2 |
3 | angular.module("base-auth.directives", [])
4 |
5 | .directive('login', function(){
6 |
7 | return {
8 | restrict: 'E'
9 | , controller: 'LoginCtrl'
10 | };
11 |
12 | })
13 |
14 | ;
--------------------------------------------------------------------------------
/mobile/www/templates/get-started/get-started-view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Back
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/mobile/www/js/controllers/app-ctrl.js:
--------------------------------------------------------------------------------
1 | // Main App Ctrl
2 |
3 | angular.module('base-auth.controllers')
4 |
5 | .controller('AppCtrl', function($scope){
6 |
7 | console.log('AppCtrl');
8 |
9 | $scope.$on('user:set', function(event, user){
10 | $scope.user = user;
11 | });
12 |
13 | });
14 |
--------------------------------------------------------------------------------
/server/gulpfile.js:
--------------------------------------------------------------------------------
1 | // Gulp Tasks
2 |
3 | var gulp = require('gulp');
4 | var nodemon = require('gulp-nodemon');
5 |
6 | // Nodemon task
7 | gulp.task('nodemon', function(){
8 |
9 | nodemon({ script: 'server.js', ext: 'js', ignore: [] })
10 | .on('restart', function(){
11 | console.log('Server restarted');
12 | });
13 |
14 | });
15 |
16 | // Default task
17 | gulp.task('default', ['nodemon']);
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/ionicons/ionicons.scss:
--------------------------------------------------------------------------------
1 | @import "ionicons-variables";
2 | /*!
3 | Ionicons, v1.5.2
4 | Created by Ben Sperry for the Ionic Framework, http://ionicons.com/
5 | https://twitter.com/benjsperry https://twitter.com/ionicframework
6 | MIT License: https://github.com/driftyco/ionicons
7 | */
8 |
9 | @import "ionicons-font";
10 | @import "ionicons-animation";
11 | @import "ionicons-icons";
12 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_backdrop.scss:
--------------------------------------------------------------------------------
1 |
2 | .backdrop {
3 | position: fixed;
4 | top: 0;
5 | left: 0;
6 | z-index: $z-index-backdrop;
7 |
8 | width: 100%;
9 | height: 100%;
10 |
11 | background-color: rgba(0,0,0,0.4);
12 |
13 | visibility: hidden;
14 | opacity: 0;
15 |
16 | &.visible {
17 | visibility: visible;
18 | }
19 | &.active {
20 | opacity: 1;
21 | }
22 |
23 | @include transition(0.1s opacity linear);
24 | }
25 |
--------------------------------------------------------------------------------
/server/util/application-auth/auth-token.js:
--------------------------------------------------------------------------------
1 | // Auth token util
2 |
3 | var uuid = require('node-uuid');
4 | var jwt = require('jwt-simple');
5 | var jwtKey = 'Doo ts doo ts dance party.';
6 |
7 | exports.create = function(email, userId) {
8 |
9 | var userObject = {
10 | email: email,
11 | userId: userId,
12 | issuedAt: new Date(),
13 | entropy: uuid.v4()
14 | };
15 |
16 | return jwt.encode(userObject, jwtKey);
17 | }
18 |
--------------------------------------------------------------------------------
/server/api/user/base-user-api.js:
--------------------------------------------------------------------------------
1 | // User Api
2 |
3 | var express = require('express');
4 | var userApi = express.Router();
5 | var userRoute = require('../../routes/user/base-user-route');
6 | var RouteAuth = require('../../util/application-auth/route-auth');
7 |
8 | // Base user api routes
9 | userApi.post('/create', userRoute.create);
10 | userApi.post('/login', userRoute.login);
11 | userApi.get('/get', RouteAuth.protect, userRoute.get);
12 |
13 | module.exports = userApi;
--------------------------------------------------------------------------------
/mobile/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "base-auth",
3 | "version": "1.0.0",
4 | "description": "Simple Auth for Ionic",
5 | "dependencies": {
6 | "gulp": "^3.5.6",
7 | "gulp-sass": "^0.7.1",
8 | "gulp-concat": "^2.2.0",
9 | "gulp-minify-css": "^0.3.0",
10 | "gulp-rename": "^1.2.0"
11 | },
12 | "devDependencies": {
13 | "bower": "^1.3.3",
14 | "gulp-concat": "^2.4.2",
15 | "gulp-util": "^2.2.14",
16 | "shelljs": "^0.3.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/mobile/www/js/services/api-service.js:
--------------------------------------------------------------------------------
1 | // Constants for the API
2 |
3 | angular.module('base-auth.services')
4 |
5 | .factory('API', function(){
6 |
7 | // Url of Node API
8 | var apiUrl = 'http://localhost:5000/api';
9 |
10 | // Base Routes
11 | var baseUserRoute = apiUrl + '/user';
12 |
13 | return {
14 |
15 | // User Routes
16 | user: {
17 | get: baseUserRoute + '/get'
18 | , create: baseUserRoute + '/create'
19 | , login: baseUserRoute +'/login'
20 | }
21 |
22 | }
23 |
24 | });
--------------------------------------------------------------------------------
/mobile/www/templates/get-started/user-created-view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | User has been created
4 |
5 | {{ user | json }}
6 |
7 |
8 |
--------------------------------------------------------------------------------
/mobile/www/js/controllers/get-started/user-created-ctrl.js:
--------------------------------------------------------------------------------
1 | // User Created Ctrl
2 |
3 | angular.module('base-auth.controllers')
4 |
5 | .controller('UserCreatedCtrl', function($scope, $timeout, User, Storage) {
6 | console.log('UserCreatedCtrl');
7 |
8 | $scope.clickToGetUser = function() {
9 |
10 | User.get().then(
11 | function(user){
12 | $scope.$emit('user:set', user);
13 | }, function(err){
14 | console.log(err);
15 | })
16 | ;
17 |
18 | };
19 |
20 | $scope.clearAuthAndGetUser = function() {
21 |
22 | Storage.clear('authToken');
23 |
24 | $scope.clickToGetUser();
25 |
26 | };
27 |
28 | })
--------------------------------------------------------------------------------
/mobile/www/js/services/auth-interceptor-service.js:
--------------------------------------------------------------------------------
1 | // Auth token interceptor (appends the Authorization header to every request)
2 |
3 | angular.module('base-auth.services')
4 |
5 | .factory('AuthTokenInterceptor', function ($q, Storage) {
6 | return {
7 | request: function (config) {
8 |
9 | var authToken = Storage.get("authToken");
10 | config.headers = config.headers || {};
11 |
12 | if (authToken) {
13 | config.headers.Authorization = 'Bearer ' + authToken;
14 | }
15 |
16 | return config || $q.when(config);
17 |
18 | }
19 | };
20 | })
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "base-auth",
3 | "version": "0.1.0",
4 | "dependencies": {
5 | "body-parser": "^1.9.3",
6 | "cheerio": "^0.18.0",
7 | "cookie-parser": "^1.3.3",
8 | "crypto": "0.0.3",
9 | "express": "^4.10.3",
10 | "gulp": "^3.8.10",
11 | "gulp-jshint": "^1.9.0",
12 | "gulp-nodemon": "^1.0.4",
13 | "jwt-simple": "^0.2.0",
14 | "mongoose": "^3.8.19",
15 | "mongoose-unique-validator": "^0.4.1",
16 | "node-uuid": "^1.4.1",
17 | "passport": "^0.2.1",
18 | "passport-http-bearer": "^1.0.1",
19 | "request": "^2.48.0",
20 | "spooky": "^0.2.5",
21 | "validator": "^3.22.1"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/mobile/www/js/controllers/get-started/create-profile-ctrl.js:
--------------------------------------------------------------------------------
1 | // Create Profile Ctrl
2 |
3 | angular.module('base-auth.controllers')
4 |
5 | .controller('CreateProfileCtrl', function($scope, $state, $ionicPopup, User, Storage){
6 |
7 | console.log('CreateProfileCtrl');
8 | $scope.profileForm = {};
9 |
10 | $scope.createProfile = function(){
11 |
12 | User.create($scope.profileForm).then(
13 | function(createdUser){
14 | Storage.set('authToken', createdUser.authToken);
15 | $state.go('get-started.user-created');
16 | },
17 | function(err){
18 | $ionicPopup.alert({
19 | title: 'Signup Error',
20 | template: err.message
21 | });
22 | });
23 |
24 | };
25 |
26 | });
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/ionic.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | @import
4 | // Ionicons
5 | "ionicons/ionicons.scss",
6 |
7 | // Variables
8 | "mixins",
9 | "variables",
10 |
11 | // Base
12 | "reset",
13 | "scaffolding",
14 | "type",
15 |
16 | // Components
17 | "action-sheet",
18 | "backdrop",
19 | "bar",
20 | "tabs",
21 | "menu",
22 | "modal",
23 | "popover",
24 | "popup",
25 | "loading",
26 | "items",
27 | "list",
28 | "badge",
29 | "slide-box",
30 |
31 | // Forms
32 | "form",
33 | "checkbox",
34 | "toggle",
35 | "radio",
36 | "range",
37 | "select",
38 | "progress",
39 |
40 | // Buttons
41 | "button",
42 | "button-bar",
43 |
44 | // Util
45 | "animations",
46 | "grid",
47 | "util",
48 | "platform";
49 |
--------------------------------------------------------------------------------
/server/util/application-auth/route-auth.js:
--------------------------------------------------------------------------------
1 | // Used when an auth token is required for a route
2 |
3 | var passport = require("passport");
4 | var path = require('path');
5 | var BearerStrategy = require("passport-http-bearer").Strategy;
6 | var User = require(path.resolve(__dirname, '../../models/user-model'));
7 |
8 | // Use Bearer Strategy as authentication
9 | passport.use(new BearerStrategy(
10 | function(token, done) {
11 | User.findOne({ authToken: token }, function (err, user) {
12 | if (err) { return done(err); }
13 | if (!user) { return done(null, false); }
14 | return done(null, user, { scope: 'all' });
15 | });
16 | }
17 | ));
18 |
19 | module.exports = {
20 | protect: passport.authenticate('bearer', { session: false })
21 | }
--------------------------------------------------------------------------------
/mobile/www/templates/login-view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Login
4 |
5 |
6 |
21 |
22 |
--------------------------------------------------------------------------------
/mobile/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Unbill
4 |
5 | An Ionic Framework and Cordova project.
6 |
7 |
8 | Ionic Framework Team
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/mobile/www/js/services/storage-service.js:
--------------------------------------------------------------------------------
1 | // Service to use local storage
2 |
3 | angular.module('base-auth.services')
4 |
5 | .factory('Storage', function() {
6 | return {
7 | get: function(key, toJson) {
8 | var value = window.localStorage[key];
9 | return toJson ? angular.fromJson(value) : value;
10 | },
11 | set: function(key, value) {
12 | window.localStorage[key] = typeof value == "object" ? angular.toJson(value) : value;
13 | },
14 | has: function(key) {
15 | return window.localStorage[key] != undefined ? true : false;
16 | },
17 | clear: function(keys) {
18 | var keysToClear = keys.split(" ");
19 | angular.forEach(keysToClear, function(key){
20 | window.localStorage.removeItem(key);
21 | });
22 | }
23 | }
24 | })
--------------------------------------------------------------------------------
/mobile/scss/ionic.app.scss:
--------------------------------------------------------------------------------
1 | /*
2 | To customize the look and feel of Ionic, you can override the variables
3 | in ionic's _variables.scss file.
4 |
5 | For example, you might change some of the default colors:
6 |
7 | $light: #fff !default;
8 | $stable: #f8f8f8 !default;
9 | $positive: #387ef5 !default;
10 | $calm: #11c1f3 !default;
11 | $balanced: #33cd5f !default;
12 | $energized: #ffc900 !default;
13 | $assertive: #ef473a !default;
14 | $royal: #886aea !default;
15 | $dark: #444 !default;
16 | */
17 |
18 | // The path for our ionicons font files, relative to the built CSS in www/css
19 | $ionicons-font-path: "../lib/ionic/fonts" !default;
20 |
21 | // Include all of Ionic
22 | @import "www/lib/ionic/scss/ionic";
23 |
24 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/ionicons/_ionicons-font.scss:
--------------------------------------------------------------------------------
1 | // Ionicons Font Path
2 | // --------------------------
3 |
4 | @font-face {
5 | font-family: $ionicons-font-family;
6 | src:url("#{$ionicons-font-path}/ionicons.eot?v=#{$ionicons-version}");
7 | src:url("#{$ionicons-font-path}/ionicons.eot?v=#{$ionicons-version}#iefix") format("embedded-opentype"),
8 | url("#{$ionicons-font-path}/ionicons.ttf?v=#{$ionicons-version}") format("truetype"),
9 | url("#{$ionicons-font-path}/ionicons.woff?v=#{$ionicons-version}") format("woff"),
10 | url("#{$ionicons-font-path}/ionicons.svg?v=#{$ionicons-version}#Ionicons") format("svg");
11 | font-weight: normal;
12 | font-style: normal;
13 | }
14 |
15 | .ion {
16 | display: inline-block;
17 | font-family: $ionicons-font-family;
18 | speak: none;
19 | font-style: normal;
20 | font-weight: normal;
21 | font-variant: normal;
22 | text-transform: none;
23 | text-rendering: auto;
24 | line-height: 1;
25 | -webkit-font-smoothing: antialiased;
26 | -moz-osx-font-smoothing: grayscale;
27 | }
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_button-bar.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Button Bar
4 | * --------------------------------------------------
5 | */
6 |
7 | .button-bar {
8 | @include display-flex();
9 | @include flex(1);
10 | width: 100%;
11 |
12 | &.button-bar-inline {
13 | display: block;
14 | width: auto;
15 |
16 | @include clearfix();
17 |
18 | > .button {
19 | width: auto;
20 | display: inline-block;
21 | float: left;
22 | }
23 | }
24 | }
25 |
26 | .button-bar > .button {
27 | @include flex(1);
28 | display: block;
29 |
30 | overflow: hidden;
31 |
32 | padding: 0 16px;
33 |
34 | width: 0;
35 |
36 | border-width: 1px 0px 1px 1px;
37 | border-radius: 0;
38 | text-align: center;
39 | text-overflow: ellipsis;
40 | white-space: nowrap;
41 |
42 | &:before,
43 | .icon:before {
44 | line-height: 44px;
45 | }
46 |
47 | &:first-child {
48 | border-radius: 2px 0px 0px 2px;
49 | }
50 | &:last-child {
51 | border-right-width: 1px;
52 | border-radius: 0px 2px 2px 0px;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_loading.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Loading
4 | * --------------------------------------------------
5 | */
6 |
7 | .loading-container {
8 | position: absolute;
9 | left: 0;
10 | top: 0;
11 | right: 0;
12 | bottom: 0;
13 |
14 | z-index: $z-index-loading;
15 |
16 | @include display-flex();
17 | @include justify-content(center);
18 | @include align-items(center);
19 |
20 | @include transition(0.2s opacity linear);
21 | visibility: hidden;
22 | opacity: 0;
23 |
24 | &:not(.visible) .icon {
25 | display: none;
26 | }
27 | &.visible {
28 | visibility: visible;
29 | }
30 | &.active {
31 | opacity: 1;
32 | }
33 |
34 | .loading {
35 | padding: $loading-padding;
36 |
37 | border-radius: $loading-border-radius;
38 | background-color: $loading-bg-color;
39 |
40 | color: $loading-text-color;
41 |
42 | text-align: center;
43 | text-overflow: ellipsis;
44 | font-size: $loading-font-size;
45 |
46 | h1, h2, h3, h4, h5, h6 {
47 | color: $loading-text-color;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_slide-box.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Slide Box
4 | * --------------------------------------------------
5 | */
6 |
7 | .slider {
8 | position: relative;
9 | visibility: hidden;
10 | // Make sure items don't scroll over ever
11 | overflow: hidden;
12 | }
13 |
14 | .slider-slides {
15 | position: relative;
16 | height: 100%;
17 | }
18 |
19 | .slider-slide {
20 | position: relative;
21 | display: block;
22 | float: left;
23 | width: 100%;
24 | height: 100%;
25 | vertical-align: top;
26 | }
27 |
28 | .slider-slide-image {
29 | > img {
30 | width: 100%;
31 | }
32 | }
33 |
34 | .slider-pager {
35 | position: absolute;
36 | bottom: 20px;
37 | z-index: $z-index-slider-pager;
38 | width: 100%;
39 | height: 15px;
40 | text-align: center;
41 |
42 | .slider-pager-page {
43 | display: inline-block;
44 | margin: 0px 3px;
45 | width: 15px;
46 | color: #000;
47 | text-decoration: none;
48 |
49 | opacity: 0.3;
50 |
51 | &.active {
52 | @include transition(opacity 0.4s ease-in);
53 | opacity: 1;
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/server/conf.js:
--------------------------------------------------------------------------------
1 | // Server Configuration
2 |
3 | var conf = {};
4 |
5 | // Shared configuration between dev and production
6 | conf.joe = "HEY THERE";
7 |
8 | // Development configuration
9 | if (process.env.NODE_ENV === 'development') {
10 |
11 | conf.mongo_config = {
12 | 'dbname' : 'base-auth-db',
13 | 'host' : 'localhost',
14 | 'port' : 27017,
15 | 'auth' : {
16 | 'name': 'XXXXXXXX',
17 | 'pass': 'XXXXXXXX'
18 | },
19 | connect_string: function(){
20 | return 'mongodb://' + this.auth.name + ':' + this.auth.pass + '@' + this.host + ':' + this.port + '/' + this.dbname;
21 | }
22 | };
23 |
24 | }
25 |
26 | // Production configuration
27 | if (process.env.NODE_ENV === 'production') {
28 |
29 | conf.mongo_config = {
30 | 'dbname' : 'base-auth-prod',
31 | 'host' : 'some.prod.db',
32 | 'port' : 12345,
33 | 'auth' : {
34 | 'name': 'XXXXXXXX',
35 | 'pass': 'XXXXXXXX'
36 | },
37 | connect_string: function(){
38 | return 'mongodb://' + this.auth.name + ':' + this.auth.pass + '@' + this.host + ':' + this.port + '/' + this.dbname;
39 | }
40 | };
41 |
42 | }
43 |
44 | module.exports = conf;
--------------------------------------------------------------------------------
/mobile/www/templates/get-started/create-profile-view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
26 |
27 |
--------------------------------------------------------------------------------
/mobile/www/js/controllers/login-ctrl.js:
--------------------------------------------------------------------------------
1 | // Login Ctrl
2 |
3 | angular.module('base-auth.controllers')
4 |
5 | .controller('LoginCtrl', function($scope, $ionicModal, $ionicPopup, AuthService, User, Storage){
6 |
7 | console.log('LoginCtrl');
8 |
9 | $scope.loginForm = {};
10 |
11 | // Load the Login Modal
12 | $ionicModal.fromTemplateUrl('templates/login-view.html', {
13 | scope: $scope,
14 | }).then(function(modal) {
15 | $scope.modal = modal;
16 | });
17 |
18 | // Remove the Modal when this scope is destroyed
19 | $scope.$on('$destroy', function() {
20 | $scope.modal.remove();
21 | });
22 |
23 | // Show the Modal if the auth-login-required function is fired
24 | $scope.$on('auth:login-required', function(){
25 | console.log('Login required!');
26 | $scope.modal.show();
27 | });
28 |
29 | $scope.login = function() {
30 |
31 | console.log($scope.loginForm);
32 |
33 | User.login($scope.loginForm).then(
34 | function(response) {
35 | Storage.set('authToken', response.authToken);
36 | AuthService.loginConfirmed();
37 | $scope.modal.hide();
38 | },
39 | function(err) {
40 | $ionicPopup.alert({
41 | title: 'Login Error',
42 | template: err.message
43 | });
44 | })
45 | ;
46 |
47 | };
48 |
49 | });
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_menu.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Menus
4 | * --------------------------------------------------
5 | * Side panel structure
6 | */
7 |
8 | .menu {
9 | position: absolute;
10 | top: 0;
11 | bottom: 0;
12 | z-index: $z-index-menu;
13 | overflow: hidden;
14 |
15 | min-height: 100%;
16 | max-height: 100%;
17 | width: $menu-width;
18 |
19 | background-color: $menu-bg;
20 |
21 | .scroll-content {
22 | z-index: $z-index-menu-scroll-content;
23 | }
24 |
25 | .bar-header {
26 | z-index: $z-index-menu-bar-header;
27 | }
28 | }
29 |
30 | .menu-content {
31 | @include transform(none);
32 | box-shadow: $menu-side-shadow;
33 | }
34 |
35 | .menu-open .menu-content .pane,
36 | .menu-open .menu-content .scroll-content {
37 | pointer-events: none;
38 | }
39 |
40 | .grade-b .menu-content,
41 | .grade-c .menu-content {
42 | @include box-sizing(content-box);
43 | right: -1px;
44 | left: -1px;
45 | border-right: 1px solid #ccc;
46 | border-left: 1px solid #ccc;
47 | box-shadow: none;
48 | }
49 |
50 | .menu-left {
51 | left: 0;
52 | }
53 |
54 | .menu-right {
55 | right: 0;
56 | }
57 |
58 | .aside-open.aside-resizing .menu-right {
59 | display: none;
60 | }
61 |
62 | .menu-animated {
63 | @include transition-transform($menu-animation-speed ease);
64 | }
65 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_radio.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Radio Button Inputs
4 | * --------------------------------------------------
5 | */
6 |
7 | .item-radio {
8 | padding: 0;
9 |
10 | &:hover {
11 | cursor: pointer;
12 | }
13 | }
14 |
15 | .item-radio .item-content {
16 | /* give some room to the right for the checkmark icon */
17 | padding-right: $item-padding * 4;
18 | }
19 |
20 | .item-radio .radio-icon {
21 | /* checkmark icon will be hidden by default */
22 | position: absolute;
23 | top: 0;
24 | right: 0;
25 | z-index: $z-index-item-radio;
26 | visibility: hidden;
27 | padding: $item-padding - 2;
28 | height: 100%;
29 | font-size: 24px;
30 | }
31 |
32 | .item-radio input {
33 | /* hide any radio button inputs elements (the ugly circles) */
34 | position: absolute;
35 | left: -9999px;
36 |
37 | &:checked ~ .item-content {
38 | /* style the item content when its checked */
39 | background: #f7f7f7;
40 | }
41 |
42 | &:checked ~ .radio-icon {
43 | /* show the checkmark icon when its checked */
44 | visibility: visible;
45 | }
46 | }
47 |
48 | // Hack for Android to correctly display the checked item
49 | // http://timpietrusky.com/advanced-checkbox-hack
50 | .platform-android.grade-b .item-radio,
51 | .platform-android.grade-c .item-radio {
52 | -webkit-animation: androidCheckedbugfix infinite 1s;
53 | }
54 | @-webkit-keyframes androidCheckedbugfix {
55 | from { padding: 0; }
56 | to { padding: 0; }
57 | }
58 |
--------------------------------------------------------------------------------
/mobile/www/js/services/user-service.js:
--------------------------------------------------------------------------------
1 | // User Service
2 |
3 | angular.module('base-auth.services')
4 |
5 | .factory('User', function($q, $http, API) {
6 |
7 | return {
8 |
9 | get: function() {
10 |
11 | var deferred = $q.defer();
12 |
13 | $http.get(API.user.get)
14 | .success(function(user){
15 | deferred.resolve(user);
16 | })
17 | .error(function(){
18 | deferred.reject('You are not authenticated.');
19 | })
20 | ;
21 |
22 | return deferred.promise;
23 |
24 | },
25 |
26 | login: function(loginForm) {
27 |
28 | var deferred = $q.defer();
29 |
30 | $http.post(API.user.login, loginForm)
31 | .success(function(user){
32 | deferred.resolve(user);
33 | })
34 | .error(function(err){
35 | deferred.reject(err);
36 | })
37 | ;
38 |
39 | return deferred.promise;
40 |
41 | },
42 |
43 | create: function(profileForm) {
44 |
45 | var deferred = $q.defer();
46 |
47 | $http.post(API.user.create, profileForm)
48 | .success(function(createdUser){
49 | deferred.resolve(createdUser);
50 | })
51 | .error(function(err){
52 | deferred.reject(err);
53 | })
54 | ;
55 |
56 | return deferred.promise;
57 |
58 | }
59 |
60 | }
61 |
62 | })
63 |
64 | ;
65 |
--------------------------------------------------------------------------------
/mobile/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var gutil = require('gulp-util');
3 | var bower = require('bower');
4 | var concat = require('gulp-concat');
5 | var sass = require('gulp-sass');
6 | var minifyCss = require('gulp-minify-css');
7 | var rename = require('gulp-rename');
8 | var sh = require('shelljs');
9 |
10 | var paths = {
11 | sass: ['./scss/**/*.scss']
12 | };
13 |
14 | gulp.task('default', ['sass']);
15 |
16 | gulp.task('sass', function(done) {
17 | gulp.src('./scss/ionic.app.scss')
18 | .pipe(sass())
19 | .pipe(gulp.dest('./www/css/'))
20 | .pipe(minifyCss({
21 | keepSpecialComments: 0
22 | }))
23 | .pipe(rename({ extname: '.min.css' }))
24 | .pipe(gulp.dest('./www/css/'))
25 | .on('end', done);
26 | });
27 |
28 | gulp.task('watch', function() {
29 | gulp.watch(paths.sass, ['sass']);
30 | });
31 |
32 | gulp.task('install', ['git-check'], function() {
33 | return bower.commands.install()
34 | .on('log', function(data) {
35 | gutil.log('bower', gutil.colors.cyan(data.id), data.message);
36 | });
37 | });
38 |
39 | gulp.task('git-check', function(done) {
40 | if (!sh.which('git')) {
41 | console.log(
42 | ' ' + gutil.colors.red('Git is not installed.'),
43 | '\n Git, the version control system, is required to download Ionic.',
44 | '\n Download git here:', gutil.colors.cyan('http://git-scm.com/downloads') + '.',
45 | '\n Once git is installed, run \'' + gutil.colors.cyan('gulp install') + '\' again.'
46 | );
47 | process.exit(1);
48 | }
49 | done();
50 | });
51 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_badge.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Badges
4 | * --------------------------------------------------
5 | */
6 |
7 | .badge {
8 | @include badge-style($badge-default-bg, $badge-default-text);
9 | z-index: $z-index-badge;
10 | display: inline-block;
11 | padding: 3px 8px;
12 | min-width: 10px;
13 | border-radius: $badge-border-radius;
14 | vertical-align: baseline;
15 | text-align: center;
16 | white-space: nowrap;
17 | font-weight: $badge-font-weight;
18 | font-size: $badge-font-size;
19 | line-height: $badge-line-height;
20 |
21 | &:empty {
22 | display: none;
23 | }
24 | }
25 |
26 | //Be sure to override specificity of rule that 'badge color matches tab color by default'
27 | .tabs .tab-item .badge,
28 | .badge {
29 | &.badge-light {
30 | @include badge-style($badge-light-bg, $badge-light-text);
31 | }
32 | &.badge-stable {
33 | @include badge-style($badge-stable-bg, $badge-stable-text);
34 | }
35 | &.badge-positive {
36 | @include badge-style($badge-positive-bg, $badge-positive-text);
37 | }
38 | &.badge-calm {
39 | @include badge-style($badge-calm-bg, $badge-calm-text);
40 | }
41 | &.badge-assertive {
42 | @include badge-style($badge-assertive-bg, $badge-assertive-text);
43 | }
44 | &.badge-balanced {
45 | @include badge-style($badge-balanced-bg, $badge-balanced-text);
46 | }
47 | &.badge-energized {
48 | @include badge-style($badge-energized-bg, $badge-energized-text);
49 | }
50 | &.badge-royal {
51 | @include badge-style($badge-royal-bg, $badge-royal-text);
52 | }
53 | &.badge-dark {
54 | @include badge-style($badge-dark-bg, $badge-dark-text);
55 | }
56 | }
57 |
58 | // Quick fix for labels/badges in buttons
59 | .button .badge {
60 | position: relative;
61 | top: -1px;
62 | }
63 |
--------------------------------------------------------------------------------
/server/models/user-model.js:
--------------------------------------------------------------------------------
1 | // User Model
2 |
3 | var mongoose = require('mongoose');
4 | var crypto = require('crypto');
5 | var uniqueValidator = require('mongoose-unique-validator');
6 | var Schema = mongoose.Schema;
7 |
8 | var UserSchema = new Schema({
9 |
10 | email: { type: String, lowercase: true, trim: true, index: true, required: true, unique: true }
11 |
12 | , hash: String
13 |
14 | , salt: String
15 |
16 | , name: { first: String, last: String }
17 |
18 | , authToken: { type: String, required: true }
19 |
20 | , utilityProviders: []
21 |
22 | });
23 |
24 | // Virtual method for getting a full name
25 | UserSchema.virtual('name.full').get(function () {
26 | return this.name.first + ' ' + this.name.last;
27 | });
28 |
29 | // Virtual method for password getting and setting
30 | UserSchema.virtual('password')
31 | .get(function() {
32 | return this.hash;
33 | })
34 | .set(function(password) {
35 | this.salt = this.makeSalt();
36 | this.hash = this.encryptPassword(password);
37 | })
38 | ;
39 |
40 | // Schema methods
41 | UserSchema.methods = {
42 |
43 | authenticate: function(plainText) {
44 | return this.encryptPassword(plainText) === this.password;
45 | },
46 |
47 | makeSalt: function() {
48 | return crypto.randomBytes(16).toString('base64');
49 | },
50 |
51 | encryptPassword: function(password) {
52 | if (!password || !this.salt) return '';
53 | var salt = new Buffer(this.salt, 'base64');
54 | return crypto.pbkdf2Sync(password, salt, 10000, 64).toString('base64');
55 | }
56 |
57 | };
58 |
59 | // Unique field plugin (makes better error messages)
60 | UserSchema.plugin(uniqueValidator, { message: 'Sorry, someone is already using {VALUE} for their {PATH}.'})
61 |
62 | module.exports = mongoose.model('User', UserSchema);
--------------------------------------------------------------------------------
/mobile/www/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 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | // API Server
2 |
3 | if (!process.env.NODE_ENV) {
4 | process.env.NODE_ENV = 'development'
5 | }
6 |
7 | process.on("uncaughtException", function(error, stack){
8 |
9 | console.log("Uncaught Error");
10 | console.log(error.stack);
11 |
12 | });
13 |
14 |
15 |
16 | // Server Variables
17 | var express = require('express');
18 | var cookieParser = require('cookie-parser');
19 | var bodyParser = require('body-parser');
20 | var Conf = require('./conf');
21 | var app = express();
22 | var port = process.env.PORT || 5000;
23 |
24 |
25 |
26 | // Middlewares
27 | app.use(bodyParser.urlencoded({ extended: true }));
28 | app.use(bodyParser.json());
29 | app.use(express.static(__dirname + '/public'));
30 | app.use(cookieParser());
31 |
32 |
33 |
34 | // Database connection
35 | var mongoose = require('mongoose');
36 | mongoose.connect(Conf.mongo_config.connect_string(), { db: { safe:true } }, function(err){
37 | if (err) {
38 | throw err;
39 | }
40 | else {
41 | console.log('Mongoose Connected');
42 | }
43 | });
44 |
45 | mongoose.connection.on("error", function(err){
46 | console.log("Mongoose error:", err);
47 | });
48 |
49 | mongoose.connection.on("disconnected", function(){
50 | console.log("Mongoose disconnected!");
51 | });
52 |
53 | mongoose.set('debug', true);
54 |
55 |
56 |
57 | // Server configuration
58 | // Development only
59 | if ('development' == app.get('env')) {
60 | console.log('STATE: Development')
61 | mongoose.set('debug', true);
62 | }
63 |
64 | // Production only
65 | if ('production' == app.get('env')) {
66 | console.log('STATE: Production')
67 | }
68 |
69 |
70 |
71 | // Api Middlewares
72 | app.use('/api', require('./api-manifest'));
73 |
74 | app.get('/', function(req, res){
75 | res.send('base-auth');
76 | });
77 |
78 |
79 |
80 | // Start the server listening
81 | app.listen(port, function(){
82 | console.log('base-auth listening on port', port);
83 | });
84 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_action-sheet.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Action Sheets
3 | * --------------------------------------------------
4 | */
5 |
6 | .action-sheet-backdrop {
7 | @include transition(background-color 300ms ease-in-out);
8 | position: fixed;
9 | top: 0;
10 | left: 0;
11 | z-index: $z-index-action-sheet;
12 | width: 100%;
13 | height: 100%;
14 | background-color: rgba(0,0,0,0);
15 |
16 | &.active {
17 | background-color: rgba(0,0,0,0.5);
18 | }
19 | }
20 |
21 | .action-sheet-wrapper {
22 | @include translate3d(0, 100%, 0);
23 | @include transition(all ease-in-out 300ms);
24 | position: absolute;
25 | bottom: 0;
26 | width: 100%;
27 | }
28 |
29 | .action-sheet-up {
30 | @include translate3d(0, 0, 0);
31 | }
32 |
33 | .action-sheet {
34 | margin-left: 15px;
35 | margin-right: 15px;
36 | width: auto;
37 | z-index: $z-index-action-sheet;
38 | overflow: hidden;
39 |
40 | .button {
41 | display: block;
42 | padding: 1px;
43 | width: 100%;
44 | border-radius: 0;
45 |
46 | background-color: transparent;
47 |
48 | color: $positive;
49 | font-size: 18px;
50 |
51 | &.destructive {
52 | color: $assertive;
53 | }
54 | }
55 | }
56 |
57 | .action-sheet-title {
58 | padding: 10px;
59 | color: lighten($base-color, 40%);
60 | text-align: center;
61 | font-size: 12px;
62 | }
63 |
64 | .action-sheet-group {
65 | margin-bottom: 5px;
66 | border-radius: $sheet-border-radius;
67 | background-color: #fff;
68 | .button {
69 | border-width: 1px 0px 0px 0px;
70 | border-radius: 0;
71 |
72 | &.active {
73 | background-color: transparent;
74 | color: inherit;
75 | }
76 | }
77 | .button:first-child:last-child {
78 | border-width: 0;
79 | }
80 | }
81 |
82 | .action-sheet-open {
83 | pointer-events: none;
84 |
85 | &.modal-open .modal {
86 | pointer-events: none;
87 | }
88 |
89 | .action-sheet-backdrop {
90 | pointer-events: auto;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/mobile/www/js/app.js:
--------------------------------------------------------------------------------
1 |
2 | // Main Application JS
3 |
4 | angular.module("base-auth", ["ionic", "base-auth.controllers", "base-auth.services", "base-auth.directives"])
5 |
6 | .run(function($ionicPlatform) {
7 |
8 | $ionicPlatform.ready(function() {
9 |
10 | // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
11 | // for form inputs)
12 | if(window.cordova && window.cordova.plugins.Keyboard) {
13 | cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
14 | }
15 | if(window.StatusBar) {
16 | // org.apache.cordova.statusbar required
17 | StatusBar.styleDefault();
18 | }
19 |
20 | });
21 |
22 | })
23 |
24 | .config(function($stateProvider, $urlRouterProvider, $httpProvider) {
25 |
26 | $stateProvider
27 |
28 | .state("get-started", {
29 | abstract: true,
30 | url: "/get-started",
31 | controller: "GetStartedCtrl",
32 | templateUrl: "templates/get-started/get-started-view.html"
33 | })
34 |
35 | .state("get-started.welcome", {
36 | url: "/welcome",
37 | views: {
38 | "get-started": {
39 | templateUrl: "templates/get-started/welcome-view.html",
40 | controller: "WelcomeCtrl"
41 | }
42 | }
43 | })
44 |
45 | .state("get-started.create-profile", {
46 | url: "/create-profile",
47 | views: {
48 | "get-started": {
49 | templateUrl: "templates/get-started/create-profile-view.html",
50 | controller: "CreateProfileCtrl"
51 | }
52 | }
53 | })
54 |
55 | .state("get-started.user-created", {
56 | url: "/user-created",
57 | views: {
58 | "get-started": {
59 | templateUrl: "templates/get-started/user-created-view.html",
60 | controller: "UserCreatedCtrl"
61 | }
62 | }
63 | })
64 |
65 | ;
66 |
67 | // Use the auth token interceptor to append the auth_token to every request
68 | $httpProvider.interceptors.push('AuthTokenInterceptor');
69 |
70 | // Fallback to this route
71 | $urlRouterProvider.otherwise("/get-started/welcome");
72 |
73 | });
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/ionicons/_ionicons-animation.scss:
--------------------------------------------------------------------------------
1 | // Animation Icons
2 | // --------------------------
3 |
4 | .#{$ionicons-prefix}spin {
5 | -webkit-animation: spin 1s infinite linear;
6 | -moz-animation: spin 1s infinite linear;
7 | -o-animation: spin 1s infinite linear;
8 | animation: spin 1s infinite linear;
9 | }
10 |
11 | @-moz-keyframes spin {
12 | 0% { -moz-transform: rotate(0deg); }
13 | 100% { -moz-transform: rotate(359deg); }
14 | }
15 | @-webkit-keyframes spin {
16 | 0% { -webkit-transform: rotate(0deg); }
17 | 100% { -webkit-transform: rotate(359deg); }
18 | }
19 | @-o-keyframes spin {
20 | 0% { -o-transform: rotate(0deg); }
21 | 100% { -o-transform: rotate(359deg); }
22 | }
23 | @-ms-keyframes spin {
24 | 0% { -ms-transform: rotate(0deg); }
25 | 100% { -ms-transform: rotate(359deg); }
26 | }
27 | @keyframes spin {
28 | 0% { transform: rotate(0deg); }
29 | 100% { transform: rotate(359deg); }
30 | }
31 |
32 |
33 | .#{$ionicons-prefix}loading-a,
34 | .#{$ionicons-prefix}loading-b,
35 | .#{$ionicons-prefix}loading-c,
36 | .#{$ionicons-prefix}loading-d,
37 | .#{$ionicons-prefix}looping,
38 | .#{$ionicons-prefix}refreshing,
39 | .#{$ionicons-prefix}ios7-reloading {
40 | @extend .ion;
41 | @extend .#{$ionicons-prefix}spin;
42 | }
43 |
44 | .#{$ionicons-prefix}loading-a {
45 | -webkit-animation-timing-function: steps(8, start);
46 | -moz-animation-timing-function: steps(8, start);
47 | animation-timing-function: steps(8, start);
48 | }
49 |
50 | .#{$ionicons-prefix}loading-a:before {
51 | @extend .#{$ionicons-prefix}load-a:before;
52 | }
53 |
54 | .#{$ionicons-prefix}loading-b:before {
55 | @extend .#{$ionicons-prefix}load-b:before;
56 | }
57 |
58 | .#{$ionicons-prefix}loading-c:before {
59 | @extend .#{$ionicons-prefix}load-c:before;
60 | }
61 |
62 | .#{$ionicons-prefix}loading-d:before {
63 | @extend .#{$ionicons-prefix}load-d:before;
64 | }
65 |
66 | .#{$ionicons-prefix}looping:before {
67 | @extend .#{$ionicons-prefix}loop:before;
68 | }
69 |
70 | .#{$ionicons-prefix}refreshing:before {
71 | @extend .#{$ionicons-prefix}refresh:before;
72 | }
73 |
74 | .#{$ionicons-prefix}ios7-reloading:before {
75 | @extend .#{$ionicons-prefix}ios7-reload:before;
76 | }
77 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ionic User Authentication with NodeJS
2 |
3 | For your enjoyment this project provides a basic Ionic app and Express server with functionality for user authentication using bearer tokens, as specified by [RFC 6750](http://tools.ietf.org/html/rfc6750).
4 |
5 | ## Prerequisites
6 |
7 | 1. Install [npm](https://github.com/npm/npm)
8 | 2. Install [Ionic CLI](http://ionicframework.com/getting-started/)
9 | 3. Install [MongoDB](http://www.mongodb.com/)
10 | 4. Install [Gulp](https://github.com/gulpjs/gulp/blob/master/docs/getting-started.md#getting-started)
11 |
12 | ## Installation
13 |
14 | 1. Download project.
15 | 2. Run `npm install` in both `mobile` and `server` directories.
16 | 3. Modify `conf.js` in `server` directory to connect to your MongoDB.
17 | 4. Run `mongod`.
18 | 5. Run `gulp` in `server` directory to start server.
19 | 6. Run `ionic serve` or `ionic build ios; ionic emulate ios;` in `mobile` directory.
20 | 7. Eat cookies
21 |
22 | If using `ionic serve` I used this [solution](http://forum.ionicframework.com/t/solved-cors-with-ionic/7454/7?u=seanhill) to do HTTP requests right from the browser.
23 |
24 | This project has not been tested in an production enviroment, so the API may not work without the `Access-Control-Allow-Origin` being set properly.
25 |
26 |
27 | ## Mobile App
28 |
29 | The mobile app is very basic. It starts you off in a "Welcome" screen that has one button `Create Profile`. This transitions you into a `Create Profile` page. After creating a profile and storing the `authToken` in the device's local storage, it transitions you to a sample page where you can get a user from the protected `/api/user/get` route. You can also test the `401` server response on this page by clearing out the `authToken` and requesting a user from `/api/user/get`. This causes the mobile app to display a `Login Modal` that requires to first authenticate, and then proceeds to get the user.
30 |
31 | Thanks to [KD Moore Consulting](http://www.kdmooreconsulting.com/blogs/authentication-with-ionic-and-angular-js-in-a-cordovaphonegap-mobile-web-application/) which provides a more detailed look at this process.
32 |
33 | ## The Point
34 |
35 | I finally figured out how to link up my Ionic App with a backend using bearer tokens for authentication, so I wanted to provide a solution that will hopefully help you on your journey to creating mobile apps.
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_modal.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Modals
4 | * --------------------------------------------------
5 | * Modals are independent windows that slide in from off-screen.
6 | */
7 |
8 | .modal-backdrop {
9 | @include transition(background-color 300ms ease-in-out);
10 | position: fixed;
11 | top: 0;
12 | left: 0;
13 | z-index: $z-index-modal;
14 | width: 100%;
15 | height: 100%;
16 | background-color: $modal-backdrop-bg-inactive;
17 |
18 | &.active {
19 | background-color: $modal-backdrop-bg-active;
20 | }
21 | }
22 |
23 | .modal {
24 | display: block;
25 | position: absolute;
26 | top: 0;
27 | z-index: $z-index-modal;
28 | overflow: hidden;
29 | min-height: 100%;
30 | width: 100%;
31 | background-color: $modal-bg-color;
32 | }
33 |
34 | @media (min-width: $modal-inset-mode-break-point) {
35 | // inset mode is when the modal doesn't fill the entire
36 | // display but instead is centered within a large display
37 | .modal {
38 | top: $modal-inset-mode-top;
39 | right: $modal-inset-mode-right;
40 | bottom: $modal-inset-mode-bottom;
41 | left: $modal-inset-mode-left;
42 | overflow: visible;
43 | min-height: $modal-inset-mode-min-height;
44 | width: (100% - $modal-inset-mode-left - $modal-inset-mode-right);
45 | }
46 |
47 | .modal.ng-leave-active {
48 | bottom: 0;
49 | }
50 |
51 | // remove ios header padding from inset header
52 | .platform-ios.platform-cordova .modal-wrapper .modal{
53 | .bar-header:not(.bar-subheader) {
54 | height: $bar-height;
55 | > * {
56 | margin-top: 0;
57 | }
58 | }
59 | .tabs-top > .tabs,
60 | .tabs.tabs-top {
61 | top: $bar-height;
62 | }
63 | .has-header,
64 | .bar-subheader {
65 | top: $bar-height;
66 | }
67 | .has-subheader {
68 | top: (2 * $bar-height);
69 | }
70 | .has-tabs-top {
71 | top: $bar-height + $tabs-height;
72 | }
73 | .has-header.has-subheader.has-tabs-top {
74 | top: 2 * $bar-height + $tabs-height;
75 | }
76 | }
77 | }
78 |
79 | // disable clicks on all but the modal
80 | .modal-open {
81 | pointer-events: none;
82 |
83 | .modal,
84 | .modal-backdrop {
85 | pointer-events: auto;
86 | }
87 | // prevent clicks on modal when loading overlay is active though
88 | &.loading-active {
89 | .modal,
90 | .modal-backdrop {
91 | pointer-events: none;
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_popup.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Popups
4 | * --------------------------------------------------
5 | */
6 |
7 | .popup-container {
8 | position: absolute;
9 | top: 0;
10 | left: 0;
11 | bottom: 0;
12 | right: 0;
13 | background: rgba(0,0,0,0);
14 |
15 | @include display-flex();
16 | @include justify-content(center);
17 | @include align-items(center);
18 |
19 | z-index: $z-index-popup;
20 |
21 | // Start hidden
22 | visibility: hidden;
23 | &.popup-showing {
24 | visibility: visible;
25 | }
26 |
27 | &.popup-hidden .popup {
28 | @include animation-name(scaleOut);
29 | @include animation-duration($popup-leave-animation-duration);
30 | @include animation-timing-function(ease-in-out);
31 | @include animation-fill-mode(both);
32 | }
33 |
34 | &.active .popup {
35 | @include animation-name(superScaleIn);
36 | @include animation-duration($popup-enter-animation-duration);
37 | @include animation-timing-function(ease-in-out);
38 | @include animation-fill-mode(both);
39 | }
40 |
41 | .popup {
42 | width: $popup-width;
43 | max-width: 100%;
44 | max-height: 90%;
45 |
46 | border-radius: $popup-border-radius;
47 | background-color: $popup-background-color;
48 |
49 | @include display-flex();
50 | @include flex-direction(column);
51 | }
52 | }
53 |
54 | .popup-head {
55 | padding: 15px 10px;
56 | border-bottom: 1px solid #eee;
57 | text-align: center;
58 | }
59 | .popup-title {
60 | margin: 0;
61 | padding: 0;
62 | font-size: 15px;
63 | }
64 | .popup-sub-title {
65 | margin: 5px 0 0 0;
66 | padding: 0;
67 | font-weight: normal;
68 | font-size: 11px;
69 | }
70 | .popup-body {
71 | padding: 10px;
72 | overflow: scroll;
73 | }
74 |
75 | .popup-buttons {
76 | @include display-flex();
77 | @include flex-direction(row);
78 | padding: 10px;
79 | min-height: $popup-button-min-height + 20;
80 |
81 | .button {
82 | @include flex(1);
83 | display: block;
84 | min-height: $popup-button-min-height;
85 | border-radius: $popup-button-border-radius;
86 | line-height: $popup-button-line-height;
87 |
88 | margin-right: 5px;
89 | &:last-child {
90 | margin-right: 0px;
91 | }
92 | }
93 | }
94 |
95 | .popup-open {
96 | pointer-events: none;
97 |
98 | &.modal-open .modal {
99 | pointer-events: none;
100 | }
101 |
102 | .popup-backdrop, .popup {
103 | pointer-events: auto;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_list.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Lists
4 | * --------------------------------------------------
5 | */
6 |
7 | .list {
8 | position: relative;
9 | padding-top: $item-border-width;
10 | padding-bottom: $item-border-width;
11 | padding-left: 0; // reset padding because ul and ol
12 | margin-bottom: 20px;
13 | }
14 | .list:last-child {
15 | margin-bottom: 0px;
16 | &.card{
17 | margin-bottom:40px;
18 | }
19 | }
20 |
21 |
22 | /**
23 | * List Header
24 | * --------------------------------------------------
25 | */
26 |
27 | .list-header {
28 | margin-top: $list-header-margin-top;
29 | padding: $list-header-padding;
30 | background-color: $list-header-bg;
31 | color: $list-header-color;
32 | font-weight: bold;
33 | }
34 |
35 | // when its a card make sure it doesn't duplicate top and bottom borders
36 | .card.list .list-item {
37 | padding-right: 1px;
38 | padding-left: 1px;
39 | }
40 |
41 |
42 | /**
43 | * Cards and Inset Lists
44 | * --------------------------------------------------
45 | * A card and list-inset are close to the same thing, except a card as a box shadow.
46 | */
47 |
48 | .card,
49 | .list-inset {
50 | overflow: hidden;
51 | margin: ($content-padding * 2) $content-padding;
52 | border-radius: $card-border-radius;
53 | background-color: $card-body-bg;
54 | }
55 |
56 | .card {
57 | padding-top: $item-border-width;
58 | padding-bottom: $item-border-width;
59 | box-shadow: 0 1px 2px rgba(0, 0, 0, .10);
60 | }
61 |
62 | .padding {
63 | .card, .list-inset {
64 | margin-left: 0;
65 | margin-right: 0;
66 | }
67 | }
68 |
69 | .card .item,
70 | .list-inset .item,
71 | .padding > .list .item
72 | {
73 | &:first-child {
74 | border-top-left-radius: $card-border-radius;
75 | border-top-right-radius: $card-border-radius;
76 |
77 | .item-content {
78 | border-top-left-radius: $card-border-radius;
79 | border-top-right-radius: $card-border-radius;
80 | }
81 | }
82 | &:last-child {
83 | border-bottom-right-radius: $card-border-radius;
84 | border-bottom-left-radius: $card-border-radius;
85 |
86 | .item-content {
87 | border-bottom-right-radius: $card-border-radius;
88 | border-bottom-left-radius: $card-border-radius;
89 | }
90 | }
91 | }
92 |
93 | .card .item:last-child,
94 | .list-inset .item:last-child {
95 | margin-bottom: $item-border-width * -1;
96 | }
97 |
98 | .card .item,
99 | .list-inset .item,
100 | .padding > .list .item,
101 | .padding-horizontal > .list .item {
102 | margin-right: 0;
103 | margin-left: 0;
104 |
105 | &.item-input input {
106 | padding-right: 44px;
107 | }
108 | }
109 | .padding-left > .list .item {
110 | margin-left: 0;
111 | }
112 | .padding-right > .list .item {
113 | margin-right: 0;
114 | }
115 |
--------------------------------------------------------------------------------
/mobile/hooks/after_prepare/010_add_platform_class.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | // Add Platform Class
4 | // v1.0
5 | // Automatically adds the platform class to the body tag
6 | // after the `prepare` command. By placing the platform CSS classes
7 | // directly in the HTML built for the platform, it speeds up
8 | // rendering the correct layout/style for the specific platform
9 | // instead of waiting for the JS to figure out the correct classes.
10 |
11 | var fs = require('fs');
12 | var path = require('path');
13 |
14 | var rootdir = process.argv[2];
15 |
16 | function addPlatformBodyTag(indexPath, platform) {
17 | // add the platform class to the body tag
18 | try {
19 | var platformClass = 'platform-' + platform;
20 | var cordovaClass = 'platform-cordova platform-webview';
21 |
22 | var html = fs.readFileSync(indexPath, 'utf8');
23 |
24 | var bodyTag = findBodyTag(html);
25 | if(!bodyTag) return; // no opening body tag, something's wrong
26 |
27 | if(bodyTag.indexOf(platformClass) > -1) return; // already added
28 |
29 | var newBodyTag = bodyTag;
30 |
31 | var classAttr = findClassAttr(bodyTag);
32 | if(classAttr) {
33 | // body tag has existing class attribute, add the classname
34 | var endingQuote = classAttr.substring(classAttr.length-1);
35 | var newClassAttr = classAttr.substring(0, classAttr.length-1);
36 | newClassAttr += ' ' + platformClass + ' ' + cordovaClass + endingQuote;
37 | newBodyTag = bodyTag.replace(classAttr, newClassAttr);
38 |
39 | } else {
40 | // add class attribute to the body tag
41 | newBodyTag = bodyTag.replace('>', ' class="' + platformClass + ' ' + cordovaClass + '">');
42 | }
43 |
44 | html = html.replace(bodyTag, newBodyTag);
45 |
46 | fs.writeFileSync(indexPath, html, 'utf8');
47 |
48 | process.stdout.write('add to body class: ' + platformClass + '\n');
49 | } catch(e) {
50 | process.stdout.write(e);
51 | }
52 | }
53 |
54 | function findBodyTag(html) {
55 | // get the body tag
56 | try{
57 | return html.match(/])(.*?)>/gi)[0];
58 | }catch(e){}
59 | }
60 |
61 | function findClassAttr(bodyTag) {
62 | // get the body tag's class attribute
63 | try{
64 | return bodyTag.match(/ class=["|'](.*?)["|']/gi)[0];
65 | }catch(e){}
66 | }
67 |
68 | if (rootdir) {
69 |
70 | // go through each of the platform directories that have been prepared
71 | var platforms = (process.env.CORDOVA_PLATFORMS ? process.env.CORDOVA_PLATFORMS.split(',') : []);
72 |
73 | for(var x=0; x
21 | # Cordova Hooks
22 |
23 | This directory may contain scripts used to customize cordova commands. This
24 | directory used to exist at `.cordova/hooks`, but has now been moved to the
25 | project root. Any scripts you add to these directories will be executed before
26 | and after the commands corresponding to the directory name. Useful for
27 | integrating your own build systems or integrating with version control systems.
28 |
29 | __Remember__: Make your scripts executable.
30 |
31 | ## Hook Directories
32 | The following subdirectories will be used for hooks:
33 |
34 | after_build/
35 | after_compile/
36 | after_docs/
37 | after_emulate/
38 | after_platform_add/
39 | after_platform_rm/
40 | after_platform_ls/
41 | after_plugin_add/
42 | after_plugin_ls/
43 | after_plugin_rm/
44 | after_plugin_search/
45 | after_prepare/
46 | after_run/
47 | after_serve/
48 | before_build/
49 | before_compile/
50 | before_docs/
51 | before_emulate/
52 | before_platform_add/
53 | before_platform_rm/
54 | before_platform_ls/
55 | before_plugin_add/
56 | before_plugin_ls/
57 | before_plugin_rm/
58 | before_plugin_search/
59 | before_prepare/
60 | before_run/
61 | before_serve/
62 | pre_package/ <-- Windows 8 and Windows Phone only.
63 |
64 | ## Script Interface
65 |
66 | All scripts are run from the project's root directory and have the root directory passes as the first argument. All other options are passed to the script using environment variables:
67 |
68 | * CORDOVA_VERSION - The version of the Cordova-CLI.
69 | * CORDOVA_PLATFORMS - Comma separated list of platforms that the command applies to (e.g.: android, ios).
70 | * CORDOVA_PLUGINS - Comma separated list of plugin IDs that the command applies to (e.g.: org.apache.cordova.file, org.apache.cordova.file-transfer)
71 | * CORDOVA_HOOK - Path to the hook that is being executed.
72 | * CORDOVA_CMDLINE - The exact command-line arguments passed to cordova (e.g.: cordova run ios --emulate)
73 |
74 | If a script returns a non-zero exit code, then the parent cordova command will be aborted.
75 |
76 |
77 | ## Writing hooks
78 |
79 | We highly recommend writting your hooks using Node.js so that they are
80 | cross-platform. Some good examples are shown here:
81 |
82 | [http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/](http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/)
83 |
84 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_select.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Select
4 | * --------------------------------------------------
5 | */
6 |
7 | .item-select {
8 | position: relative;
9 |
10 | select {
11 | @include appearance(none);
12 | position: absolute;
13 | top: 0;
14 | right: 0;
15 | padding: ($item-padding - 2) ($item-padding * 3) ($item-padding) $item-padding;
16 | max-width: 65%;
17 |
18 | border: none;
19 | background: $item-default-bg;
20 | color: #333;
21 |
22 | // hack to hide default dropdown arrow in FF
23 | text-indent: .01px;
24 | text-overflow: '';
25 |
26 | white-space: nowrap;
27 | font-size: $font-size-base;
28 |
29 | cursor: pointer;
30 | direction: rtl; // right align the select text
31 | }
32 |
33 | select::-ms-expand {
34 | // hide default dropdown arrow in IE
35 | display: none;
36 | }
37 |
38 | option {
39 | direction: ltr;
40 | }
41 |
42 | &:after {
43 | position: absolute;
44 | top: 50%;
45 | right: $item-padding;
46 | margin-top: -3px;
47 | width: 0;
48 | height: 0;
49 | border-top: 5px solid;
50 | border-right: 5px solid rgba(0, 0, 0, 0);
51 | border-left: 5px solid rgba(0, 0, 0, 0);
52 | color: #999;
53 | content: "";
54 | pointer-events: none;
55 | }
56 | &.item-light {
57 | select{
58 | background:$item-light-bg;
59 | color:$item-light-text;
60 | }
61 | }
62 | &.item-stable {
63 | select{
64 | background:$item-stable-bg;
65 | color:$item-stable-text;
66 | }
67 | &:after, .input-label{
68 | color:darken($item-stable-border,30%);
69 | }
70 | }
71 | &.item-positive {
72 | select{
73 | background:$item-positive-bg;
74 | color:$item-positive-text;
75 | }
76 | &:after, .input-label{
77 | color:$item-positive-text;
78 | }
79 | }
80 | &.item-calm {
81 | select{
82 | background:$item-calm-bg;
83 | color:$item-calm-text;
84 | }
85 | &:after, .input-label{
86 | color:$item-calm-text;
87 | }
88 | }
89 | &.item-assertive {
90 | select{
91 | background:$item-assertive-bg;
92 | color:$item-assertive-text;
93 | }
94 | &:after, .input-label{
95 | color:$item-assertive-text;
96 | }
97 | }
98 | &.item-balanced {
99 | select{
100 | background:$item-balanced-bg;
101 | color:$item-balanced-text;
102 | }
103 | &:after, .input-label{
104 | color:$item-balanced-text;
105 | }
106 | }
107 | &.item-energized {
108 | select{
109 | background:$item-energized-bg;
110 | color:$item-energized-text;
111 | }
112 | &:after, .input-label{
113 | color:$item-energized-text;
114 | }
115 | }
116 | &.item-royal {
117 | select{
118 | background:$item-royal-bg;
119 | color:$item-royal-text;
120 | }
121 | &:after, .input-label{
122 | color:$item-royal-text;
123 | }
124 | }
125 | &.item-dark {
126 | select{
127 | background:$item-dark-bg;
128 | color:$item-dark-text;
129 | }
130 | &:after, .input-label{
131 | color:$item-dark-text;
132 | }
133 | }
134 | }
135 |
136 | select {
137 | &[multiple],
138 | &[size] {
139 | height: auto;
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_range.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Range
4 | * --------------------------------------------------
5 | */
6 |
7 | input[type="range"] {
8 | display: inline-block;
9 | overflow: hidden;
10 | margin-top: 5px;
11 | margin-bottom: 5px;
12 | padding-right: 2px;
13 | padding-left: 1px;
14 | width: auto;
15 | height: $range-slider-height + 15;
16 | outline: none;
17 | background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, $range-default-track-bg), color-stop(100%, $range-default-track-bg));
18 | background: linear-gradient(to right, $range-default-track-bg 0%, $range-default-track-bg 100%);
19 | background-position: center;
20 | background-size: 99% $range-track-height;
21 | background-repeat: no-repeat;
22 | -webkit-appearance: none;
23 |
24 | &::-webkit-slider-thumb {
25 | position: relative;
26 | width: $range-slider-width;
27 | height: $range-slider-height;
28 | border-radius: $range-slider-border-radius;
29 | background-color: $toggle-handle-off-bg-color;
30 | box-shadow: 0 0 2px rgba(0,0,0,.5), 1px 3px 5px rgba(0,0,0,0.25);
31 | cursor: pointer;
32 | -webkit-appearance: none;
33 | }
34 |
35 | &::-webkit-slider-thumb:before {
36 | /* what creates the colorful line on the left side of the slider */
37 | position: absolute;
38 | top: ($range-slider-height / 2) - ($range-track-height / 2);
39 | left: -2001px;
40 | width: 2000px;
41 | height: $range-track-height;
42 | background: $dark;
43 | content: ' ';
44 | }
45 |
46 | &::-webkit-slider-thumb:after {
47 | /* create a larger (but hidden) hit area */
48 | position: absolute;
49 | top: -20px;
50 | left: -20px;
51 | padding: 30px;
52 | content: ' ';
53 | //background: red;
54 | //opacity: .5;
55 | }
56 |
57 | }
58 |
59 | .range {
60 | @include display-flex();
61 | @include align-items(center);
62 | padding: 2px 11px;
63 |
64 | &.range-light {
65 | input { @include range-style($range-light-track-bg); }
66 | }
67 | &.range-stable {
68 | input { @include range-style($range-stable-track-bg); }
69 | }
70 | &.range-positive {
71 | input { @include range-style($range-positive-track-bg); }
72 | }
73 | &.range-calm {
74 | input { @include range-style($range-calm-track-bg); }
75 | }
76 | &.range-balanced {
77 | input { @include range-style($range-balanced-track-bg); }
78 | }
79 | &.range-assertive {
80 | input { @include range-style($range-assertive-track-bg); }
81 | }
82 | &.range-energized {
83 | input { @include range-style($range-energized-track-bg); }
84 | }
85 | &.range-royal {
86 | input { @include range-style($range-royal-track-bg); }
87 | }
88 | &.range-dark {
89 | input { @include range-style($range-dark-track-bg); }
90 | }
91 | }
92 |
93 | .range .icon {
94 | @include flex(0);
95 | display: block;
96 | min-width: $range-icon-size;
97 | text-align: center;
98 | font-size: $range-icon-size;
99 | }
100 |
101 | .range input {
102 | @include flex(1);
103 | display: block;
104 | margin-right: 10px;
105 | margin-left: 10px;
106 | }
107 |
108 | .range-label {
109 | @include flex(0, 0, auto);
110 | display: block;
111 | white-space: nowrap;
112 | }
113 |
114 | .range-label:first-child {
115 | padding-left: 5px;
116 | }
117 | .range input + .range-label {
118 | padding-right: 5px;
119 | padding-left: 0;
120 | }
121 |
122 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_grid.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Grid
3 | * --------------------------------------------------
4 | * Using flexbox for the grid, inspired by Philip Walton:
5 | * http://philipwalton.github.io/solved-by-flexbox/demos/grids/
6 | * By default each .col within a .row will evenly take up
7 | * available width, and the height of each .col with take
8 | * up the height of the tallest .col in the same .row.
9 | */
10 |
11 | .row {
12 | @include display-flex();
13 | padding: ($grid-padding-width / 2);
14 | width: 100%;
15 | }
16 |
17 | .row-wrap {
18 | @include flex-wrap(wrap);
19 | }
20 |
21 | .row + .row {
22 | margin-top: ($grid-padding-width / 2) * -1;
23 | padding-top: 0;
24 | }
25 |
26 | .col {
27 | @include flex(1);
28 | display: block;
29 | padding: ($grid-padding-width / 2);
30 | width: 100%;
31 | }
32 |
33 |
34 | /* Vertically Align Columns */
35 | /* .row-* vertically aligns every .col in the .row */
36 | .row-top {
37 | @include align-items(flex-start);
38 | }
39 | .row-bottom {
40 | @include align-items(flex-end);
41 | }
42 | .row-center {
43 | @include align-items(center);
44 | }
45 | .row-stretch {
46 | @include align-items(stretch);
47 | }
48 | .row-baseline {
49 | @include align-items(baseline);
50 | }
51 |
52 | /* .col-* vertically aligns an individual .col */
53 | .col-top {
54 | @include align-self(flex-start);
55 | }
56 | .col-bottom {
57 | @include align-self(flex-end);
58 | }
59 | .col-center {
60 | @include align-self(center);
61 | }
62 |
63 | /* Column Offsets */
64 | .col-offset-10 {
65 | margin-left: 10%;
66 | }
67 | .col-offset-20 {
68 | margin-left: 20%;
69 | }
70 | .col-offset-25 {
71 | margin-left: 25%;
72 | }
73 | .col-offset-33, .col-offset-34 {
74 | margin-left: 33.3333%;
75 | }
76 | .col-offset-50 {
77 | margin-left: 50%;
78 | }
79 | .col-offset-66, .col-offset-67 {
80 | margin-left: 66.6666%;
81 | }
82 | .col-offset-75 {
83 | margin-left: 75%;
84 | }
85 | .col-offset-80 {
86 | margin-left: 80%;
87 | }
88 | .col-offset-90 {
89 | margin-left: 90%;
90 | }
91 |
92 |
93 | /* Explicit Column Percent Sizes */
94 | /* By default each grid column will evenly distribute */
95 | /* across the grid. However, you can specify individual */
96 | /* columns to take up a certain size of the available area */
97 | .col-10 {
98 | @include flex(0, 0, 10%);
99 | max-width: 10%;
100 | }
101 | .col-20 {
102 | @include flex(0, 0, 20%);
103 | max-width: 20%;
104 | }
105 | .col-25 {
106 | @include flex(0, 0, 25%);
107 | max-width: 25%;
108 | }
109 | .col-33, .col-34 {
110 | @include flex(0, 0, 33.3333%);
111 | max-width: 33.3333%;
112 | }
113 | .col-50 {
114 | @include flex(0, 0, 50%);
115 | max-width: 50%;
116 | }
117 | .col-66, .col-67 {
118 | @include flex(0, 0, 66.6666%);
119 | max-width: 66.6666%;
120 | }
121 | .col-75 {
122 | @include flex(0, 0, 75%);
123 | max-width: 75%;
124 | }
125 | .col-80 {
126 | @include flex(0, 0, 80%);
127 | max-width: 80%;
128 | }
129 | .col-90 {
130 | @include flex(0, 0, 90%);
131 | max-width: 90%;
132 | }
133 |
134 |
135 | /* Responsive Grid Classes */
136 | /* Adding a class of responsive-X to a row */
137 | /* will trigger the flex-direction to */
138 | /* change to column and add some margin */
139 | /* to any columns in the row for clearity */
140 |
141 | @include responsive-grid-break('.responsive-sm', $grid-responsive-sm-break);
142 | @include responsive-grid-break('.responsive-md', $grid-responsive-md-break);
143 | @include responsive-grid-break('.responsive-lg', $grid-responsive-lg-break);
144 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_type.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Typography
4 | * --------------------------------------------------
5 | */
6 |
7 |
8 | // Body text
9 | // -------------------------
10 |
11 | p {
12 | margin: 0 0 ($line-height-computed / 2);
13 | }
14 |
15 |
16 | // Emphasis & misc
17 | // -------------------------
18 |
19 | small { font-size: 85%; }
20 | cite { font-style: normal; }
21 |
22 |
23 | // Alignment
24 | // -------------------------
25 |
26 | .text-left { text-align: left; }
27 | .text-right { text-align: right; }
28 | .text-center { text-align: center; }
29 |
30 |
31 | // Headings
32 | // -------------------------
33 |
34 | h1, h2, h3, h4, h5, h6,
35 | .h1, .h2, .h3, .h4, .h5, .h6 {
36 | color: $base-color;
37 | font-weight: $headings-font-weight;
38 | font-family: $headings-font-family;
39 | line-height: $headings-line-height;
40 |
41 | small {
42 | font-weight: normal;
43 | line-height: 1;
44 | }
45 | }
46 |
47 | h1, .h1,
48 | h2, .h2,
49 | h3, .h3 {
50 | margin-top: $line-height-computed;
51 | margin-bottom: ($line-height-computed / 2);
52 |
53 | &:first-child {
54 | margin-top: 0;
55 | }
56 |
57 | + h1, + .h1,
58 | + h2, + .h2,
59 | + h3, + .h3 {
60 | margin-top: ($line-height-computed / 2);
61 | }
62 | }
63 |
64 | h4, .h4,
65 | h5, .h5,
66 | h6, .h6 {
67 | margin-top: ($line-height-computed / 2);
68 | margin-bottom: ($line-height-computed / 2);
69 | }
70 |
71 | h1, .h1 { font-size: floor($font-size-base * 2.60); } // ~36px
72 | h2, .h2 { font-size: floor($font-size-base * 2.15); } // ~30px
73 | h3, .h3 { font-size: ceil($font-size-base * 1.70); } // ~24px
74 | h4, .h4 { font-size: ceil($font-size-base * 1.25); } // ~18px
75 | h5, .h5 { font-size: $font-size-base; }
76 | h6, .h6 { font-size: ceil($font-size-base * 0.85); } // ~12px
77 |
78 | h1 small, .h1 small { font-size: ceil($font-size-base * 1.70); } // ~24px
79 | h2 small, .h2 small { font-size: ceil($font-size-base * 1.25); } // ~18px
80 | h3 small, .h3 small,
81 | h4 small, .h4 small { font-size: $font-size-base; }
82 |
83 |
84 | // Description Lists
85 | // -------------------------
86 |
87 | dl {
88 | margin-bottom: $line-height-computed;
89 | }
90 | dt,
91 | dd {
92 | line-height: $line-height-base;
93 | }
94 | dt {
95 | font-weight: bold;
96 | }
97 |
98 |
99 | // Blockquotes
100 | // -------------------------
101 |
102 | blockquote {
103 | margin: 0 0 $line-height-computed;
104 | padding: ($line-height-computed / 2) $line-height-computed;
105 | border-left: 5px solid gray;
106 |
107 | p {
108 | font-weight: 300;
109 | font-size: ($font-size-base * 1.25);
110 | line-height: 1.25;
111 | }
112 |
113 | p:last-child {
114 | margin-bottom: 0;
115 | }
116 |
117 | small {
118 | display: block;
119 | line-height: $line-height-base;
120 | &:before {
121 | content: '\2014 \00A0';// EM DASH, NBSP;
122 | }
123 | }
124 | }
125 |
126 |
127 | // Quotes
128 | // -------------------------
129 |
130 | q:before,
131 | q:after,
132 | blockquote:before,
133 | blockquote:after {
134 | content: "";
135 | }
136 |
137 |
138 | // Addresses
139 | // -------------------------
140 |
141 | address {
142 | display: block;
143 | margin-bottom: $line-height-computed;
144 | font-style: normal;
145 | line-height: $line-height-base;
146 | }
147 |
148 |
149 | // Links
150 | // -------------------------
151 |
152 | a.subdued {
153 | padding-right: 10px;
154 | color: #888;
155 | text-decoration: none;
156 |
157 | &:hover {
158 | text-decoration: none;
159 | }
160 | &:last-child {
161 | padding-right: 0;
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_platform.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Platform
4 | * --------------------------------------------------
5 | * Platform specific tweaks
6 | */
7 |
8 |
9 | /**
10 | * Apply roboto font
11 | */
12 |
13 | .roboto {
14 | font-family: "Roboto", $font-family-base;
15 |
16 | input {
17 | font-family: "Roboto", $font-family-base;
18 | }
19 | }
20 | /*
21 | .platform-android {
22 |
23 |
24 | .bar {
25 | padding: 0;
26 |
27 | line-height: 40px;
28 |
29 | .button {
30 | line-height: 40px;
31 | }
32 |
33 | .button-icon:before {
34 | font-size: 24px;
35 | }
36 | }
37 |
38 | .back-button {
39 | &.button-icon:before {
40 | line-height: 40px;
41 | }
42 | margin-left: -3px;
43 | padding: 0px 2px !important;
44 | &.ion-android-arrow-back:before {
45 | font-size: 12px;
46 | }
47 |
48 | &.back-button.active,
49 | &.back-button.activated {
50 | background-color: rgba(0,0,0,0.1);
51 | }
52 | }
53 |
54 | .item-divider {
55 | background: none;
56 | border-top-width: 0;
57 | border-bottom-width: 2px;
58 | text-transform: uppercase;
59 | margin-top: 10px;
60 | font-size: 14px;
61 | }
62 | .item {
63 | border-left-width: 0;
64 | border-right-width: 0;
65 | }
66 |
67 | .item-divider ~ .item:not(.item-divider) {
68 | border-bottom-width: 0;
69 | }
70 |
71 | .back-button:not(.ng-hide) + .left-buttons + .title {
72 | // Don't allow normal titles in this mode
73 | display: none;
74 | }
75 |
76 | .bar .title {
77 | text-align: left;
78 | font-weight: normal;
79 | }
80 |
81 | font-family: 'Roboto';
82 |
83 | h1, h2, h3, h4, h5 {
84 | font-family: 'Roboto', $font-family-base;
85 | }
86 |
87 | .tab-item {
88 | font-family: 'Roboto', $font-family-base;
89 | }
90 |
91 |
92 | input, button, select, textarea {
93 | font-family: 'Roboto', $font-family-base;
94 | }
95 | */
96 | //}
97 |
98 | .platform-ios.platform-cordova {
99 | // iOS7/8 has a status bar which sits on top of the header.
100 | // Bump down everything to make room for it. However, if
101 | // if its in Cordova, and set to fullscreen, then disregard the bump.
102 | &:not(.fullscreen) {
103 | .bar-header:not(.bar-subheader) {
104 | height: $bar-height + $ios-statusbar-height;
105 |
106 | &.item-input-inset .item-input-wrapper {
107 | margin-top: 19px !important;
108 | }
109 |
110 | > * {
111 | margin-top: $ios-statusbar-height;
112 | }
113 | }
114 | .tabs-top > .tabs,
115 | .tabs.tabs-top {
116 | top: $bar-height + $ios-statusbar-height;
117 | }
118 |
119 | .has-header,
120 | .bar-subheader {
121 | top: $bar-height + $ios-statusbar-height;
122 | }
123 | .has-subheader {
124 | top: (2 * $bar-height) + $ios-statusbar-height;
125 | }
126 | .has-tabs-top {
127 | top: $bar-height + $tabs-height + $ios-statusbar-height;
128 | }
129 | .has-header.has-subheader.has-tabs-top {
130 | top: 2 * $bar-height + $tabs-height + $ios-statusbar-height;
131 | }
132 | }
133 | &.status-bar-hide {
134 | // Cordova doesn't adjust the body height correctly, this makes up for it
135 | margin-bottom: 20px;
136 | }
137 | }
138 |
139 | @media (orientation:landscape) {
140 | .platform-ios.platform-browser.platform-ipad {
141 | position: fixed; // required for iPad 7 Safari
142 | }
143 | }
144 |
145 | .platform-c:not(.enable-transitions) * {
146 | // disable transitions on grade-c devices (Android 2)
147 | -webkit-transition: none !important;
148 | transition: none !important;
149 | }
150 |
151 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_popover.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Popovers
4 | * --------------------------------------------------
5 | * Popovers are independent views which float over content
6 | */
7 |
8 | .popover-backdrop {
9 | position: fixed;
10 | top: 0;
11 | left: 0;
12 | z-index: $z-index-popover;
13 | width: 100%;
14 | height: 100%;
15 | background-color: $popover-backdrop-bg-inactive;
16 |
17 | &.active {
18 | background-color: $popover-backdrop-bg-active;
19 | }
20 | }
21 |
22 | .popover {
23 | position: absolute;
24 | top: 25%;
25 | left: 50%;
26 | z-index: $z-index-popover;
27 | display: block;
28 | margin-top: 12px;
29 | margin-left: -$popover-width / 2;
30 | height: $popover-height;
31 | width: $popover-width;
32 | background-color: $popover-bg-color;
33 | box-shadow: $popover-box-shadow;
34 | opacity: 0;
35 |
36 | .item:first-child {
37 | border-top: 0;
38 | }
39 |
40 | .item:last-child {
41 | border-bottom: 0;
42 | }
43 |
44 | &.popover-bottom {
45 | margin-top: -12px;
46 | }
47 | }
48 |
49 |
50 | // Set popover border-radius
51 | .popover,
52 | .popover .bar-header {
53 | border-radius: $popover-border-radius;
54 | }
55 | .popover .scroll-content {
56 | z-index: 1;
57 | margin: 2px 0;
58 | }
59 | .popover .bar-header {
60 | border-bottom-right-radius: 0;
61 | border-bottom-left-radius: 0;
62 | }
63 | .popover .has-header {
64 | border-top-right-radius: 0;
65 | border-top-left-radius: 0;
66 | }
67 | .popover-arrow {
68 | display: none;
69 | }
70 |
71 |
72 | // iOS Popover
73 | .platform-ios {
74 |
75 | .popover {
76 | box-shadow: $popover-box-shadow-ios;
77 | }
78 |
79 | .popover,
80 | .popover .bar-header {
81 | border-radius: $popover-border-radius-ios;
82 | }
83 | .popover .scroll-content {
84 | margin: 8px 0;
85 | border-radius: $popover-border-radius-ios;
86 | }
87 | .popover .scroll-content.has-header {
88 | margin-top: 0;
89 | }
90 | .popover-arrow {
91 | position: absolute;
92 | display: block;
93 | top: -17px;
94 | width: 30px;
95 | height: 19px;
96 | overflow: hidden;
97 |
98 | &:after {
99 | position: absolute;
100 | top: 12px;
101 | left: 5px;
102 | width: 20px;
103 | height: 20px;
104 | background-color: $popover-bg-color;
105 | border-radius: 3px;
106 | content: '';
107 | @include rotate(-45deg);
108 | }
109 | }
110 | .popover-bottom .popover-arrow {
111 | top: auto;
112 | bottom: -10px;
113 | &:after {
114 | top: -6px;
115 | }
116 | }
117 | }
118 |
119 |
120 | // Android Popover
121 | .platform-android {
122 |
123 | .popover {
124 | margin-top: -32px;
125 | background-color: $popover-bg-color-android;
126 | box-shadow: $popover-box-shadow-android;
127 |
128 | .item {
129 | border-color: $popover-bg-color-android;
130 | background-color: $popover-bg-color-android;
131 | color: #4d4d4d;
132 | }
133 | &.popover-bottom {
134 | margin-top: 32px;
135 | }
136 | }
137 |
138 | .popover-backdrop,
139 | .popover-backdrop.active {
140 | background-color: transparent;
141 | }
142 | }
143 |
144 |
145 | // disable clicks on all but the popover
146 | .popover-open {
147 | pointer-events: none;
148 |
149 | .popover,
150 | .popover-backdrop {
151 | pointer-events: auto;
152 | }
153 | // prevent clicks on popover when loading overlay is active though
154 | &.loading-active {
155 | .popover,
156 | .popover-backdrop {
157 | pointer-events: none;
158 | }
159 | }
160 | }
161 |
162 |
163 | // wider popover on larger viewports
164 | @media (min-width: $popover-large-break-point) {
165 | .popover {
166 | width: $popover-large-width;
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_toggle.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Toggle
4 | * --------------------------------------------------
5 | */
6 |
7 | .item-toggle {
8 | pointer-events: none;
9 | }
10 |
11 | .toggle {
12 | // set the color defaults
13 | @include toggle-style($toggle-on-default-border, $toggle-on-default-bg);
14 |
15 | position: relative;
16 | display: inline-block;
17 | pointer-events: auto;
18 | margin: -$toggle-hit-area-expansion;
19 | padding: $toggle-hit-area-expansion;
20 |
21 | &.dragging {
22 | .handle {
23 | background-color: $toggle-handle-dragging-bg-color !important;
24 | }
25 | }
26 |
27 | &.toggle-light {
28 | @include toggle-style($toggle-on-light-border, $toggle-on-light-bg);
29 | }
30 | &.toggle-stable {
31 | @include toggle-style($toggle-on-stable-border, $toggle-on-stable-bg);
32 | }
33 | &.toggle-positive {
34 | @include toggle-style($toggle-on-positive-border, $toggle-on-positive-bg);
35 | }
36 | &.toggle-calm {
37 | @include toggle-style($toggle-on-calm-border, $toggle-on-calm-bg);
38 | }
39 | &.toggle-assertive {
40 | @include toggle-style($toggle-on-assertive-border, $toggle-on-assertive-bg);
41 | }
42 | &.toggle-balanced {
43 | @include toggle-style($toggle-on-balanced-border, $toggle-on-balanced-bg);
44 | }
45 | &.toggle-energized {
46 | @include toggle-style($toggle-on-energized-border, $toggle-on-energized-bg);
47 | }
48 | &.toggle-royal {
49 | @include toggle-style($toggle-on-royal-border, $toggle-on-royal-bg);
50 | }
51 | &.toggle-dark {
52 | @include toggle-style($toggle-on-dark-border, $toggle-on-dark-bg);
53 | }
54 | }
55 |
56 | .toggle input {
57 | // hide the actual input checkbox
58 | display: none;
59 | }
60 |
61 | /* the track appearance when the toggle is "off" */
62 | .toggle .track {
63 | @include transition-timing-function(ease-in-out);
64 | @include transition-duration($toggle-transition-duration);
65 | @include transition-property((background-color, border));
66 |
67 | display: inline-block;
68 | box-sizing: border-box;
69 | width: $toggle-width;
70 | height: $toggle-height;
71 | border: solid $toggle-border-width $toggle-off-border-color;
72 | border-radius: $toggle-border-radius;
73 | background-color: $toggle-off-bg-color;
74 | content: ' ';
75 | cursor: pointer;
76 | pointer-events: none;
77 | }
78 |
79 | /* Fix to avoid background color bleeding */
80 | /* (occured on (at least) Android 4.2, Asus MeMO Pad HD7 ME173X) */
81 | .platform-android4_2 .toggle .track {
82 | -webkit-background-clip: padding-box;
83 | }
84 |
85 | /* the handle (circle) thats inside the toggle's track area */
86 | /* also the handle's appearance when it is "off" */
87 | .toggle .handle {
88 | @include transition($toggle-transition-duration ease-in-out);
89 | position: absolute;
90 | display: block;
91 | width: $toggle-handle-width;
92 | height: $toggle-handle-height;
93 | border-radius: $toggle-handle-radius;
94 | background-color: $toggle-handle-off-bg-color;
95 | top: $toggle-border-width + $toggle-hit-area-expansion;
96 | left: $toggle-border-width + $toggle-hit-area-expansion;
97 |
98 | &:before {
99 | // used to create a larger (but hidden) hit area to slide the handle
100 | position: absolute;
101 | top: -4px;
102 | left: ( ($toggle-handle-width / 2) * -1) - 8;
103 | padding: ($toggle-handle-height / 2) + 5 ($toggle-handle-width + 7);
104 | content: " ";
105 | }
106 | }
107 |
108 | .toggle input:checked + .track .handle {
109 | // the handle when the toggle is "on"
110 | @include translate3d($toggle-width - $toggle-handle-width - ($toggle-border-width * 2), 0, 0);
111 | background-color: $toggle-handle-on-bg-color;
112 | }
113 |
114 | .item-toggle.active {
115 | box-shadow: none;
116 | }
117 |
118 | .item-toggle,
119 | .item-toggle.item-complex .item-content {
120 | // make sure list item content have enough padding on right to fit the toggle
121 | padding-right: ($item-padding * 3) + $toggle-width;
122 | }
123 |
124 | .item-toggle.item-complex {
125 | padding-right: 0;
126 | }
127 |
128 | .item-toggle .toggle {
129 | // position the toggle to the right within a list item
130 | position: absolute;
131 | top: $item-padding / 2;
132 | right: $item-padding;
133 | z-index: $z-index-item-toggle;
134 | }
135 |
136 | .toggle input:disabled + .track {
137 | opacity: .6;
138 | }
139 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/js/angular/angular-sanitize.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | AngularJS v1.2.25
3 | (c) 2010-2014 Google, Inc. http://angularjs.org
4 | License: MIT
5 | */
6 | (function(q,g,r){'use strict';function F(a){var d=[];t(d,g.noop).chars(a);return d.join("")}function m(a){var d={};a=a.split(",");var c;for(c=0;c=c;e--)d.end&&d.end(f[e]);f.length=c}}"string"!==typeof a&&(a=null===a||"undefined"===typeof a?"":""+a);var b,l,f=[],n=a,h;for(f.last=function(){return f[f.length-1]};a;){h="";l=!0;if(f.last()&&y[f.last()])a=a.replace(RegExp("(.*)<\\s*\\/\\s*"+f.last()+"[^>]*>","i"),function(a,b){b=b.replace(I,"$1").replace(J,"$1");d.chars&&d.chars(s(b));return""}),e("",f.last());else{if(0===a.indexOf("\x3c!--"))b=a.indexOf("--",4),0<=b&&a.lastIndexOf("--\x3e",b)===b&&(d.comment&&d.comment(a.substring(4,
8 | b)),a=a.substring(b+3),l=!1);else if(z.test(a)){if(b=a.match(z))a=a.replace(b[0],""),l=!1}else if(K.test(a)){if(b=a.match(A))a=a.substring(b[0].length),b[0].replace(A,e),l=!1}else L.test(a)&&((b=a.match(B))?(b[4]&&(a=a.substring(b[0].length),b[0].replace(B,c)),l=!1):(h+="<",a=a.substring(1)));l&&(b=a.indexOf("<"),h+=0>b?a:a.substring(0,b),a=0>b?"":a.substring(b),d.chars&&d.chars(s(h)))}if(a==n)throw M("badparse",a);n=a}e()}function s(a){if(!a)return"";var d=N.exec(a);a=d[1];var c=d[3];if(d=d[2])p.innerHTML=
9 | d.replace(//g,">")}function t(a,d){var c=!1,e=g.bind(a,a.push);return{start:function(a,l,f){a=g.lowercase(a);!c&&y[a]&&(c=a);c||!0!==D[a]||(e("<"),e(a),g.forEach(l,function(c,f){var k=
10 | g.lowercase(f),l="img"===a&&"src"===k||"background"===k;!0!==Q[k]||!0===E[k]&&!d(c,l)||(e(" "),e(f),e('="'),e(C(c)),e('"'))}),e(f?"/>":">"))},end:function(a){a=g.lowercase(a);c||!0!==D[a]||(e(""),e(a),e(">"));a==c&&(c=!1)},chars:function(a){c||e(C(a))}}}var M=g.$$minErr("$sanitize"),B=/^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,A=/^<\/\s*([\w:-]+)[^>]*>/,H=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,L=/^,
11 | K=/^<\//,I=/\x3c!--(.*?)--\x3e/g,z=/]*?)>/i,J=/"]/,c=/^mailto:/;return function(e,b){function l(a){a&&k.push(F(a))}function f(a,c){k.push("');l(c);k.push("")}
14 | if(!e)return e;for(var n,h=e,k=[],m,p;n=h.match(d);)m=n[0],n[2]==n[3]&&(m="mailto:"+m),p=n.index,l(h.substr(0,p)),f(m,n[0].replace(c,"")),h=h.substring(p+n[0].length);l(h);return a(k.join(""))}}])})(window,window.angular);
15 | //# sourceMappingURL=angular-sanitize.min.js.map
16 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_checkbox.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Checkbox
4 | * --------------------------------------------------
5 | */
6 |
7 | .checkbox {
8 | // set the color defaults
9 | @include checkbox-style($checkbox-off-border-default, $checkbox-on-bg-default);
10 |
11 | position: relative;
12 | display: inline-block;
13 | padding: ($checkbox-height / 4) ($checkbox-width / 4);
14 | cursor: pointer;
15 | }
16 | .checkbox-light {
17 | @include checkbox-style($checkbox-off-border-light, $checkbox-on-bg-light);
18 | }
19 | .checkbox-stable {
20 | @include checkbox-style($checkbox-off-border-stable, $checkbox-on-bg-stable);
21 | }
22 | .checkbox-positive {
23 | @include checkbox-style($checkbox-off-border-positive, $checkbox-on-bg-positive);
24 | }
25 | .checkbox-calm {
26 | @include checkbox-style($checkbox-off-border-calm, $checkbox-on-bg-calm);
27 | }
28 | .checkbox-assertive {
29 | @include checkbox-style($checkbox-off-border-assertive, $checkbox-on-bg-assertive);
30 | }
31 | .checkbox-balanced {
32 | @include checkbox-style($checkbox-off-border-balanced, $checkbox-on-bg-balanced);
33 | }
34 | .checkbox-energized{
35 | @include checkbox-style($checkbox-off-border-energized, $checkbox-on-bg-energized);
36 | }
37 | .checkbox-royal {
38 | @include checkbox-style($checkbox-off-border-royal, $checkbox-on-bg-royal);
39 | }
40 | .checkbox-dark {
41 | @include checkbox-style($checkbox-off-border-dark, $checkbox-on-bg-dark);
42 | }
43 |
44 | .checkbox input:disabled:before,
45 | .checkbox input:disabled + .checkbox-icon:before {
46 | border-color: $checkbox-off-border-light;
47 | }
48 |
49 | .checkbox input:disabled:checked:before,
50 | .checkbox input:disabled:checked + .checkbox-icon:before {
51 | background: $checkbox-on-bg-light;
52 | }
53 |
54 |
55 | .checkbox.checkbox-input-hidden input {
56 | display: none !important;
57 | }
58 |
59 | .checkbox input,
60 | .checkbox-icon {
61 | position: relative;
62 | width: $checkbox-width;
63 | height: $checkbox-height;
64 | display: block;
65 | border: 0;
66 | background: transparent;
67 | cursor: pointer;
68 | -webkit-appearance: none;
69 |
70 | &:before {
71 | // what the checkbox looks like when its not checked
72 | display: table;
73 | width: 100%;
74 | height: 100%;
75 | border-width: $checkbox-border-width;
76 | border-style: solid;
77 | border-radius: $checkbox-border-radius;
78 | background: $checkbox-off-bg-color;
79 | content: ' ';
80 | transition: background-color 20ms ease-in-out;
81 | }
82 | }
83 |
84 | .checkbox input:checked:before,
85 | input:checked + .checkbox-icon:before {
86 | border-width: $checkbox-border-width + 1;
87 | }
88 |
89 | // the checkmark within the box
90 | .checkbox input:after,
91 | .checkbox-icon:after {
92 | @include transition(opacity .05s ease-in-out);
93 | @include rotate(-45deg);
94 | position: absolute;
95 | top: 30%;
96 | left: 26%;
97 | display: table;
98 | width: ($checkbox-width / 2) + 1;
99 | height: ($checkbox-width / 3) + 1;
100 | border: $checkbox-check-width solid $checkbox-check-color;
101 | border-top: 0;
102 | border-right: 0;
103 | content: ' ';
104 | opacity: 0;
105 | }
106 |
107 | .grade-c .checkbox input:after,
108 | .grade-c .checkbox-icon:after {
109 | @include rotate(0);
110 | top: 3px;
111 | left: 4px;
112 | border: none;
113 | color: $checkbox-check-color;
114 | content: '\2713';
115 | font-weight: bold;
116 | font-size: 20px;
117 | }
118 |
119 | // what the checkmark looks like when its checked
120 | .checkbox input:checked:after,
121 | input:checked + .checkbox-icon:after {
122 | opacity: 1;
123 | }
124 |
125 | // make sure item content have enough padding on left to fit the checkbox
126 | .item-checkbox {
127 | padding-left: ($item-padding * 2) + $checkbox-width;
128 |
129 | &.active {
130 | box-shadow: none;
131 | }
132 | }
133 |
134 | // position the checkbox to the left within an item
135 | .item-checkbox .checkbox {
136 | position: absolute;
137 | top: 50%;
138 | right: $item-padding / 2;
139 | left: $item-padding / 2;
140 | z-index: $z-index-item-checkbox;
141 | margin-top: (($checkbox-height + ($checkbox-height / 2)) / 2) * -1;
142 | }
143 |
144 |
145 | .item-checkbox.item-checkbox-right {
146 | padding-right: ($item-padding * 2) + $checkbox-width;
147 | padding-left: $item-padding;
148 | }
149 |
150 | .item-checkbox-right .checkbox input,
151 | .item-checkbox-right .checkbox-icon {
152 | float: right;
153 | }
--------------------------------------------------------------------------------
/mobile/www/js/services/auth-retry-service.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | angular.module('http-auth-interceptor', ['http-auth-interceptor-buffer'])
5 |
6 | .factory('AuthService', ['$rootScope', 'httpBuffer', function($rootScope, httpBuffer) {
7 | return {
8 | /**
9 | * Call this function to indicate that authentication was successfull and trigger a
10 | * retry of all deferred requests.
11 | * @param data an optional argument to pass on to $broadcast which may be useful for
12 | * example if you need to pass through details of the user that was logged in
13 | */
14 | loginConfirmed: function(data, configUpdater) {
15 | var updater = configUpdater || function(config) {
16 | return config;
17 | };
18 | $rootScope.$broadcast('auth:login-confirmed', data);
19 | httpBuffer.retryAll(updater);
20 | },
21 |
22 | /**
23 | * Call this function to indicate that authentication should not proceed.
24 | * All deferred requests will be abandoned or rejected (if reason is provided).
25 | * @param data an optional argument to pass on to $broadcast.
26 | * @param reason if provided, the requests are rejected; abandoned otherwise.
27 | */
28 | loginCancelled: function(data, reason) {
29 | httpBuffer.rejectAll(reason);
30 | $rootScope.$broadcast('auth:login-cancelled', data);
31 | }
32 | };
33 | }])
34 |
35 | /**
36 | * $http interceptor.
37 | * On 401 response (without 'ignoreAuthModule' option) stores the request
38 | * and broadcasts 'event:angular-auth-loginRequired'.
39 | */
40 | .config(['$httpProvider', function($httpProvider) {
41 | $httpProvider.interceptors.push(['$rootScope', '$q', 'httpBuffer', function($rootScope, $q, httpBuffer) {
42 | return {
43 | responseError: function(rejection) {
44 | if (rejection.status === 401 && !rejection.config.ignoreAuthModule) {
45 | var deferred = $q.defer();
46 | httpBuffer.append(rejection.config, deferred);
47 | $rootScope.$broadcast('auth:login-required', rejection);
48 | return deferred.promise;
49 | }
50 | // otherwise, default behaviour
51 | return $q.reject(rejection);
52 | }
53 | };
54 | }]);
55 | }]);
56 |
57 | /**
58 | * Private module, a utility, required internally by 'http-auth-interceptor'.
59 | */
60 | angular.module('http-auth-interceptor-buffer', [])
61 |
62 | .factory('httpBuffer', ['$injector', function($injector) {
63 | /** Holds all the requests, so they can be re-requested in future. */
64 | var buffer = [];
65 |
66 | /** Service initialized later because of circular dependency problem. */
67 | var $http;
68 |
69 | function retryHttpRequest(config, deferred) {
70 | function successCallback(response) {
71 | deferred.resolve(response);
72 | }
73 |
74 | function errorCallback(response) {
75 | deferred.reject(response);
76 | }
77 | $http = $http || $injector.get('$http');
78 | $http(config).then(successCallback, errorCallback);
79 | }
80 |
81 | return {
82 | /**
83 | * Appends HTTP request configuration object with deferred response attached to buffer.
84 | */
85 | append: function(config, deferred) {
86 | buffer.push({
87 | config: config,
88 | deferred: deferred
89 | });
90 | },
91 |
92 | /**
93 | * Abandon or reject (if reason provided) all the buffered requests.
94 | */
95 | rejectAll: function(reason) {
96 | if (reason) {
97 | for (var i = 0; i < buffer.length; ++i) {
98 | buffer[i].deferred.reject(reason);
99 | }
100 | }
101 | buffer = [];
102 | },
103 |
104 | /**
105 | * Retries all the buffered requests clears the buffer.
106 | */
107 | retryAll: function(updater) {
108 | for (var i = 0; i < buffer.length; ++i) {
109 | retryHttpRequest(updater(buffer[i].config), buffer[i].deferred);
110 | }
111 | buffer = [];
112 | }
113 | };
114 | }]);
115 | })();
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_util.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Utility Classes
4 | * --------------------------------------------------
5 | */
6 |
7 | .hide {
8 | display: none;
9 | }
10 | .opacity-hide {
11 | opacity: 0;
12 | }
13 | .grade-b .opacity-hide,
14 | .grade-c .opacity-hide {
15 | opacity: 1;
16 | display: none;
17 | }
18 | .show {
19 | display: block;
20 | }
21 | .opacity-show {
22 | opacity: 1;
23 | }
24 | .invisible {
25 | visibility: hidden;
26 | }
27 |
28 | .keyboard-open .hide-on-keyboard-open {
29 | display: none;
30 | }
31 |
32 | .keyboard-open .tabs.hide-on-keyboard-open + .pane .has-tabs,
33 | .keyboard-open .bar-footer.hide-on-keyboard-open + .pane .has-footer {
34 | bottom: 0;
35 | }
36 |
37 | .inline {
38 | display: inline-block;
39 | }
40 |
41 | .disable-pointer-events {
42 | pointer-events: none;
43 | }
44 |
45 | .enable-pointer-events {
46 | pointer-events: auto;
47 | }
48 |
49 | .disable-user-behavior {
50 | // used to prevent the browser from doing its native behavior. this doesnt
51 | // prevent the scrolling, but cancels the contextmenu, tap highlighting, etc
52 |
53 | @include user-select(none);
54 | @include touch-callout(none);
55 | @include tap-highlight-transparent();
56 |
57 | -webkit-user-drag: none;
58 |
59 | -ms-touch-action: none;
60 | -ms-content-zooming: none;
61 | }
62 |
63 | // Fill the screen to block clicks (a better pointer-events: none) for the body
64 | // to avoid full-page reflows and paints which can cause flickers
65 | .click-block {
66 | position: absolute;
67 | top: 0;
68 | left: 0;
69 | z-index: $z-index-click-block;
70 | width: 100%;
71 | height: 100%;
72 | background: transparent;
73 | }
74 |
75 | .no-resize {
76 | resize: none;
77 | }
78 |
79 | .block {
80 | display: block;
81 | clear: both;
82 | &:after {
83 | display: block;
84 | visibility: hidden;
85 | clear: both;
86 | height: 0;
87 | content: ".";
88 | }
89 | }
90 |
91 | .full-image {
92 | width: 100%;
93 | }
94 |
95 | .clearfix {
96 | *zoom: 1;
97 | &:before,
98 | &:after {
99 | display: table;
100 | content: "";
101 | // Fixes Opera/contenteditable bug:
102 | // http://nicolasgallagher.com/micro-clearfix-hack/#comment-36952
103 | line-height: 0;
104 | }
105 | &:after {
106 | clear: both;
107 | }
108 | }
109 |
110 | /**
111 | * Content Padding
112 | * --------------------------------------------------
113 | */
114 |
115 | .padding {
116 | padding: $content-padding;
117 | }
118 |
119 | .padding-top,
120 | .padding-vertical {
121 | padding-top: $content-padding;
122 | }
123 |
124 | .padding-right,
125 | .padding-horizontal {
126 | padding-right: $content-padding;
127 | }
128 |
129 | .padding-bottom,
130 | .padding-vertical {
131 | padding-bottom: $content-padding;
132 | }
133 |
134 | .padding-left,
135 | .padding-horizontal {
136 | padding-left: $content-padding;
137 | }
138 |
139 |
140 | /**
141 | * Rounded
142 | * --------------------------------------------------
143 | */
144 |
145 | .rounded {
146 | border-radius: $border-radius-base;
147 | }
148 |
149 |
150 | /**
151 | * Utility Colors
152 | * --------------------------------------------------
153 | * Utility colors are added to help set a naming convention. You'll
154 | * notice we purposely do not use words like "red" or "blue", but
155 | * instead have colors which represent an emotion or generic theme.
156 | */
157 |
158 | .light, a.light {
159 | color: $light;
160 | }
161 | .light-bg {
162 | background-color: $light;
163 | }
164 | .light-border {
165 | border-color: $button-light-border;
166 | }
167 |
168 | .stable, a.stable {
169 | color: $stable;
170 | }
171 | .stable-bg {
172 | background-color: $stable;
173 | }
174 | .stable-border {
175 | border-color: $button-stable-border;
176 | }
177 |
178 | .positive, a.positive {
179 | color: $positive;
180 | }
181 | .positive-bg {
182 | background-color: $positive;
183 | }
184 | .positive-border {
185 | border-color: $button-positive-border;
186 | }
187 |
188 | .calm, a.calm {
189 | color: $calm;
190 | }
191 | .calm-bg {
192 | background-color: $calm;
193 | }
194 | .calm-border {
195 | border-color: $button-calm-border;
196 | }
197 |
198 | .assertive, a.assertive {
199 | color: $assertive;
200 | }
201 | .assertive-bg {
202 | background-color: $assertive;
203 | }
204 | .assertive-border {
205 | border-color: $button-assertive-border;
206 | }
207 |
208 | .balanced, a.balanced {
209 | color: $balanced;
210 | }
211 | .balanced-bg {
212 | background-color: $balanced;
213 | }
214 | .balanced-border {
215 | border-color: $button-balanced-border;
216 | }
217 |
218 | .energized, a.energized {
219 | color: $energized;
220 | }
221 | .energized-bg {
222 | background-color: $energized;
223 | }
224 | .energized-border {
225 | border-color: $button-energized-border;
226 | }
227 |
228 | .royal, a.royal {
229 | color: $royal;
230 | }
231 | .royal-bg {
232 | background-color: $royal;
233 | }
234 | .royal-border {
235 | border-color: $button-royal-border;
236 | }
237 |
238 | .dark, a.dark {
239 | color: $dark;
240 | }
241 | .dark-bg {
242 | background-color: $dark;
243 | }
244 | .dark-border {
245 | border-color: $button-dark-border;
246 | }
247 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_form.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Forms
3 | * --------------------------------------------------
4 | */
5 |
6 | // Make all forms have space below them
7 | form {
8 | margin: 0 0 $line-height-base;
9 | }
10 |
11 | // Groups of fields with labels on top (legends)
12 | legend {
13 | display: block;
14 | margin-bottom: $line-height-base;
15 | padding: 0;
16 | width: 100%;
17 | border: $input-border-width solid $input-border;
18 | color: $dark;
19 | font-size: $font-size-base * 1.5;
20 | line-height: $line-height-base * 2;
21 |
22 | small {
23 | color: $stable;
24 | font-size: $line-height-base * .75;
25 | }
26 | }
27 |
28 | // Set font for forms
29 | label,
30 | input,
31 | button,
32 | select,
33 | textarea {
34 | @include font-shorthand($font-size-base, normal, $line-height-base); // Set size, weight, line-height here
35 | }
36 | input,
37 | button,
38 | select,
39 | textarea {
40 | font-family: $font-family-base; // And only set font-family here for those that need it (note the missing label element)
41 | }
42 |
43 |
44 | // Input List
45 | // -------------------------------
46 |
47 | .item-input {
48 | @include display-flex();
49 | @include align-items(center);
50 | position: relative;
51 | overflow: hidden;
52 | padding: 6px 0 5px 16px;
53 |
54 | input {
55 | @include border-radius(0);
56 | @include flex(1, 0, 220px);
57 | @include appearance(none);
58 | margin: 0;
59 | padding-right: 24px;
60 | background-color: transparent;
61 | }
62 |
63 | .button .icon {
64 | @include flex(0, 0, 24px);
65 | position: static;
66 | display: inline-block;
67 | height: auto;
68 | text-align: center;
69 | font-size: 16px;
70 | }
71 |
72 | .button-bar {
73 | @include border-radius(0);
74 | @include flex(1, 0, 220px);
75 | @include appearance(none);
76 | }
77 |
78 | .icon {
79 | min-width: 14px;
80 | }
81 | }
82 |
83 | .item-input-inset {
84 | @include display-flex();
85 | @include align-items(center);
86 | position: relative;
87 | overflow: hidden;
88 | padding: ($item-padding / 3) * 2;
89 | }
90 |
91 | .item-input-wrapper {
92 | @include display-flex();
93 | @include flex(1, 0);
94 | @include align-items(center);
95 | @include border-radius(4px);
96 | padding-right: 8px;
97 | padding-left: 8px;
98 | background: #eee;
99 | }
100 |
101 | .item-input-inset .item-input-wrapper input {
102 | padding-left: 4px;
103 | height: 29px;
104 | background: transparent;
105 | line-height: 18px;
106 | }
107 |
108 | .item-input-wrapper ~ .button {
109 | margin-left: ($item-padding / 3) * 2;
110 | }
111 |
112 | .input-label {
113 | @include flex(1, 0, 100px);
114 | display: table;
115 | padding: 7px 10px 7px 0px;
116 | max-width: 200px;
117 | width: 35%;
118 | color: $input-label-color;
119 | font-size: 16px;
120 | }
121 |
122 | .placeholder-icon {
123 | color: #aaa;
124 | &:first-child {
125 | padding-right: 6px;
126 | }
127 | &:last-child {
128 | padding-left: 6px;
129 | }
130 | }
131 |
132 | .item-stacked-label {
133 | display: block;
134 | background-color: transparent;
135 | box-shadow: none;
136 |
137 | .input-label, .icon {
138 | display: inline-block;
139 | padding: 4px 0 0 0px;
140 | vertical-align: middle;
141 | }
142 | }
143 |
144 | .item-stacked-label input,
145 | .item-stacked-label textarea {
146 | @include border-radius(2px);
147 | padding: 4px 8px 3px 0;
148 | border: none;
149 | background-color: $input-bg;
150 | }
151 | .item-stacked-label input {
152 | overflow: hidden;
153 | height: $line-height-computed + $font-size-base + 12px;
154 | }
155 |
156 | .item-floating-label {
157 | display: block;
158 | background-color: transparent;
159 | box-shadow: none;
160 |
161 | .input-label {
162 | position: relative;
163 | padding: 5px 0 0 0;
164 | opacity: 0;
165 | top: 10px;
166 | @include transition(opacity .15s ease-in, top .2s linear);
167 |
168 | &.has-input {
169 | opacity: 1;
170 | top: 0;
171 | @include transition(opacity .15s ease-in, top .2s linear);
172 | }
173 | }
174 | }
175 |
176 |
177 | // Form Controls
178 | // -------------------------------
179 |
180 | // Shared size and type resets
181 | textarea,
182 | input[type="text"],
183 | input[type="password"],
184 | input[type="datetime"],
185 | input[type="datetime-local"],
186 | input[type="date"],
187 | input[type="month"],
188 | input[type="time"],
189 | input[type="week"],
190 | input[type="number"],
191 | input[type="email"],
192 | input[type="url"],
193 | input[type="search"],
194 | input[type="tel"],
195 | input[type="color"] {
196 | display: block;
197 | padding-top: 2px;
198 | padding-left: 0;
199 | height: $line-height-computed + $font-size-base;
200 | color: $input-color;
201 | vertical-align: middle;
202 | font-size: $font-size-base;
203 | line-height: $font-size-base + 2;
204 | }
205 |
206 | .platform-ios,
207 | .platform-android {
208 | input[type="datetime-local"],
209 | input[type="date"],
210 | input[type="month"],
211 | input[type="time"],
212 | input[type="week"] {
213 | padding-top: 8px;
214 | }
215 | }
216 |
217 | input,
218 | textarea {
219 | width: 100%;
220 | }
221 | textarea {
222 | padding-left: 0;
223 | @include placeholder($input-color-placeholder, -3px);
224 | }
225 |
226 | // Reset height since textareas have rows
227 | textarea {
228 | height: auto;
229 | }
230 |
231 | // Everything else
232 | textarea,
233 | input[type="text"],
234 | input[type="password"],
235 | input[type="datetime"],
236 | input[type="datetime-local"],
237 | input[type="date"],
238 | input[type="month"],
239 | input[type="time"],
240 | input[type="week"],
241 | input[type="number"],
242 | input[type="email"],
243 | input[type="url"],
244 | input[type="search"],
245 | input[type="tel"],
246 | input[type="color"] {
247 | border: 0;
248 | }
249 |
250 | // Position radios and checkboxes better
251 | input[type="radio"],
252 | input[type="checkbox"] {
253 | margin: 0;
254 | line-height: normal;
255 | }
256 |
257 | // Reset width of input images, buttons, radios, checkboxes
258 | input[type="file"],
259 | input[type="image"],
260 | input[type="submit"],
261 | input[type="reset"],
262 | input[type="button"],
263 | input[type="radio"],
264 | input[type="checkbox"] {
265 | width: auto; // Override of generic input selector
266 | }
267 |
268 | // Set the height of file to match text inputs
269 | input[type="file"] {
270 | line-height: $input-height-base;
271 | }
272 |
273 | // Text input classes to hide text caret during scroll
274 | .previous-input-focus,
275 | .cloned-text-input + input,
276 | .cloned-text-input + textarea {
277 | position: absolute !important;
278 | left: -9999px;
279 | width: 200px;
280 | }
281 |
282 |
283 | // Placeholder
284 | // -------------------------------
285 | input,
286 | textarea {
287 | @include placeholder();
288 | }
289 |
290 |
291 | // DISABLED STATE
292 | // -------------------------------
293 |
294 | // Disabled and read-only inputs
295 | input[disabled],
296 | select[disabled],
297 | textarea[disabled],
298 | input[readonly]:not(.cloned-text-input),
299 | textarea[readonly]:not(.cloned-text-input),
300 | select[readonly] {
301 | background-color: $input-bg-disabled;
302 | cursor: not-allowed;
303 | }
304 | // Explicitly reset the colors here
305 | input[type="radio"][disabled],
306 | input[type="checkbox"][disabled],
307 | input[type="radio"][readonly],
308 | input[type="checkbox"][readonly] {
309 | background-color: transparent;
310 | }
311 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_button.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Buttons
4 | * --------------------------------------------------
5 | */
6 |
7 | .button {
8 | // set the color defaults
9 | @include button-style($button-default-bg, $button-default-border, $button-default-active-bg, $button-default-active-border, $button-default-text);
10 |
11 | position: relative;
12 | display: inline-block;
13 | margin: 0;
14 | padding: 0 $button-padding;
15 |
16 | min-width: ($button-padding * 3) + $button-font-size;
17 | min-height: $button-height + 5px;
18 |
19 | border-width: $button-border-width;
20 | border-style: solid;
21 | border-radius: $button-border-radius;
22 |
23 | vertical-align: top;
24 | text-align: center;
25 |
26 | text-overflow: ellipsis;
27 | font-size: $button-font-size;
28 | line-height: $button-height - $button-border-width + 1px;
29 |
30 | cursor: pointer;
31 |
32 | &:after {
33 | // used to create a larger button "hit" area
34 | position: absolute;
35 | top: -6px;
36 | right: -6px;
37 | bottom: -6px;
38 | left: -6px;
39 | content: ' ';
40 | }
41 |
42 | .icon {
43 | vertical-align: top;
44 | pointer-events: none;
45 | }
46 |
47 | .icon:before,
48 | &.icon:before,
49 | &.icon-left:before,
50 | &.icon-right:before {
51 | display: inline-block;
52 | padding: 0 0 $button-border-width 0;
53 | vertical-align: inherit;
54 | font-size: $button-icon-size;
55 | line-height: $button-height - $button-border-width;
56 | pointer-events: none;
57 | }
58 | &.icon-left:before {
59 | float: left;
60 | padding-right: .2em;
61 | padding-left: 0;
62 | }
63 | &.icon-right:before {
64 | float: right;
65 | padding-right: 0;
66 | padding-left: .2em;
67 | }
68 |
69 | &.button-block, &.button-full {
70 | margin-top: $button-block-margin;
71 | margin-bottom: $button-block-margin;
72 | }
73 |
74 | &.button-light {
75 | @include button-style($button-light-bg, $button-light-border, $button-light-active-bg, $button-light-active-border, $button-light-text);
76 | @include button-clear($button-light-border);
77 | @include button-outline($button-light-border);
78 | }
79 |
80 | &.button-stable {
81 | @include button-style($button-stable-bg, $button-stable-border, $button-stable-active-bg, $button-stable-active-border, $button-stable-text);
82 | @include button-clear($button-stable-border);
83 | @include button-outline($button-stable-border);
84 | }
85 |
86 | &.button-positive {
87 | @include button-style($button-positive-bg, $button-positive-border, $button-positive-active-bg, $button-positive-active-border, $button-positive-text);
88 | @include button-clear($button-positive-bg);
89 | @include button-outline($button-positive-bg);
90 | }
91 |
92 | &.button-calm {
93 | @include button-style($button-calm-bg, $button-calm-border, $button-calm-active-bg, $button-calm-active-border, $button-calm-text);
94 | @include button-clear($button-calm-bg);
95 | @include button-outline($button-calm-bg);
96 | }
97 |
98 | &.button-assertive {
99 | @include button-style($button-assertive-bg, $button-assertive-border, $button-assertive-active-bg, $button-assertive-active-border, $button-assertive-text);
100 | @include button-clear($button-assertive-bg);
101 | @include button-outline($button-assertive-bg);
102 | }
103 |
104 | &.button-balanced {
105 | @include button-style($button-balanced-bg, $button-balanced-border, $button-balanced-active-bg, $button-balanced-active-border, $button-balanced-text);
106 | @include button-clear($button-balanced-bg);
107 | @include button-outline($button-balanced-bg);
108 | }
109 |
110 | &.button-energized {
111 | @include button-style($button-energized-bg, $button-energized-border, $button-energized-active-bg, $button-energized-active-border, $button-energized-text);
112 | @include button-clear($button-energized-bg);
113 | @include button-outline($button-energized-bg);
114 | }
115 |
116 | &.button-royal {
117 | @include button-style($button-royal-bg, $button-royal-border, $button-royal-active-bg, $button-royal-active-border, $button-royal-text);
118 | @include button-clear($button-royal-bg);
119 | @include button-outline($button-royal-bg);
120 | }
121 |
122 | &.button-dark {
123 | @include button-style($button-dark-bg, $button-dark-border, $button-dark-active-bg, $button-dark-active-border, $button-dark-text);
124 | @include button-clear($button-dark-bg);
125 | @include button-outline($button-dark-bg);
126 | }
127 | }
128 |
129 | .button-small {
130 | padding: 2px $button-small-padding 1px;
131 | min-width: $button-small-height;
132 | min-height: $button-small-height + 2;
133 | font-size: $button-small-font-size;
134 | line-height: $button-small-height - $button-border-width - 1;
135 |
136 | .icon:before,
137 | &.icon:before,
138 | &.icon-left:before,
139 | &.icon-right:before {
140 | font-size: $button-small-icon-size;
141 | line-height: $button-small-icon-size + 3;
142 | margin-top: 3px;
143 | }
144 | }
145 |
146 | .button-large {
147 | padding: 0 $button-large-padding;
148 | min-width: ($button-large-padding * 3) + $button-large-font-size;
149 | min-height: $button-large-height + 5;
150 | font-size: $button-large-font-size;
151 | line-height: $button-large-height - $button-border-width;
152 |
153 | .icon:before,
154 | &.icon:before,
155 | &.icon-left:before,
156 | &.icon-right:before {
157 | padding-bottom: ($button-border-width * 2);
158 | font-size: $button-large-icon-size;
159 | line-height: $button-large-height - ($button-border-width * 2) - 1;
160 | }
161 | }
162 |
163 | .button-icon {
164 | @include transition(opacity .1s);
165 | padding: 0 6px;
166 | min-width: initial;
167 | border-color: transparent;
168 | background: none;
169 |
170 | &.button.active,
171 | &.button.activated {
172 | border-color: transparent;
173 | background: none;
174 | box-shadow: none;
175 | opacity: 0.3;
176 | }
177 |
178 | .icon:before,
179 | &.icon:before {
180 | font-size: $button-large-icon-size;
181 | }
182 | }
183 |
184 | .button-clear {
185 | @include button-clear($button-default-border);
186 | @include transition(opacity .1s);
187 | padding: 0 $button-clear-padding;
188 | max-height: $button-height;
189 | border-color: transparent;
190 | background: none;
191 | box-shadow: none;
192 |
193 | &.active,
194 | &.activated {
195 | opacity: 0.3;
196 | }
197 | }
198 |
199 | .button-outline {
200 | @include button-outline($button-default-border);
201 | @include transition(opacity .1s);
202 | background: none;
203 | box-shadow: none;
204 | }
205 |
206 | .padding > .button.button-block:first-child {
207 | margin-top: 0;
208 | }
209 |
210 | .button-block {
211 | display: block;
212 | clear: both;
213 |
214 | &:after {
215 | clear: both;
216 | }
217 | }
218 |
219 | .button-full,
220 | .button-full > .button {
221 | display: block;
222 | margin-right: 0;
223 | margin-left: 0;
224 | border-right-width: 0;
225 | border-left-width: 0;
226 | border-radius: 0;
227 | }
228 |
229 | button.button-block,
230 | button.button-full,
231 | .button-full > button.button,
232 | input.button.button-block {
233 | width: 100%;
234 | }
235 |
236 | a.button {
237 | text-decoration: none;
238 |
239 | .icon:before,
240 | &.icon:before,
241 | &.icon-left:before,
242 | &.icon-right:before {
243 | margin-top: 2px;
244 | }
245 | }
246 |
247 | .button.disabled,
248 | .button[disabled] {
249 | opacity: .4;
250 | cursor: default !important;
251 | pointer-events: none;
252 | }
253 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_reset.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Resets
4 | * --------------------------------------------------
5 | * Adapted from normalize.css and some reset.css. We don't care even one
6 | * bit about old IE, so we don't need any hacks for that in here.
7 | *
8 | * There are probably other things we could remove here, as well.
9 | *
10 | * normalize.css v2.1.2 | MIT License | git.io/normalize
11 |
12 | * Eric Meyer's Reset CSS v2.0 (http://meyerweb.com/eric/tools/css/reset/)
13 | * http://cssreset.com
14 | */
15 |
16 | html, body, div, span, applet, object, iframe,
17 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
18 | a, abbr, acronym, address, big, cite, code,
19 | del, dfn, em, img, ins, kbd, q, s, samp,
20 | small, strike, strong, sub, sup, tt, var,
21 | b, i, u, center,
22 | dl, dt, dd, ol, ul, li,
23 | fieldset, form, label, legend,
24 | table, caption, tbody, tfoot, thead, tr, th, td,
25 | article, aside, canvas, details, embed, fieldset,
26 | figure, figcaption, footer, header, hgroup,
27 | menu, nav, output, ruby, section, summary,
28 | time, mark, audio, video {
29 | margin: 0;
30 | padding: 0;
31 | border: 0;
32 | vertical-align: baseline;
33 | font: inherit;
34 | font-size: 100%;
35 | }
36 |
37 | ol, ul {
38 | list-style: none;
39 | }
40 | blockquote, q {
41 | quotes: none;
42 | }
43 | blockquote:before, blockquote:after,
44 | q:before, q:after {
45 | content: '';
46 | content: none;
47 | }
48 |
49 | /**
50 | * Prevent modern browsers from displaying `audio` without controls.
51 | * Remove excess height in iOS 5 devices.
52 | */
53 |
54 | audio:not([controls]) {
55 | display: none;
56 | height: 0;
57 | }
58 |
59 | /**
60 | * Hide the `template` element in IE, Safari, and Firefox < 22.
61 | */
62 |
63 | [hidden],
64 | template {
65 | display: none;
66 | }
67 |
68 | script {
69 | display: none !important;
70 | }
71 |
72 | /* ==========================================================================
73 | Base
74 | ========================================================================== */
75 |
76 | /**
77 | * 1. Set default font family to sans-serif.
78 | * 2. Prevent iOS text size adjust after orientation change, without disabling
79 | * user zoom.
80 | */
81 |
82 | html {
83 | @include user-select(none);
84 | font-family: sans-serif; /* 1 */
85 | -webkit-text-size-adjust: 100%;
86 | -ms-text-size-adjust: 100%; /* 2 */
87 | -webkit-text-size-adjust: 100%; /* 2 */
88 | }
89 |
90 | /**
91 | * Remove default margin.
92 | */
93 |
94 | body {
95 | margin: 0;
96 | line-height: 1;
97 | }
98 |
99 |
100 | /**
101 | * Remove default outlines.
102 | */
103 | a,
104 | button,
105 | :focus,
106 | a:focus,
107 | button:focus,
108 | a:active,
109 | a:hover {
110 | outline: 0;
111 | }
112 |
113 | /* *
114 | * Remove tap highlight color
115 | */
116 |
117 | a {
118 | -webkit-user-drag: none;
119 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
120 | -webkit-tap-highlight-color: transparent;
121 |
122 | &[href]:hover {
123 | cursor: pointer;
124 | }
125 | }
126 |
127 | /* ==========================================================================
128 | Typography
129 | ========================================================================== */
130 |
131 |
132 | /**
133 | * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
134 | */
135 |
136 | b,
137 | strong {
138 | font-weight: bold;
139 | }
140 |
141 | /**
142 | * Address styling not present in Safari 5 and Chrome.
143 | */
144 |
145 | dfn {
146 | font-style: italic;
147 | }
148 |
149 | /**
150 | * Address differences between Firefox and other browsers.
151 | */
152 |
153 | hr {
154 | -moz-box-sizing: content-box;
155 | box-sizing: content-box;
156 | height: 0;
157 | }
158 |
159 |
160 | /**
161 | * Correct font family set oddly in Safari 5 and Chrome.
162 | */
163 |
164 | code,
165 | kbd,
166 | pre,
167 | samp {
168 | font-size: 1em;
169 | font-family: monospace, serif;
170 | }
171 |
172 | /**
173 | * Improve readability of pre-formatted text in all browsers.
174 | */
175 |
176 | pre {
177 | white-space: pre-wrap;
178 | }
179 |
180 | /**
181 | * Set consistent quote types.
182 | */
183 |
184 | q {
185 | quotes: "\201C" "\201D" "\2018" "\2019";
186 | }
187 |
188 | /**
189 | * Address inconsistent and variable font size in all browsers.
190 | */
191 |
192 | small {
193 | font-size: 80%;
194 | }
195 |
196 | /**
197 | * Prevent `sub` and `sup` affecting `line-height` in all browsers.
198 | */
199 |
200 | sub,
201 | sup {
202 | position: relative;
203 | vertical-align: baseline;
204 | font-size: 75%;
205 | line-height: 0;
206 | }
207 |
208 | sup {
209 | top: -0.5em;
210 | }
211 |
212 | sub {
213 | bottom: -0.25em;
214 | }
215 |
216 | /**
217 | * Define consistent border, margin, and padding.
218 | */
219 |
220 | fieldset {
221 | margin: 0 2px;
222 | padding: 0.35em 0.625em 0.75em;
223 | border: 1px solid #c0c0c0;
224 | }
225 |
226 | /**
227 | * 1. Correct `color` not being inherited in IE 8/9.
228 | * 2. Remove padding so people aren't caught out if they zero out fieldsets.
229 | */
230 |
231 | legend {
232 | padding: 0; /* 2 */
233 | border: 0; /* 1 */
234 | }
235 |
236 | /**
237 | * 1. Correct font family not being inherited in all browsers.
238 | * 2. Correct font size not being inherited in all browsers.
239 | * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome.
240 | * 4. Remove any default :focus styles
241 | * 5. Make sure webkit font smoothing is being inherited
242 | * 6. Remove default gradient in Android Firefox / FirefoxOS
243 | */
244 |
245 | button,
246 | input,
247 | select,
248 | textarea {
249 | margin: 0; /* 3 */
250 | font-size: 100%; /* 2 */
251 | font-family: inherit; /* 1 */
252 | outline-offset: 0; /* 4 */
253 | outline-style: none; /* 4 */
254 | outline-width: 0; /* 4 */
255 | -webkit-font-smoothing: inherit; /* 5 */
256 | background-image: none; /* 6 */
257 | }
258 |
259 | /**
260 | * Address Firefox 4+ setting `line-height` on `input` using `importnt` in
261 | * the UA stylesheet.
262 | */
263 |
264 | button,
265 | input {
266 | line-height: normal;
267 | }
268 |
269 | /**
270 | * Address inconsistent `text-transform` inheritance for `button` and `select`.
271 | * All other form control elements do not inherit `text-transform` values.
272 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+.
273 | * Correct `select` style inheritance in Firefox 4+ and Opera.
274 | */
275 |
276 | button,
277 | select {
278 | text-transform: none;
279 | }
280 |
281 | /**
282 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
283 | * and `video` controls.
284 | * 2. Correct inability to style clickable `input` types in iOS.
285 | * 3. Improve usability and consistency of cursor style between image-type
286 | * `input` and others.
287 | */
288 |
289 | button,
290 | html input[type="button"], /* 1 */
291 | input[type="reset"],
292 | input[type="submit"] {
293 | cursor: pointer; /* 3 */
294 | -webkit-appearance: button; /* 2 */
295 | }
296 |
297 | /**
298 | * Re-set default cursor for disabled elements.
299 | */
300 |
301 | button[disabled],
302 | html input[disabled] {
303 | cursor: default;
304 | }
305 |
306 | /**
307 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
308 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
309 | * (include `-moz` to future-proof).
310 | */
311 |
312 | input[type="search"] {
313 | -webkit-box-sizing: content-box; /* 2 */
314 | -moz-box-sizing: content-box;
315 | box-sizing: content-box;
316 | -webkit-appearance: textfield; /* 1 */
317 | }
318 |
319 | /**
320 | * Remove inner padding and search cancel button in Safari 5 and Chrome
321 | * on OS X.
322 | */
323 |
324 | input[type="search"]::-webkit-search-cancel-button,
325 | input[type="search"]::-webkit-search-decoration {
326 | -webkit-appearance: none;
327 | }
328 |
329 | /**
330 | * Remove inner padding and border in Firefox 4+.
331 | */
332 |
333 | button::-moz-focus-inner,
334 | input::-moz-focus-inner {
335 | padding: 0;
336 | border: 0;
337 | }
338 |
339 | /**
340 | * 1. Remove default vertical scrollbar in IE 8/9.
341 | * 2. Improve readability and alignment in all browsers.
342 | */
343 |
344 | textarea {
345 | overflow: auto; /* 1 */
346 | vertical-align: top; /* 2 */
347 | }
348 |
349 |
350 | img {
351 | -webkit-user-drag: none;
352 | }
353 |
354 | /* ==========================================================================
355 | Tables
356 | ========================================================================== */
357 |
358 | /**
359 | * Remove most spacing between table cells.
360 | */
361 |
362 | table {
363 | border-spacing: 0;
364 | border-collapse: collapse;
365 | }
366 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_scaffolding.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Scaffolding
4 | * --------------------------------------------------
5 | */
6 |
7 | *,
8 | *:before,
9 | *:after {
10 | @include box-sizing(border-box);
11 | }
12 |
13 | html {
14 | overflow: hidden;
15 | -ms-touch-action: pan-y;
16 | touch-action: pan-y;
17 | }
18 |
19 | body,
20 | .ionic-body {
21 | @include touch-callout(none);
22 | @include font-smoothing(antialiased);
23 | @include text-size-adjust(none);
24 | @include tap-highlight-transparent();
25 | @include user-select(none);
26 |
27 | top: 0;
28 | right: 0;
29 | bottom: 0;
30 | left: 0;
31 | overflow: hidden;
32 |
33 | margin: 0;
34 | padding: 0;
35 |
36 | color: $base-color;
37 | word-wrap: break-word;
38 | font-size: $font-size-base;
39 | font-family: $font-family-base;
40 | line-height: $line-height-computed;
41 | text-rendering: optimizeLegibility;
42 | -webkit-backface-visibility: hidden;
43 | -webkit-user-drag: none;
44 | }
45 |
46 | body.grade-b,
47 | body.grade-c {
48 | // disable optimizeLegibility for low end devices
49 | text-rendering: auto;
50 | }
51 |
52 | .content {
53 | // used for content areas not using the content directive
54 | position: relative;
55 | }
56 |
57 | .scroll-content {
58 | position: absolute;
59 | top: 0;
60 | right: 0;
61 | bottom: 0;
62 | left: 0;
63 | overflow: hidden;
64 |
65 | // Hide the top border if any
66 | margin-top: -1px;
67 |
68 | // Prevents any distortion of lines
69 | padding-top:1px;
70 |
71 | width: auto;
72 | height: auto;
73 | }
74 |
75 | .scroll-content-false,
76 | .menu .scroll-content.scroll-content-false{
77 | z-index: $z-index-scroll-content-false;
78 | }
79 |
80 | .scroll-view {
81 | position: relative;
82 | display: block;
83 | overflow: hidden;
84 |
85 | // Hide the top border if any
86 | margin-top: -1px;
87 | }
88 |
89 | /**
90 | * Scroll is the scroll view component available for complex and custom
91 | * scroll view functionality.
92 | */
93 | .scroll {
94 | @include user-select(none);
95 | @include touch-callout(none);
96 | @include text-size-adjust(none);
97 | @include transform-origin(left, top);
98 | }
99 |
100 | // hide webkit scrollbars
101 | ::-webkit-scrollbar {
102 | display:none;
103 | }
104 |
105 | // Scroll bar styles
106 | .scroll-bar {
107 | position: absolute;
108 | z-index: $z-index-scroll-bar;
109 | }
110 | // hide the scroll-bar during animations
111 | .ng-animate .scroll-bar {
112 | visibility: hidden;
113 | }
114 | .scroll-bar-h {
115 | right: 2px;
116 | bottom: 3px;
117 | left: 2px;
118 | height: 3px;
119 |
120 | .scroll-bar-indicator {
121 | height: 100%;
122 | }
123 | }
124 |
125 | .scroll-bar-v {
126 | top: 2px;
127 | right: 3px;
128 | bottom: 2px;
129 | width: 3px;
130 |
131 | .scroll-bar-indicator {
132 | width: 100%;
133 | }
134 | }
135 | .scroll-bar-indicator {
136 | position: absolute;
137 | border-radius: 4px;
138 | background: rgba(0,0,0,0.3);
139 | opacity: 1;
140 | @include transition(opacity .3s linear);
141 |
142 | &.scroll-bar-fade-out {
143 | opacity: 0;
144 | }
145 | }
146 | .grade-b .scroll-bar-indicator,
147 | .grade-c .scroll-bar-indicator {
148 | // disable rgba background and border radius for low end devices
149 | border-radius: 0;
150 | background: #aaa;
151 |
152 | &.scroll-bar-fade-out {
153 | @include transition(none);
154 | }
155 | }
156 |
157 | @keyframes refresh-spin {
158 | 0% { transform: translate3d(0,0,0) rotate(0); }
159 | 100% { transform: translate3d(0,0,0) rotate(180deg); }
160 | }
161 |
162 | @-webkit-keyframes refresh-spin {
163 | 0% {-webkit-transform: translate3d(0,0,0) rotate(0); }
164 | 100% {-webkit-transform: translate3d(0,0,0) rotate(180deg); }
165 | }
166 |
167 | @keyframes refresh-spin-back {
168 | 0% { transform: translate3d(0,0,0) rotate(180deg); }
169 | 100% { transform: translate3d(0,0,0) rotate(0); }
170 | }
171 |
172 | @-webkit-keyframes refresh-spin-back {
173 | 0% {-webkit-transform: translate3d(0,0,0) rotate(180deg); }
174 | 100% {-webkit-transform: translate3d(0,0,0) rotate(0); }
175 | }
176 |
177 | // Scroll refresher (for pull to refresh)
178 | .scroll-refresher {
179 | position: absolute;
180 | top: -60px;
181 | right: 0;
182 | left: 0;
183 | overflow: hidden;
184 | margin: auto;
185 | height: 60px;
186 |
187 | .ionic-refresher-content {
188 | position: absolute;
189 | bottom: 15px;
190 | left: 0;
191 | width: 100%;
192 | color: $scroll-refresh-icon-color;
193 | text-align: center;
194 |
195 | font-size: 30px;
196 |
197 | .text-refreshing,
198 | .text-pulling {
199 | font-size: 16px;
200 | line-height: 16px;
201 | }
202 | &.ionic-refresher-with-text {
203 | bottom: 10px;
204 | }
205 | }
206 |
207 | .icon-refreshing,
208 | .icon-pulling {
209 | width: 100%;
210 | -webkit-backface-visibility: hidden;
211 | -webkit-transform-style: preserve-3d;
212 | backface-visibility: hidden;
213 | transform-style: preserve-3d;
214 | }
215 | .icon-pulling {
216 | @include animation-name(refresh-spin-back);
217 | @include animation-duration(200ms);
218 | @include animation-timing-function(linear);
219 | @include animation-fill-mode(none);
220 | -webkit-transform: translate3d(0,0,0) rotate(0deg);
221 | transform: translate3d(0,0,0) rotate(0deg);
222 | }
223 | .icon-refreshing,
224 | .text-refreshing {
225 | display: none;
226 | }
227 | .icon-refreshing {
228 | @include animation-duration(1.5s);
229 | }
230 |
231 | &.active {
232 | .icon-pulling:not(.pulling-rotation-disabled) {
233 | @include animation-name(refresh-spin);
234 | -webkit-transform: translate3d(0,0,0) rotate(-180deg);
235 | transform: translate3d(0,0,0) rotate(-180deg);
236 | }
237 | &.refreshing {
238 | @include transition(transform .2s);
239 | @include transition(-webkit-transform .2s);
240 | -webkit-transform: scale(1,1);
241 | transform: scale(1,1);
242 | .icon-pulling,
243 | .text-pulling {
244 | display: none;
245 | }
246 | .icon-refreshing,
247 | .text-refreshing {
248 | display: block;
249 | }
250 | &.refreshing-tail{
251 | -webkit-transform: scale(0,0);
252 | transform: scale(0,0);
253 | }
254 | }
255 | }
256 | }
257 |
258 | ion-infinite-scroll {
259 | height: 60px;
260 | width: 100%;
261 | opacity: 0;
262 | display: block;
263 |
264 | @include transition(opacity 0.25s);
265 | @include display-flex();
266 | @include flex-direction(row);
267 | @include justify-content(center);
268 | @include align-items(center);
269 |
270 | .icon {
271 | color: #666666;
272 | font-size: 30px;
273 | color: $scroll-refresh-icon-color;
274 | }
275 |
276 | &.active {
277 | opacity: 1;
278 | }
279 | }
280 |
281 | .overflow-scroll {
282 | overflow-x: hidden;
283 | overflow-y: scroll;
284 | -webkit-overflow-scrolling: touch;
285 | top: 0;
286 | right: 0;
287 | bottom: 0;
288 | left: 0;
289 | position: absolute;
290 |
291 | .scroll {
292 | position: static;
293 | height: 100%;
294 | -webkit-transform: translate3d(0, 0, 0); // fix iOS bug where relative children of scroller disapear while scrolling. see: http://stackoverflow.com/questions/9807620/ipad-safari-scrolling-causes-html-elements-to-disappear-and-reappear-with-a-dela
295 | }
296 | }
297 |
298 |
299 | // Pad top/bottom of content so it doesn't hide behind .bar-title and .bar-tab.
300 | // Note: For these to work, content must come after both bars in the markup
301 | /* If you change these, change platform.scss as well */
302 | .has-header {
303 | top: $bar-height;
304 | }
305 | // Force no header
306 | .no-header {
307 | top: 0;
308 | }
309 |
310 | .has-subheader {
311 | top: $bar-height * 2;
312 | }
313 | .has-tabs-top {
314 | top: $bar-height + $tabs-height;
315 | }
316 | .has-header.has-subheader.has-tabs-top {
317 | top: 2 * $bar-height + $tabs-height;
318 | }
319 |
320 | .has-footer {
321 | bottom: $bar-height;
322 | }
323 | .has-subfooter {
324 | bottom: $bar-height * 2;
325 | }
326 |
327 | .has-tabs,
328 | .bar-footer.has-tabs {
329 | bottom: $tabs-height;
330 | }
331 |
332 | .has-footer.has-tabs {
333 | bottom: $tabs-height + $bar-height;
334 | }
335 |
336 | // A full screen section with a solid background
337 | .pane {
338 | @include translate3d(0,0,0);
339 | z-index: $z-index-pane;
340 | }
341 | .view {
342 | z-index: $z-index-view;
343 | }
344 | .pane,
345 | .view {
346 | position: absolute;
347 | top: 0;
348 | right: 0;
349 | bottom: 0;
350 | left: 0;
351 | width: 100%;
352 | height: 100%;
353 | background-color: $base-background-color;
354 | overflow: hidden;
355 | }
356 |
357 | ion-nav-view {
358 | position: absolute;
359 | top: 0;
360 | left: 0;
361 | width: 100%;
362 | height: 100%;
363 | background-color: #000;
364 | }
365 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_bar.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Bar (Headers and Footers)
4 | * --------------------------------------------------
5 | */
6 |
7 | .bar {
8 | @include display-flex();
9 | @include translate3d(0,0,0);
10 | @include user-select(none);
11 | position: absolute;
12 | right: 0;
13 | left: 0;
14 | z-index: $z-index-bar;
15 |
16 | box-sizing: border-box;
17 | padding: $bar-padding-portrait;
18 |
19 | width: 100%;
20 | height: $bar-height;
21 | border-width: 0;
22 | border-style: solid;
23 | border-top: 1px solid transparent;
24 | border-bottom: 1px solid $bar-default-border;
25 |
26 | background-color: $bar-default-bg;
27 |
28 | /* border-width: 1px will actually create 2 device pixels on retina */
29 | /* this nifty trick sets an actual 1px border on hi-res displays */
30 | background-size: 0;
31 | @media (min--moz-device-pixel-ratio: 1.5),
32 | (-webkit-min-device-pixel-ratio: 1.5),
33 | (min-device-pixel-ratio: 1.5),
34 | (min-resolution: 144dpi),
35 | (min-resolution: 1.5dppx) {
36 | border: none;
37 | background-image: linear-gradient(0deg, $bar-default-border, $bar-default-border 50%, transparent 50%);
38 | background-position: bottom;
39 | background-size: 100% 1px;
40 | background-repeat: no-repeat;
41 | }
42 |
43 | &.bar-clear {
44 | border: none;
45 | background: none;
46 | color: #fff;
47 |
48 | .button {
49 | color: #fff;
50 | }
51 | .title {
52 | color: #fff;
53 | }
54 | }
55 |
56 | &.item-input-inset {
57 | .item-input-wrapper {
58 | margin-top: -1px;
59 |
60 | input {
61 | padding-left: 8px;
62 | width: 94%;
63 | height: 28px;
64 | background: transparent;
65 | }
66 | }
67 | }
68 |
69 | &.bar-light {
70 | @include bar-style($bar-light-bg, $bar-light-border, $bar-light-text);
71 | &.bar-footer{
72 | background-image: linear-gradient(180deg, $bar-light-border, $bar-light-border 50%, transparent 50%);
73 | }
74 | }
75 | &.bar-stable {
76 | @include bar-style($bar-stable-bg, $bar-stable-border, $bar-stable-text);
77 | &.bar-footer{
78 | background-image: linear-gradient(180deg, $bar-stable-border, $bar-stable-border 50%, transparent 50%);
79 | }
80 | }
81 | &.bar-positive {
82 | @include bar-style($bar-positive-bg, $bar-positive-border, $bar-positive-text);
83 | &.bar-footer{
84 | background-image: linear-gradient(180deg, $bar-positive-border, $bar-positive-border 50%, transparent 50%);
85 | }
86 | }
87 | &.bar-calm {
88 | @include bar-style($bar-calm-bg, $bar-calm-border, $bar-calm-text);
89 | &.bar-footer{
90 | background-image: linear-gradient(180deg, $bar-calm-border, $bar-calm-border 50%, transparent 50%);
91 | }
92 | }
93 | &.bar-assertive {
94 | @include bar-style($bar-assertive-bg, $bar-assertive-border, $bar-assertive-text);
95 | &.bar-footer{
96 | background-image: linear-gradient(180deg, $bar-assertive-border, $bar-assertive-border 50%, transparent 50%);
97 | }
98 | }
99 | &.bar-balanced {
100 | @include bar-style($bar-balanced-bg, $bar-balanced-border, $bar-balanced-text);
101 | &.bar-footer{
102 | background-image: linear-gradient(180deg, $bar-balanced-border, $bar-positive-border 50%, transparent 50%);
103 | }
104 | }
105 | &.bar-energized {
106 | @include bar-style($bar-energized-bg, $bar-energized-border, $bar-energized-text);
107 | &.bar-footer{
108 | background-image: linear-gradient(180deg, $bar-energized-border, $bar-energized-border 50%, transparent 50%);
109 | }
110 | }
111 | &.bar-royal {
112 | @include bar-style($bar-royal-bg, $bar-royal-border, $bar-royal-text);
113 | &.bar-footer{
114 | background-image: linear-gradient(180deg, $bar-royal-border, $bar-royal-border 50%, transparent 50%);
115 | }
116 | }
117 | &.bar-dark {
118 | @include bar-style($bar-dark-bg, $bar-dark-border, $bar-dark-text);
119 | &.bar-footer{
120 | background-image: linear-gradient(180deg, $bar-dark-border, $bar-dark-border 50%, transparent 50%);
121 | }
122 | }
123 |
124 | // Title inside of a bar is centered
125 | .title {
126 | position: absolute;
127 |
128 | top: 0;
129 | right: 0;
130 | left: 0;
131 | z-index: $z-index-bar-title;
132 | overflow: hidden;
133 |
134 | margin: 0 10px;
135 |
136 | min-width: 30px;
137 | height: $bar-height - 1;
138 |
139 | text-align: center;
140 |
141 | // Go into ellipsis if too small
142 | text-overflow: ellipsis;
143 | white-space: nowrap;
144 |
145 | font-size: $bar-title-font-size;
146 |
147 | line-height: $bar-height;
148 |
149 | &.title-left {
150 | text-align: left;
151 | }
152 | &.title-right {
153 | text-align: right;
154 | }
155 | }
156 |
157 | .title a {
158 | color: inherit;
159 | }
160 |
161 | .button {
162 | z-index: $z-index-bar-button;
163 | padding: 0 $button-bar-button-padding;
164 | min-width: initial;
165 | min-height: $button-bar-button-height - 1;
166 | font-weight: 400;
167 | font-size: $button-bar-button-font-size;
168 | line-height: $button-bar-button-height;
169 |
170 | &.button-icon:before,
171 | .icon:before,
172 | &.icon:before,
173 | &.icon-left:before,
174 | &.icon-right:before {
175 | padding-right: 2px;
176 | padding-left: 2px;
177 | font-size: $button-bar-button-icon-size;
178 | line-height: $button-bar-button-height;
179 | }
180 |
181 | &.button-icon {
182 | font-size: $bar-title-font-size;
183 | .icon:before,
184 | &:before,
185 | &.icon-left:before,
186 | &.icon-right:before {
187 | vertical-align: top;
188 | font-size: $button-large-icon-size;
189 | line-height: $button-bar-button-height;
190 | }
191 | }
192 | &.button-clear {
193 | padding-right: 2px;
194 | padding-left: 2px;
195 | font-weight: 300;
196 | font-size: $bar-title-font-size;
197 |
198 | .icon:before,
199 | &.icon:before,
200 | &.icon-left:before,
201 | &.icon-right:before {
202 | font-size: $button-large-icon-size;
203 | line-height: $button-bar-button-height;
204 | }
205 | }
206 |
207 | &.back-button {
208 | padding: 0;
209 | opacity: 0.8;
210 | .back-button-title {
211 | display: inline-block;
212 | vertical-align: middle;
213 | margin-left: 4px;
214 | }
215 | }
216 |
217 | &.back-button.active,
218 | &.back-button.activated {
219 | opacity: 1;
220 | }
221 | }
222 |
223 | .button-bar > .button,
224 | .buttons > .button {
225 | min-height: $button-bar-button-height - 1;
226 | line-height: $button-bar-button-height;
227 | }
228 |
229 | .button-bar + .button,
230 | .button + .button-bar {
231 | margin-left: 5px;
232 | }
233 |
234 | // Android 4.4 messes with the display property
235 | .buttons,
236 | .buttons.left-buttons,
237 | .buttons.right-buttons {
238 | display: inherit;
239 | }
240 | .buttons span {
241 | display: inline-flex;
242 | }
243 |
244 | // Place the last button in a bar on the right of the bar
245 | .title + .button:last-child,
246 | > .button + .button:last-child,
247 | > .button.pull-right,
248 | .buttons.pull-right,
249 | .title + .buttons {
250 | position: absolute;
251 | top: 5px;
252 | right: 5px;
253 | bottom: 5px;
254 | }
255 |
256 | }
257 |
258 | // Default styles for buttons inside of styled bars
259 | .bar-light {
260 | .button {
261 | @include button-style($bar-light-bg, $bar-light-border, $bar-light-active-bg, $bar-light-active-border, $bar-light-text);
262 | @include button-clear($bar-light-text, $bar-title-font-size);
263 | }
264 | }
265 | .bar-stable {
266 | .button {
267 | @include button-style($bar-stable-bg, $bar-stable-border, $bar-stable-active-bg, $bar-stable-active-border, $bar-stable-text);
268 | @include button-clear($bar-stable-text, $bar-title-font-size);
269 | }
270 | }
271 | .bar-positive {
272 | .button {
273 | @include button-style($bar-positive-bg, $bar-positive-border, $bar-positive-active-bg, $bar-positive-active-border, $bar-positive-text);
274 | @include button-clear(#fff, $bar-title-font-size);
275 | }
276 | }
277 | .bar-calm {
278 | .button {
279 | @include button-style($bar-calm-bg, $bar-calm-border, $bar-calm-active-bg, $bar-calm-active-border, $bar-calm-text);
280 | @include button-clear(#fff, $bar-title-font-size);
281 | }
282 | }
283 | .bar-assertive {
284 | .button {
285 | @include button-style($bar-assertive-bg, $bar-assertive-border, $bar-assertive-active-bg, $bar-assertive-active-border, $bar-assertive-text);
286 | @include button-clear(#fff, $bar-title-font-size);
287 | }
288 | }
289 | .bar-balanced {
290 | .button {
291 | @include button-style($bar-balanced-bg, $bar-balanced-border, $bar-balanced-active-bg, $bar-balanced-active-border, $bar-balanced-text);
292 | @include button-clear(#fff, $bar-title-font-size);
293 | }
294 | }
295 | .bar-energized {
296 | .button {
297 | @include button-style($bar-energized-bg, $bar-energized-border, $bar-energized-active-bg, $bar-energized-active-border, $bar-energized-text);
298 | @include button-clear(#fff, $bar-title-font-size);
299 | }
300 | }
301 | .bar-royal {
302 | .button {
303 | @include button-style($bar-royal-bg, $bar-royal-border, $bar-royal-active-bg, $bar-royal-active-border, $bar-royal-text);
304 | @include button-clear(#fff, $bar-title-font-size);
305 | }
306 | }
307 | .bar-dark {
308 | .button {
309 | @include button-style($bar-dark-bg, $bar-dark-border, $bar-dark-active-bg, $bar-dark-active-border, $bar-dark-text);
310 | @include button-clear(#fff, $bar-title-font-size);
311 | }
312 | }
313 |
314 | // Header at top
315 | .bar-header {
316 | top: 0;
317 | border-top-width: 0;
318 | border-bottom-width: 1px;
319 | &.has-tabs-top{
320 | border-bottom-width: 0px;
321 | }
322 | }
323 |
324 | // Footer at bottom
325 | .bar-footer {
326 | bottom: 0;
327 | border-top-width: 1px;
328 | border-bottom-width: 0;
329 | background-position: top;
330 |
331 | &.item-input-inset {
332 | position: absolute;
333 | }
334 | }
335 |
336 | // Don't render padding if the bar is just for tabs
337 | .bar-tabs {
338 | padding: 0;
339 | }
340 |
341 | .bar-subheader {
342 | top: $bar-height;
343 | display: block;
344 | }
345 | .bar-subfooter {
346 | bottom: $bar-height;
347 | display: block;
348 | }
349 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/js/angular/angular-animate.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | AngularJS v1.2.25
3 | (c) 2010-2014 Google, Inc. http://angularjs.org
4 | License: MIT
5 | */
6 | (function(F,e,O){'use strict';e.module("ngAnimate",["ng"]).directive("ngAnimateChildren",function(){return function(G,s,g){g=g.ngAnimateChildren;e.isString(g)&&0===g.length?s.data("$$ngAnimateChildren",!0):G.$watch(g,function(e){s.data("$$ngAnimateChildren",!!e)})}}).factory("$$animateReflow",["$$rAF","$document",function(e,s){return function(g){return e(function(){g()})}}]).config(["$provide","$animateProvider",function(G,s){function g(e){for(var g=0;g=y&&b>=v&&e()}var h=g(b);a=b.data(q);if(-1!=h.getAttribute("class").indexOf(d)&&a){var m="";u(d.split(" "),function(a,b){m+=(0 .tabs,
46 | .tabs.tabs-light {
47 | @include tab-style($tabs-light-bg, $tabs-light-border, $tabs-light-text);
48 | @include tab-badge-style($tabs-light-text, $tabs-light-bg);
49 | }
50 | .tabs-stable > .tabs,
51 | .tabs.tabs-stable {
52 | @include tab-style($tabs-stable-bg, $tabs-stable-border, $tabs-stable-text);
53 | @include tab-badge-style($tabs-stable-text, $tabs-stable-bg);
54 | }
55 | .tabs-positive > .tabs,
56 | .tabs.tabs-positive {
57 | @include tab-style($tabs-positive-bg, $tabs-positive-border, $tabs-positive-text);
58 | @include tab-badge-style($tabs-positive-text, $tabs-positive-bg);
59 | }
60 | .tabs-calm > .tabs,
61 | .tabs.tabs-calm {
62 | @include tab-style($tabs-calm-bg, $tabs-calm-border, $tabs-calm-text);
63 | @include tab-badge-style($tabs-calm-text, $tabs-calm-bg);
64 | }
65 | .tabs-assertive > .tabs,
66 | .tabs.tabs-assertive {
67 | @include tab-style($tabs-assertive-bg, $tabs-assertive-border, $tabs-assertive-text);
68 | @include tab-badge-style($tabs-assertive-text, $tabs-assertive-bg);
69 | }
70 | .tabs-balanced > .tabs,
71 | .tabs.tabs-balanced {
72 | @include tab-style($tabs-balanced-bg, $tabs-balanced-border, $tabs-balanced-text);
73 | @include tab-badge-style($tabs-balanced-text, $tabs-balanced-bg);
74 | }
75 | .tabs-energized > .tabs,
76 | .tabs.tabs-energized {
77 | @include tab-style($tabs-energized-bg, $tabs-energized-border, $tabs-energized-text);
78 | @include tab-badge-style($tabs-energized-text, $tabs-energized-bg);
79 | }
80 | .tabs-royal > .tabs,
81 | .tabs.tabs-royal {
82 | @include tab-style($tabs-royal-bg, $tabs-royal-border, $tabs-royal-text);
83 | @include tab-badge-style($tabs-royal-text, $tabs-royal-bg);
84 | }
85 | .tabs-dark > .tabs,
86 | .tabs.tabs-dark {
87 | @include tab-style($tabs-dark-bg, $tabs-dark-border, $tabs-dark-text);
88 | @include tab-badge-style($tabs-dark-text, $tabs-dark-bg);
89 | }
90 |
91 | @mixin tabs-striped($style, $color, $background) {
92 | &.#{$style} {
93 | .tabs{
94 | background-color: $background;
95 | }
96 | .tab-item {
97 | color: rgba($color, $tabs-striped-off-opacity);
98 | opacity: 1;
99 | .badge{
100 | opacity:$tabs-striped-off-opacity;
101 | }
102 | &.tab-item-active,
103 | &.active,
104 | &.activated {
105 | margin-top: -$tabs-striped-border-width;
106 | color: $color;
107 | border-style: solid;
108 | border-width: $tabs-striped-border-width 0 0 0;
109 | border-color: $color;
110 | .badge{
111 | top:$tabs-striped-border-width;
112 | opacity: 1;
113 | }
114 | }
115 | }
116 | }
117 | &.tabs-top{
118 | .tab-item {
119 | &.tab-item-active,
120 | &.active,
121 | &.activated {
122 | .badge {
123 | top: 4%;
124 | }
125 | }
126 | }
127 | }
128 | }
129 |
130 | @mixin tabs-background($style, $color) {
131 | &.#{$style} {
132 | .tabs {
133 | background-color: $color;
134 | }
135 | }
136 | }
137 |
138 | @mixin tabs-color($style, $color) {
139 | &.#{$style} {
140 | .tab-item {
141 | color: rgba($color, $tabs-striped-off-opacity);
142 | opacity: 1;
143 | .badge{
144 | opacity:$tabs-striped-off-opacity;
145 | }
146 | &.tab-item-active,
147 | &.active,
148 | &.activated {
149 | margin-top: -$tabs-striped-border-width;
150 | color: $color;
151 | border: 0 solid $color;
152 | border-top-width: $tabs-striped-border-width;
153 | .badge{
154 | top:$tabs-striped-border-width;
155 | opacity: 1;
156 | }
157 | }
158 | }
159 | }
160 | }
161 |
162 | .tabs-striped {
163 | .tabs {
164 | background-color: white;
165 | background-image: none;
166 | border: none;
167 | box-shadow: 0 2px 3px rgba(0, 0, 0, 0.15);
168 | padding-top: $tabs-striped-border-width;
169 | }
170 | .tab-item {
171 | // default android tab style
172 | &.tab-item-active,
173 | &.active,
174 | &.activated {
175 | margin-top: -$tabs-striped-border-width;
176 | border-style: solid;
177 | border-width: $tabs-striped-border-width 0 0 0;
178 | border-color: $dark;
179 | }
180 | }
181 | @include tabs-striped('tabs-light', $light, $dark);
182 | @include tabs-striped('tabs-stable', $stable, $dark);
183 | @include tabs-striped('tabs-positive', $positive, $light);
184 | @include tabs-striped('tabs-calm', $calm, $light);
185 | @include tabs-striped('tabs-assertive', $assertive, $light);
186 | @include tabs-striped('tabs-balanced', $balanced, $light);
187 | @include tabs-striped('tabs-energized', $energized, $light);
188 | @include tabs-striped('tabs-royal', $royal, $light);
189 | @include tabs-striped('tabs-dark', $dark, $light);
190 |
191 |
192 | @include tabs-background('tabs-background-light', $light);
193 | @include tabs-background('tabs-background-stable', $stable);
194 | @include tabs-background('tabs-background-positive', $positive);
195 | @include tabs-background('tabs-background-calm', $calm);
196 | @include tabs-background('tabs-background-assertive', $assertive);
197 | @include tabs-background('tabs-background-balanced', $balanced);
198 | @include tabs-background('tabs-background-energized',$energized);
199 | @include tabs-background('tabs-background-royal', $royal);
200 | @include tabs-background('tabs-background-dark', $dark);
201 |
202 | @include tabs-color('tabs-color-light', $light);
203 | @include tabs-color('tabs-color-stable', $stable);
204 | @include tabs-color('tabs-color-positive', $positive);
205 | @include tabs-color('tabs-color-calm', $calm);
206 | @include tabs-color('tabs-color-assertive', $assertive);
207 | @include tabs-color('tabs-color-balanced', $balanced);
208 | @include tabs-color('tabs-color-energized',$energized);
209 | @include tabs-color('tabs-color-royal', $royal);
210 | @include tabs-color('tabs-color-dark', $dark);
211 | }
212 |
213 | .tabs-top {
214 | &.tabs-striped {
215 | padding-bottom:0;
216 | .tab-item{
217 | background: transparent;
218 | // animate the top bar, leave bottom for platform consistency
219 | -webkit-transition: all .1s ease;
220 | -moz-transition: all .1s ease;
221 | -ms-transition: all .1s ease;
222 | -o-transition: all .1s ease;
223 | transition: all .1s ease;
224 | &.tab-item-active,
225 | &.active,
226 | &.activated {
227 | margin-top: 0;
228 | margin-bottom: -$tabs-striped-border-width;
229 | border-width: 0px 0px $tabs-striped-border-width 0px !important;
230 | border-style: solid;
231 | }
232 | .badge{
233 | -webkit-transition: all .2s ease;
234 | -moz-transition: all .2s ease;
235 | -ms-transition: all .2s ease;
236 | -o-transition: all .2s ease;
237 | transition: all .2s ease;
238 | }
239 | }
240 | }
241 | }
242 |
243 | /* Allow parent element to have tabs-top */
244 | /* If you change this, change platform.scss as well */
245 | .tabs-top > .tabs,
246 | .tabs.tabs-top {
247 | top: $bar-height;
248 | padding-top: 0;
249 | background-position: bottom;
250 | .tab-item {
251 | &.tab-item-active,
252 | &.active,
253 | &.activated {
254 | .badge {
255 | top: 4%;
256 | }
257 | }
258 | }
259 | }
260 | .tabs-top ~ .bar-header {
261 | border-bottom-width: 0;
262 | }
263 |
264 | .tab-item {
265 | @include flex(1);
266 | display: block;
267 | overflow: hidden;
268 |
269 | max-width: $tab-item-max-width;
270 | height: 100%;
271 |
272 | color: inherit;
273 | text-align: center;
274 | text-decoration: none;
275 | text-overflow: ellipsis;
276 | white-space: nowrap;
277 |
278 | font-weight: 400;
279 | font-size: $tabs-text-font-size;
280 | font-family: $font-family-light-sans-serif;
281 |
282 | opacity: 0.7;
283 |
284 | &:hover {
285 | cursor: pointer;
286 | }
287 | &.tab-hidden{
288 | display:none;
289 | }
290 | }
291 |
292 | .tabs-item-hide > .tabs,
293 | .tabs.tabs-item-hide {
294 | display: none;
295 | }
296 |
297 | .tabs-icon-top > .tabs .tab-item,
298 | .tabs-icon-top.tabs .tab-item,
299 | .tabs-icon-bottom > .tabs .tab-item,
300 | .tabs-icon-bottom.tabs .tab-item {
301 | font-size: $tabs-text-font-size-side-icon;
302 | line-height: $tabs-text-font-size;
303 | }
304 |
305 | .tab-item .icon {
306 | display: block;
307 | margin: 0 auto;
308 | height: $tabs-icon-size;
309 | font-size: $tabs-icon-size;
310 | }
311 |
312 | .tabs-icon-left.tabs .tab-item,
313 | .tabs-icon-left > .tabs .tab-item,
314 | .tabs-icon-right.tabs .tab-item,
315 | .tabs-icon-right > .tabs .tab-item {
316 | font-size: $tabs-text-font-size-side-icon;
317 |
318 | .icon {
319 | display: inline-block;
320 | vertical-align: top;
321 | margin-top: -.1em;
322 |
323 | &:before {
324 | font-size: $tabs-icon-size - 8;
325 | line-height: $tabs-height;
326 | }
327 | }
328 | }
329 |
330 | .tabs-icon-left > .tabs .tab-item .icon,
331 | .tabs-icon-left.tabs .tab-item .icon {
332 | padding-right: 3px;
333 | }
334 |
335 | .tabs-icon-right > .tabs .tab-item .icon,
336 | .tabs-icon-right.tabs .tab-item .icon {
337 | padding-left: 3px;
338 | }
339 |
340 | .tabs-icon-only > .tabs .icon,
341 | .tabs-icon-only.tabs .icon {
342 | line-height: inherit;
343 | }
344 |
345 |
346 | .tab-item.has-badge {
347 | position: relative;
348 | }
349 |
350 | .tab-item .badge {
351 | position: absolute;
352 | top: 4%;
353 | right: 33%; // fallback
354 | right: calc(50% - 26px);
355 | padding: $tabs-badge-padding;
356 | height: auto;
357 | font-size: $tabs-badge-font-size;
358 | line-height: $tabs-badge-font-size + 4;
359 | }
360 |
361 |
362 | /* Navigational tab */
363 |
364 | /* Active state for tab */
365 | .tab-item.tab-item-active,
366 | .tab-item.active,
367 | .tab-item.activated {
368 | opacity: 1;
369 |
370 | &.tab-item-light {
371 | color: $light;
372 | }
373 | &.tab-item-stable {
374 | color: $stable;
375 | }
376 | &.tab-item-positive {
377 | color: $positive;
378 | }
379 | &.tab-item-calm {
380 | color: $calm;
381 | }
382 | &.tab-item-assertive {
383 | color: $assertive;
384 | }
385 | &.tab-item-balanced {
386 | color: $balanced;
387 | }
388 | &.tab-item-energized {
389 | color: $energized;
390 | }
391 | &.tab-item-royal {
392 | color: $royal;
393 | }
394 | &.tab-item-dark {
395 | color: $dark;
396 | }
397 | }
398 |
399 | .item.tabs {
400 | @include display-flex();
401 | padding: 0;
402 |
403 | .icon:before {
404 | position: relative;
405 | }
406 | }
407 |
408 | .tab-item.disabled,
409 | .tab-item[disabled] {
410 | opacity: .4;
411 | cursor: default;
412 | pointer-events: none;
413 | }
414 |
415 | /** Platform styles **/
416 |
417 | .tab-item.tab-item-ios {
418 | }
419 | .tab-item.tab-item-android {
420 | border-top: 2px solid inherit;
421 | }
422 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/scss/_mixins.scss:
--------------------------------------------------------------------------------
1 |
2 | // Button Mixins
3 | // --------------------------------------------------
4 |
5 | @mixin button-style($bg-color, $border-color, $active-bg-color, $active-border-color, $color) {
6 | border-color: $border-color;
7 | background-color: $bg-color;
8 | color: $color;
9 |
10 | // Give desktop users something to play with
11 | &:hover {
12 | color: $color;
13 | text-decoration: none;
14 | }
15 | &.active,
16 | &.activated {
17 | border-color: $active-border-color;
18 | background-color: $active-bg-color;
19 | box-shadow: inset 0px 1px 3px rgba(0,0,0,0.15);
20 | }
21 | }
22 |
23 | @mixin button-clear($color, $font-size:"") {
24 | &.button-clear {
25 | border-color: transparent;
26 | background: none;
27 | box-shadow: none;
28 | color: $color;
29 |
30 | @if $font-size != "" {
31 | font-size: $font-size;
32 | }
33 | }
34 | &.button-icon {
35 | border-color: transparent;
36 | background: none;
37 | }
38 | }
39 |
40 | @mixin button-outline($color, $text-color:"") {
41 | &.button-outline {
42 | border-color: $color;
43 | background: transparent;
44 | @if $text-color == "" {
45 | $text-color: $color;
46 | }
47 | color: $text-color;
48 | &.active,
49 | &.activated {
50 | background-color: $color;
51 | box-shadow: none;
52 | color: #fff;
53 | }
54 | }
55 | }
56 |
57 |
58 | // Bar Mixins
59 | // --------------------------------------------------
60 |
61 | @mixin bar-style($bg-color, $border-color, $color) {
62 | border-color: $border-color;
63 | background-color: $bg-color;
64 | background-image: linear-gradient(0deg, $border-color, $border-color 50%, transparent 50%);
65 | color: $color;
66 |
67 | .title {
68 | color: $color;
69 | }
70 | }
71 |
72 |
73 | // Tab Mixins
74 | // --------------------------------------------------
75 |
76 | @mixin tab-style($bg-color, $border-color, $color) {
77 | border-color: $border-color;
78 | background-color: $bg-color;
79 | background-image: linear-gradient(0deg, $border-color, $border-color 50%, transparent 50%);
80 | color: $color;
81 | }
82 |
83 | @mixin tab-badge-style($bg-color, $color) {
84 | .tab-item .badge {
85 | background-color: $bg-color;
86 | color: $color;
87 | }
88 | }
89 |
90 |
91 | // Item Mixins
92 | // --------------------------------------------------
93 |
94 | @mixin item-style($bg-color, $border-color, $color) {
95 | border-color: $border-color;
96 | background-color: $bg-color;
97 | color: $color;
98 | }
99 |
100 | @mixin item-active-style($active-bg-color, $active-border-color) {
101 | border-color: $active-border-color;
102 | background-color: $active-bg-color;
103 | }
104 |
105 |
106 | // Badge Mixins
107 | // --------------------------------------------------
108 |
109 | @mixin badge-style($bg-color, $color) {
110 | background-color: $bg-color;
111 | color: $color;
112 | }
113 |
114 |
115 | // Range Mixins
116 | // --------------------------------------------------
117 |
118 | @mixin range-style($track-bg-color) {
119 | &::-webkit-slider-thumb:before {
120 | background: $track-bg-color;
121 | }
122 | }
123 |
124 |
125 | // Checkbox Mixins
126 | // --------------------------------------------------
127 |
128 | @mixin checkbox-style($off-border-color, $on-bg-color) {
129 | & input:before,
130 | & .checkbox-icon:before {
131 | border-color: $off-border-color;
132 | }
133 |
134 | // what the background looks like when its checked
135 | & input:checked:before,
136 | & input:checked + .checkbox-icon:before {
137 | background: $on-bg-color;
138 | }
139 | }
140 |
141 |
142 | // Toggle Mixins
143 | // --------------------------------------------------
144 |
145 | @mixin toggle-style($on-border-color, $on-bg-color) {
146 | // the track when the toggle is "on"
147 | & input:checked + .track {
148 | border-color: $on-border-color;
149 | background-color: $on-bg-color;
150 | }
151 | }
152 |
153 |
154 | // Clearfix
155 | // --------------------------------------------------
156 |
157 | @mixin clearfix {
158 | *zoom: 1;
159 | &:before,
160 | &:after {
161 | display: table;
162 | content: "";
163 | line-height: 0;
164 | }
165 | &:after {
166 | clear: both;
167 | }
168 | }
169 |
170 |
171 | // Placeholder text
172 | // --------------------------------------------------
173 |
174 | @mixin placeholder($color: $input-color-placeholder, $text-indent: 0) {
175 | &::-moz-placeholder { /* Firefox 19+ */
176 | color: $color;
177 | }
178 | &:-ms-input-placeholder {
179 | color: $color;
180 | }
181 | &::-webkit-input-placeholder {
182 | color: $color;
183 | // Safari placeholder margin issue
184 | text-indent: $text-indent;
185 | }
186 | }
187 |
188 |
189 | // Text Mixins
190 | // --------------------------------------------------
191 |
192 | @mixin text-size-adjust($value: none) {
193 | -webkit-text-size-adjust: $value;
194 | -moz-text-size-adjust: $value;
195 | text-size-adjust: $value;
196 | }
197 | @mixin tap-highlight-transparent() {
198 | -webkit-tap-highlight-color: rgba(0,0,0,0);
199 | -webkit-tap-highlight-color: transparent; // For some Androids
200 | }
201 | @mixin touch-callout($value: none) {
202 | -webkit-touch-callout: $value;
203 | }
204 |
205 |
206 | // Font Mixins
207 | // --------------------------------------------------
208 |
209 | @mixin font-family-serif() {
210 | font-family: $serif-font-family;
211 | }
212 | @mixin font-family-sans-serif() {
213 | font-family: $sans-font-family;
214 | }
215 | @mixin font-family-monospace() {
216 | font-family: $mono-font-family;
217 | }
218 | @mixin font-shorthand($size: $base-font-size, $weight: normal, $line-height: $base-line-height) {
219 | font-weight: $weight;
220 | font-size: $size;
221 | line-height: $line-height;
222 | }
223 | @mixin font-serif($size: $base-font-size, $weight: normal, $line-height: $base-line-height) {
224 | @include font-family-serif();
225 | @include font-shorthand($size, $weight, $line-height);
226 | }
227 | @mixin font-sans-serif($size: $base-font-size, $weight: normal, $line-height: $base-line-height) {
228 | @include font-family-sans-serif();
229 | @include font-shorthand($size, $weight, $line-height);
230 | }
231 | @mixin font-monospace($size: $base-font-size, $weight: normal, $line-height: $base-line-height) {
232 | @include font-family-monospace();
233 | @include font-shorthand($size, $weight, $line-height);
234 | }
235 | @mixin font-smoothing($font-smoothing) {
236 | -webkit-font-smoothing: $font-smoothing;
237 | font-smoothing: $font-smoothing;
238 | }
239 |
240 |
241 | // Appearance
242 | // --------------------------------------------------
243 |
244 | @mixin appearance($val) {
245 | -webkit-appearance: $val;
246 | -moz-appearance: $val;
247 | appearance: $val;
248 | }
249 |
250 |
251 | // Border Radius Mixins
252 | // --------------------------------------------------
253 |
254 | @mixin border-radius($radius) {
255 | -webkit-border-radius: $radius;
256 | -moz-border-radius: $radius;
257 | border-radius: $radius;
258 | }
259 |
260 | // Single Corner Border Radius
261 | @mixin border-top-left-radius($radius) {
262 | -webkit-border-top-left-radius: $radius;
263 | -moz-border-radius-topleft: $radius;
264 | border-top-left-radius: $radius;
265 | }
266 | @mixin border-top-right-radius($radius) {
267 | -webkit-border-top-right-radius: $radius;
268 | -moz-border-radius-topright: $radius;
269 | border-top-right-radius: $radius;
270 | }
271 | @mixin border-bottom-right-radius($radius) {
272 | -webkit-border-bottom-right-radius: $radius;
273 | -moz-border-radius-bottomright: $radius;
274 | border-bottom-right-radius: $radius;
275 | }
276 | @mixin border-bottom-left-radius($radius) {
277 | -webkit-border-bottom-left-radius: $radius;
278 | -moz-border-radius-bottomleft: $radius;
279 | border-bottom-left-radius: $radius;
280 | }
281 |
282 | // Single Side Border Radius
283 | @mixin border-top-radius($radius) {
284 | @include border-top-right-radius($radius);
285 | @include border-top-left-radius($radius);
286 | }
287 | @mixin border-right-radius($radius) {
288 | @include border-top-right-radius($radius);
289 | @include border-bottom-right-radius($radius);
290 | }
291 | @mixin border-bottom-radius($radius) {
292 | @include border-bottom-right-radius($radius);
293 | @include border-bottom-left-radius($radius);
294 | }
295 | @mixin border-left-radius($radius) {
296 | @include border-top-left-radius($radius);
297 | @include border-bottom-left-radius($radius);
298 | }
299 |
300 |
301 | // Box shadows
302 | // --------------------------------------------------
303 |
304 | @mixin box-shadow($shadow...) {
305 | -webkit-box-shadow: $shadow;
306 | -moz-box-shadow: $shadow;
307 | box-shadow: $shadow;
308 | }
309 |
310 |
311 | // Transition Mixins
312 | // --------------------------------------------------
313 |
314 | @mixin transition($transition...) {
315 | -webkit-transition: $transition;
316 | -moz-transition: $transition;
317 | transition: $transition;
318 | }
319 | @mixin transition-delay($transition-delay) {
320 | -webkit-transition-delay: $transition-delay;
321 | -moz-transition-delay: $transition-delay;
322 | transition-delay: $transition-delay;
323 | }
324 | @mixin transition-duration($transition-duration) {
325 | -webkit-transition-duration: $transition-duration;
326 | -moz-transition-duration: $transition-duration;
327 | transition-duration: $transition-duration;
328 | }
329 | @mixin transition-timing-function($transition-timing) {
330 | -webkit-transition-timing-function: $transition-timing;
331 | -moz-transition-timing-function: $transition-timing;
332 | transition-timing-function: $transition-timing;
333 | }
334 | @mixin transition-property($property) {
335 | -webkit-transition-property: $property;
336 | -moz-transition-property: $property;
337 | transition-property: $property;
338 | }
339 | @mixin transition-transform($properties...) {
340 | // special case cuz of transform vendor prefixes
341 | -webkit-transition: -webkit-transform $properties;
342 | -moz-transition: -moz-transform $properties;
343 | transition: transform $properties;
344 | }
345 |
346 |
347 | // Animation Mixins
348 | // --------------------------------------------------
349 |
350 | @mixin animation($animation) {
351 | -webkit-animation: $animation;
352 | -moz-animation: $animation;
353 | animation: $animation;
354 | }
355 | @mixin animation-duration($duration) {
356 | -webkit-animation-duration: $duration;
357 | -moz-animation-duration: $duration;
358 | animation-duration: $duration;
359 | }
360 | @mixin animation-direction($direction) {
361 | -webkit-animation-direction: $direction;
362 | -moz-animation-direction: $direction;
363 | animation-direction: $direction;
364 | }
365 | @mixin animation-timing-function($animation-timing) {
366 | -webkit-animation-timing-function: $animation-timing;
367 | -moz-animation-timing-function: $animation-timing;
368 | animation-timing-function: $animation-timing;
369 | }
370 | @mixin animation-fill-mode($fill-mode) {
371 | -webkit-animation-fill-mode: $fill-mode;
372 | -moz-animation-fill-mode: $fill-mode;
373 | animation-fill-mode: $fill-mode;
374 | }
375 | @mixin animation-name($name) {
376 | -webkit-animation-name: $name;
377 | -moz-animation-name: $name;
378 | animation-name: $name;
379 | }
380 | @mixin animation-iteration-count($count) {
381 | -webkit-animation-iteration-count: $count;
382 | -moz-animation-iteration-count: $count;
383 | animation-iteration-count: $count;
384 | }
385 |
386 |
387 | // Transformation Mixins
388 | // --------------------------------------------------
389 |
390 | @mixin rotate($degrees) {
391 | @include transform( rotate($degrees) );
392 | }
393 | @mixin scale($ratio) {
394 | @include transform( scale($ratio) );
395 | }
396 | @mixin translate($x, $y) {
397 | @include transform( translate($x, $y) );
398 | }
399 | @mixin skew($x, $y) {
400 | @include transform( skew($x, $y) );
401 | -webkit-backface-visibility: hidden;
402 | }
403 | @mixin translate3d($x, $y, $z) {
404 | @include transform( translate3d($x, $y, $z) );
405 | }
406 | @mixin translateZ($z) {
407 | @include transform( translateZ($z) );
408 | }
409 | @mixin transform($val) {
410 | -webkit-transform: $val;
411 | -moz-transform: $val;
412 | transform: $val;
413 | }
414 |
415 | @mixin transform-origin($left, $top) {
416 | -webkit-transform-origin: $left $top;
417 | -moz-transform-origin: $left $top;
418 | transform-origin: $left $top;
419 | }
420 |
421 |
422 | // Backface visibility
423 | // --------------------------------------------------
424 | // Prevent browsers from flickering when using CSS 3D transforms.
425 | // Default value is `visible`, but can be changed to `hidden
426 |
427 | @mixin backface-visibility($visibility){
428 | -webkit-backface-visibility: $visibility;
429 | backface-visibility: $visibility;
430 | }
431 |
432 |
433 | // Background clipping
434 | // --------------------------------------------------
435 |
436 | @mixin background-clip($clip) {
437 | -webkit-background-clip: $clip;
438 | -moz-background-clip: $clip;
439 | background-clip: $clip;
440 | }
441 |
442 |
443 | // Background sizing
444 | // --------------------------------------------------
445 |
446 | @mixin background-size($size) {
447 | -webkit-background-size: $size;
448 | -moz-background-size: $size;
449 | background-size: $size;
450 | }
451 |
452 |
453 | // Box sizing
454 | // --------------------------------------------------
455 |
456 | @mixin box-sizing($boxmodel) {
457 | -webkit-box-sizing: $boxmodel;
458 | -moz-box-sizing: $boxmodel;
459 | box-sizing: $boxmodel;
460 | }
461 |
462 |
463 | // User select
464 | // --------------------------------------------------
465 |
466 | @mixin user-select($select) {
467 | -webkit-user-select: $select;
468 | -moz-user-select: $select;
469 | -ms-user-select: $select;
470 | user-select: $select;
471 | }
472 |
473 |
474 | // Content Columns
475 | // --------------------------------------------------
476 |
477 | @mixin content-columns($columnCount, $columnGap: $grid-gutter-width) {
478 | -webkit-column-count: $columnCount;
479 | -moz-column-count: $columnCount;
480 | column-count: $columnCount;
481 | -webkit-column-gap: $columnGap;
482 | -moz-column-gap: $columnGap;
483 | column-gap: $columnGap;
484 | }
485 |
486 |
487 | // Flexbox Mixins
488 | // --------------------------------------------------
489 | // http://philipwalton.github.io/solved-by-flexbox/
490 | // https://github.com/philipwalton/solved-by-flexbox
491 |
492 | @mixin display-flex {
493 | display: -webkit-box;
494 | display: -webkit-flex;
495 | display: -moz-box;
496 | display: -moz-flex;
497 | display: -ms-flexbox;
498 | display: flex;
499 | }
500 |
501 | @mixin dislay-inline-flex {
502 | display: -webkit-inline-box;
503 | display: -webkit-inline-flex;
504 | display: -moz-inline-flex;
505 | display: -ms-inline-flexbox;
506 | display: inline-flex;
507 | }
508 |
509 | @mixin flex-direction($value: row) {
510 | @if $value == row-reverse {
511 | -webkit-box-direction: reverse;
512 | -webkit-box-orient: horizontal;
513 | } @else if $value == column {
514 | -webkit-box-direction: normal;
515 | -webkit-box-orient: vertical;
516 | } @else if $value == column-reverse {
517 | -webkit-box-direction: reverse;
518 | -webkit-box-orient: vertical;
519 | } @else {
520 | -webkit-box-direction: normal;
521 | -webkit-box-orient: horizontal;
522 | }
523 | -webkit-flex-direction: $value;
524 | -moz-flex-direction: $value;
525 | -ms-flex-direction: $value;
526 | flex-direction: $value;
527 | }
528 |
529 | @mixin flex-wrap($value: nowrap) {
530 | // No Webkit Box fallback.
531 | -webkit-flex-wrap: $value;
532 | -moz-flex-wrap: $value;
533 | @if $value == nowrap {
534 | -ms-flex-wrap: none;
535 | } @else {
536 | -ms-flex-wrap: $value;
537 | }
538 | flex-wrap: $value;
539 | }
540 |
541 | @mixin flex($fg: 1, $fs: null, $fb: null) {
542 | -webkit-box-flex: $fg;
543 | -webkit-flex: $fg $fs $fb;
544 | -moz-box-flex: $fg;
545 | -moz-flex: $fg $fs $fb;
546 | -ms-flex: $fg $fs $fb;
547 | flex: $fg $fs $fb;
548 | }
549 |
550 | @mixin flex-flow($values: (row nowrap)) {
551 | // No Webkit Box fallback.
552 | -webkit-flex-flow: $values;
553 | -moz-flex-flow: $values;
554 | -ms-flex-flow: $values;
555 | flex-flow: $values;
556 | }
557 |
558 | @mixin align-items($value: stretch) {
559 | @if $value == flex-start {
560 | -webkit-box-align: start;
561 | -ms-flex-align: start;
562 | } @else if $value == flex-end {
563 | -webkit-box-align: end;
564 | -ms-flex-align: end;
565 | } @else {
566 | -webkit-box-align: $value;
567 | -ms-flex-align: $value;
568 | }
569 | -webkit-align-items: $value;
570 | -moz-align-items: $value;
571 | align-items: $value;
572 | }
573 |
574 | @mixin align-self($value: auto) {
575 | -webkit-align-self: $value;
576 | -moz-align-self: $value;
577 | @if $value == flex-start {
578 | -ms-flex-item-align: start;
579 | } @else if $value == flex-end {
580 | -ms-flex-item-align: end;
581 | } @else {
582 | -ms-flex-item-align: $value;
583 | }
584 | align-self: $value;
585 | }
586 |
587 | @mixin align-content($value: stretch) {
588 | -webkit-align-content: $value;
589 | -moz-align-content: $value;
590 | @if $value == flex-start {
591 | -ms-flex-line-pack: start;
592 | } @else if $value == flex-end {
593 | -ms-flex-line-pack: end;
594 | } @else {
595 | -ms-flex-line-pack: $value;
596 | }
597 | align-content: $value;
598 | }
599 |
600 | @mixin justify-content($value: stretch) {
601 | @if $value == flex-start {
602 | -webkit-box-pack: start;
603 | -ms-flex-pack: start;
604 | } @else if $value == flex-end {
605 | -webkit-box-pack: end;
606 | -ms-flex-pack: end;
607 | } @else if $value == space-between {
608 | -webkit-box-pack: justify;
609 | -ms-flex-pack: justify;
610 | } @else {
611 | -webkit-box-pack: $value;
612 | -ms-flex-pack: $value;
613 | }
614 | -webkit-justify-content: $value;
615 | -moz-justify-content: $value;
616 | justify-content: $value;
617 | }
618 |
619 | @mixin responsive-grid-break($selector, $max-width) {
620 | @media (max-width: $max-width) {
621 | #{$selector} {
622 | -webkit-box-direction: normal;
623 | -moz-box-direction: normal;
624 | -webkit-box-orient: vertical;
625 | -moz-box-orient: vertical;
626 | -webkit-flex-direction: column;
627 | -ms-flex-direction: column;
628 | flex-direction: column;
629 |
630 | .col, .col-10, .col-20, .col-25, .col-33, .col-34, .col-50, .col-66, .col-67, .col-75, .col-80, .col-90 {
631 | @include flex(1);
632 | margin-bottom: ($grid-padding-width * 3) / 2;
633 | margin-left: 0;
634 | max-width: 100%;
635 | width: 100%;
636 | }
637 | }
638 | }
639 | }
640 |
--------------------------------------------------------------------------------
/mobile/www/lib/ionic/js/angular-ui/angular-ui-router.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * State-based routing for AngularJS
3 | * @version v0.2.10
4 | * @link http://angular-ui.github.com/
5 | * @license MIT License, http://www.opensource.org/licenses/MIT
6 | */
7 | "undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="ui.router"),function(a,b,c){"use strict";function d(a,b){return I(new(I(function(){},{prototype:a})),b)}function e(a){return H(arguments,function(b){b!==a&&H(b,function(b,c){a.hasOwnProperty(c)||(a[c]=b)})}),a}function f(a,b){var c=[];for(var d in a.path){if(a.path[d]!==b.path[d])break;c.push(a.path[d])}return c}function g(a,b){if(Array.prototype.indexOf)return a.indexOf(b,Number(arguments[2])||0);var c=a.length>>>0,d=Number(arguments[2])||0;for(d=0>d?Math.ceil(d):Math.floor(d),0>d&&(d+=c);c>d;d++)if(d in a&&a[d]===b)return d;return-1}function h(a,b,c,d){var e,h=f(c,d),i={},j=[];for(var k in h)if(h[k].params&&h[k].params.length){e=h[k].params;for(var l in e)g(j,e[l])>=0||(j.push(e[l]),i[e[l]]=a[e[l]])}return I({},i,b)}function i(a,b){var c={};return H(a,function(a){var d=b[a];c[a]=null!=d?String(d):null}),c}function j(a,b,c){if(!c){c=[];for(var d in a)c.push(d)}for(var e=0;e "));if(o[c]=d,E(a))m.push(c,[function(){return b.get(a)}],h);else{var e=b.annotate(a);H(e,function(a){a!==c&&g.hasOwnProperty(a)&&k(g[a],a)}),m.push(c,a,e)}n.pop(),o[c]=f}}function l(a){return F(a)&&a.then&&a.$$promises}if(!F(g))throw new Error("'invocables' must be an object");var m=[],n=[],o={};return H(g,k),g=n=o=null,function(d,f,g){function h(){--s||(t||e(r,f.$$values),p.$$values=r,p.$$promises=!0,o.resolve(r))}function k(a){p.$$failure=a,o.reject(a)}function n(c,e,f){function i(a){l.reject(a),k(a)}function j(){if(!C(p.$$failure))try{l.resolve(b.invoke(e,g,r)),l.promise.then(function(a){r[c]=a,h()},i)}catch(a){i(a)}}var l=a.defer(),m=0;H(f,function(a){q.hasOwnProperty(a)&&!d.hasOwnProperty(a)&&(m++,q[a].then(function(b){r[a]=b,--m||j()},i))}),m||j(),q[c]=l.promise}if(l(d)&&g===c&&(g=f,f=d,d=null),d){if(!F(d))throw new Error("'locals' must be an object")}else d=i;if(f){if(!l(f))throw new Error("'parent' must be a promise returned by $resolve.resolve()")}else f=j;var o=a.defer(),p=o.promise,q=p.$$promises={},r=I({},d),s=1+m.length/3,t=!1;if(C(f.$$failure))return k(f.$$failure),p;f.$$values?(t=e(r,f.$$values),h()):(I(q,f.$$promises),f.then(h,k));for(var u=0,v=m.length;v>u;u+=3)d.hasOwnProperty(m[u])?h():n(m[u],m[u+1],m[u+2]);return p}},this.resolve=function(a,b,c,d){return this.study(a)(b,c,d)}}function m(a,b,c){this.fromConfig=function(a,b,c){return C(a.template)?this.fromString(a.template,b):C(a.templateUrl)?this.fromUrl(a.templateUrl,b):C(a.templateProvider)?this.fromProvider(a.templateProvider,b,c):null},this.fromString=function(a,b){return D(a)?a(b):a},this.fromUrl=function(c,d){return D(c)&&(c=c(d)),null==c?null:a.get(c,{cache:b}).then(function(a){return a.data})},this.fromProvider=function(a,b,d){return c.invoke(a,null,d||{params:b})}}function n(a){function b(b){if(!/^\w+(-+\w+)*$/.test(b))throw new Error("Invalid parameter name '"+b+"' in pattern '"+a+"'");if(f[b])throw new Error("Duplicate parameter name '"+b+"' in pattern '"+a+"'");f[b]=!0,j.push(b)}function c(a){return a.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&")}var d,e=/([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,f={},g="^",h=0,i=this.segments=[],j=this.params=[];this.source=a;for(var k,l,m;(d=e.exec(a))&&(k=d[2]||d[3],l=d[4]||("*"==d[1]?".*":"[^/]*"),m=a.substring(h,d.index),!(m.indexOf("?")>=0));)g+=c(m)+"("+l+")",b(k),i.push(m),h=e.lastIndex;m=a.substring(h);var n=m.indexOf("?");if(n>=0){var o=this.sourceSearch=m.substring(n);m=m.substring(0,n),this.sourcePath=a.substring(0,h+n),H(o.substring(1).split(/[&?]/),b)}else this.sourcePath=a,this.sourceSearch="";g+=c(m)+"$",i.push(m),this.regexp=new RegExp(g),this.prefix=i[0]}function o(){this.compile=function(a){return new n(a)},this.isMatcher=function(a){return F(a)&&D(a.exec)&&D(a.format)&&D(a.concat)},this.$get=function(){return this}}function p(a){function b(a){var b=/^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(a.source);return null!=b?b[1].replace(/\\(.)/g,"$1"):""}function c(a,b){return a.replace(/\$(\$|\d{1,2})/,function(a,c){return b["$"===c?0:Number(c)]})}function d(a,b,c){if(!c)return!1;var d=a.invoke(b,b,{$match:c});return C(d)?d:!0}var e=[],f=null;this.rule=function(a){if(!D(a))throw new Error("'rule' must be a function");return e.push(a),this},this.otherwise=function(a){if(E(a)){var b=a;a=function(){return b}}else if(!D(a))throw new Error("'rule' must be a function");return f=a,this},this.when=function(e,f){var g,h=E(f);if(E(e)&&(e=a.compile(e)),!h&&!D(f)&&!G(f))throw new Error("invalid 'handler' in when()");var i={matcher:function(b,c){return h&&(g=a.compile(c),c=["$match",function(a){return g.format(a)}]),I(function(a,e){return d(a,c,b.exec(e.path(),e.search()))},{prefix:E(b.prefix)?b.prefix:""})},regex:function(a,e){if(a.global||a.sticky)throw new Error("when() RegExp must not be global or sticky");return h&&(g=e,e=["$match",function(a){return c(g,a)}]),I(function(b,c){return d(b,e,a.exec(c.path()))},{prefix:b(a)})}},j={matcher:a.isMatcher(e),regex:e instanceof RegExp};for(var k in j)if(j[k])return this.rule(i[k](e,f));throw new Error("invalid 'what' in when()")},this.$get=["$location","$rootScope","$injector",function(a,b,c){function d(b){function d(b){var d=b(c,a);return d?(E(d)&&a.replace().url(d),!0):!1}if(!b||!b.defaultPrevented){var g,h=e.length;for(g=0;h>g;g++)if(d(e[g]))return;f&&d(f)}}return b.$on("$locationChangeSuccess",d),{sync:function(){d()}}}]}function q(a,e,f){function g(a){return 0===a.indexOf(".")||0===a.indexOf("^")}function l(a,b){var d=E(a),e=d?a:a.name,f=g(e);if(f){if(!b)throw new Error("No reference point given for path '"+e+"'");for(var h=e.split("."),i=0,j=h.length,k=b;j>i;i++)if(""!==h[i]||0!==i){if("^"!==h[i])break;if(!k.parent)throw new Error("Path '"+e+"' not valid for state '"+b.name+"'");k=k.parent}else k=b;h=h.slice(i).join("."),e=k.name+(k.name&&h?".":"")+h}var l=w[e];return!l||!d&&(d||l!==a&&l.self!==a)?c:l}function m(a,b){x[a]||(x[a]=[]),x[a].push(b)}function n(b){b=d(b,{self:b,resolve:b.resolve||{},toString:function(){return this.name}});var c=b.name;if(!E(c)||c.indexOf("@")>=0)throw new Error("State must have a valid name");if(w.hasOwnProperty(c))throw new Error("State '"+c+"'' is already defined");var e=-1!==c.indexOf(".")?c.substring(0,c.lastIndexOf(".")):E(b.parent)?b.parent:"";if(e&&!w[e])return m(e,b.self);for(var f in z)D(z[f])&&(b[f]=z[f](b,z.$delegates[f]));if(w[c]=b,!b[y]&&b.url&&a.when(b.url,["$match","$stateParams",function(a,c){v.$current.navigable==b&&j(a,c)||v.transitionTo(b,a,{location:!1})}]),x[c])for(var g=0;g-1}function p(a){var b=a.split("."),c=v.$current.name.split(".");if("**"===b[0]&&(c=c.slice(c.indexOf(b[1])),c.unshift("**")),"**"===b[b.length-1]&&(c.splice(c.indexOf(b[b.length-2])+1,Number.MAX_VALUE),c.push("**")),b.length!=c.length)return!1;for(var d=0,e=b.length;e>d;d++)"*"===b[d]&&(c[d]="*");return c.join("")===b.join("")}function q(a,b){return E(a)&&!C(b)?z[a]:D(b)&&E(a)?(z[a]&&!z.$delegates[a]&&(z.$delegates[a]=z[a]),z[a]=b,this):this}function r(a,b){return F(a)?b=a:b.name=a,n(b),this}function s(a,e,g,m,n,q,r,s,x){function z(){r.url()!==M&&(r.url(M),r.replace())}function A(a,c,d,f,h){var i=d?c:k(a.params,c),j={$stateParams:i};h.resolve=n.resolve(a.resolve,j,h.resolve,a);var l=[h.resolve.then(function(a){h.globals=a})];return f&&l.push(f),H(a.views,function(c,d){var e=c.resolve&&c.resolve!==a.resolve?c.resolve:{};e.$template=[function(){return g.load(d,{view:c,locals:j,params:i,notify:!1})||""}],l.push(n.resolve(e,j,h.resolve,a).then(function(f){if(D(c.controllerProvider)||G(c.controllerProvider)){var g=b.extend({},e,j);f.$$controller=m.invoke(c.controllerProvider,null,g)}else f.$$controller=c.controller;f.$$state=a,f.$$controllerAs=c.controllerAs,h[d]=f}))}),e.all(l).then(function(){return h})}var B=e.reject(new Error("transition superseded")),F=e.reject(new Error("transition prevented")),K=e.reject(new Error("transition aborted")),L=e.reject(new Error("transition failed")),M=r.url(),N=x.baseHref();return u.locals={resolve:null,globals:{$stateParams:{}}},v={params:{},current:u.self,$current:u,transition:null},v.reload=function(){v.transitionTo(v.current,q,{reload:!0,inherit:!1,notify:!1})},v.go=function(a,b,c){return this.transitionTo(a,b,I({inherit:!0,relative:v.$current},c))},v.transitionTo=function(b,c,f){c=c||{},f=I({location:!0,inherit:!1,relative:null,notify:!0,reload:!1,$retry:!1},f||{});var g,k=v.$current,n=v.params,o=k.path,p=l(b,f.relative);if(!C(p)){var s={to:b,toParams:c,options:f};if(g=a.$broadcast("$stateNotFound",s,k.self,n),g.defaultPrevented)return z(),K;if(g.retry){if(f.$retry)return z(),L;var w=v.transition=e.when(g.retry);return w.then(function(){return w!==v.transition?B:(s.options.$retry=!0,v.transitionTo(s.to,s.toParams,s.options))},function(){return K}),z(),w}if(b=s.to,c=s.toParams,f=s.options,p=l(b,f.relative),!C(p)){if(f.relative)throw new Error("Could not resolve '"+b+"' from state '"+f.relative+"'");throw new Error("No such state '"+b+"'")}}if(p[y])throw new Error("Cannot transition to abstract state '"+b+"'");f.inherit&&(c=h(q,c||{},v.$current,p)),b=p;var x,D,E=b.path,G=u.locals,H=[];for(x=0,D=E[x];D&&D===o[x]&&j(c,n,D.ownParams)&&!f.reload;x++,D=E[x])G=H[x]=D.locals;if(t(b,k,G,f))return b.self.reloadOnSearch!==!1&&z(),v.transition=null,e.when(v.current);if(c=i(b.params,c||{}),f.notify&&(g=a.$broadcast("$stateChangeStart",b.self,c,k.self,n),g.defaultPrevented))return z(),F;for(var N=e.when(G),O=x;O=x;d--)g=o[d],g.self.onExit&&m.invoke(g.self.onExit,g.self,g.locals.globals),g.locals=null;for(d=x;d1||b.ctrlKey||b.metaKey||b.shiftKey||f.attr("target")||(c(function(){a.go(i.state,j,o)}),b.preventDefault())})}}}function y(a,b,c){return{restrict:"A",controller:["$scope","$element","$attrs",function(d,e,f){function g(){a.$current.self===i&&h()?e.addClass(l):e.removeClass(l)}function h(){return!k||j(k,b)}var i,k,l;l=c(f.uiSrefActive||"",!1)(d),this.$$setStateInfo=function(b,c){i=a.get(b,w(e)),k=c,g()},d.$on("$stateChangeSuccess",g)}]}}function z(a){return function(b){return a.is(b)}}function A(a){return function(b){return a.includes(b)}}function B(a,b){function e(a){this.locals=a.locals.globals,this.params=this.locals.$stateParams}function f(){this.locals=null,this.params=null}function g(c,g){if(null!=g.redirectTo){var h,j=g.redirectTo;if(E(j))h=j;else{if(!D(j))throw new Error("Invalid 'redirectTo' in when()");h=function(a,b){return j(a,b.path(),b.search())}}b.when(c,h)}else a.state(d(g,{parent:null,name:"route:"+encodeURIComponent(c),url:c,onEnter:e,onExit:f}));return i.push(g),this}function h(a,b,d){function e(a){return""!==a.name?a:c}var f={routes:i,params:d,current:c};return b.$on("$stateChangeStart",function(a,c,d,f){b.$broadcast("$routeChangeStart",e(c),e(f))}),b.$on("$stateChangeSuccess",function(a,c,d,g){f.current=e(c),b.$broadcast("$routeChangeSuccess",e(c),e(g)),J(d,f.params)}),b.$on("$stateChangeError",function(a,c,d,f,g,h){b.$broadcast("$routeChangeError",e(c),e(f),h)}),f}var i=[];e.$inject=["$$state"],this.when=g,this.$get=h,h.$inject=["$state","$rootScope","$routeParams"]}var C=b.isDefined,D=b.isFunction,E=b.isString,F=b.isObject,G=b.isArray,H=b.forEach,I=b.extend,J=b.copy;b.module("ui.router.util",["ng"]),b.module("ui.router.router",["ui.router.util"]),b.module("ui.router.state",["ui.router.router","ui.router.util"]),b.module("ui.router",["ui.router.state"]),b.module("ui.router.compat",["ui.router"]),l.$inject=["$q","$injector"],b.module("ui.router.util").service("$resolve",l),m.$inject=["$http","$templateCache","$injector"],b.module("ui.router.util").service("$templateFactory",m),n.prototype.concat=function(a){return new n(this.sourcePath+a+this.sourceSearch)},n.prototype.toString=function(){return this.source},n.prototype.exec=function(a,b){var c=this.regexp.exec(a);if(!c)return null;var d,e=this.params,f=e.length,g=this.segments.length-1,h={};if(g!==c.length-1)throw new Error("Unbalanced capture group in route '"+this.source+"'");for(d=0;g>d;d++)h[e[d]]=c[d+1];for(;f>d;d++)h[e[d]]=b[e[d]];return h},n.prototype.parameters=function(){return this.params},n.prototype.format=function(a){var b=this.segments,c=this.params;if(!a)return b.join("");var d,e,f,g=b.length-1,h=c.length,i=b[0];for(d=0;g>d;d++)f=a[c[d]],null!=f&&(i+=encodeURIComponent(f)),i+=b[d+1];for(;h>d;d++)f=a[c[d]],null!=f&&(i+=(e?"&":"?")+c[d]+"="+encodeURIComponent(f),e=!0);return i},b.module("ui.router.util").provider("$urlMatcherFactory",o),p.$inject=["$urlMatcherFactoryProvider"],b.module("ui.router.router").provider("$urlRouter",p),q.$inject=["$urlRouterProvider","$urlMatcherFactoryProvider","$locationProvider"],b.module("ui.router.state").value("$stateParams",{}).provider("$state",q),r.$inject=[],b.module("ui.router.state").provider("$view",r),b.module("ui.router.state").provider("$uiViewScroll",s),t.$inject=["$state","$injector","$uiViewScroll"],u.$inject=["$compile","$controller","$state"],b.module("ui.router.state").directive("uiView",t),b.module("ui.router.state").directive("uiView",u),x.$inject=["$state","$timeout"],y.$inject=["$state","$stateParams","$interpolate"],b.module("ui.router.state").directive("uiSref",x).directive("uiSrefActive",y),z.$inject=["$state"],A.$inject=["$state"],b.module("ui.router.state").filter("isState",z).filter("includedByState",A),B.$inject=["$stateProvider","$urlRouterProvider"],b.module("ui.router.compat").provider("$route",B).directive("ngView",t)}(window,window.angular);
--------------------------------------------------------------------------------