├── .bowerrc
├── www
├── css
│ └── style.css
├── 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
│ │ ├── _loading.scss
│ │ ├── _slide-box.scss
│ │ ├── _button-bar.scss
│ │ ├── _menu.scss
│ │ ├── _animations.scss
│ │ ├── _radio.scss
│ │ ├── _badge.scss
│ │ ├── _platform.scss
│ │ ├── _action-sheet.scss
│ │ ├── _modal.scss
│ │ ├── _popup.scss
│ │ ├── _list.scss
│ │ ├── _select.scss
│ │ ├── _range.scss
│ │ ├── _grid.scss
│ │ ├── _type.scss
│ │ ├── _popover.scss
│ │ ├── _transitions.scss
│ │ ├── _toggle.scss
│ │ ├── _util.scss
│ │ ├── _checkbox.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
├── templates
│ ├── categories.html
│ ├── password_view.html
│ ├── password_list.html
│ ├── locked.html
│ ├── create_vault.html
│ ├── firebase.html
│ └── password_new.html
├── index.html
└── js
│ ├── app.js
│ └── angularfire.min.js
├── ionic.project
├── bower.json
├── .gitignore
├── CHANGELOG.md
├── package.json
├── hooks
├── before_platform_add
│ └── init_directories.js
├── after_plugin_rm
│ └── 010_deregister_plugin.js
├── after_platform_add
│ └── 010_install_plugins.js
├── after_prepare
│ ├── 020_remove_sass_from_platforms.js
│ └── 010_add_platform_class.js
├── after_plugin_add
│ └── 010_register_plugin.js
└── README.md
├── config.xml
├── scss
└── ionic.app.scss
├── LICENSE
├── gulpfile.js
└── README.md
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "www/lib"
3 | }
4 |
--------------------------------------------------------------------------------
/www/css/style.css:
--------------------------------------------------------------------------------
1 | /* Empty. Add your own CSS if you like */
2 |
--------------------------------------------------------------------------------
/ionic.project:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Cipher Safe",
3 | "app_id": ""
4 | }
5 |
--------------------------------------------------------------------------------
/www/img/ionic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/ionic-cipher-safe-app/HEAD/www/img/ionic.png
--------------------------------------------------------------------------------
/www/lib/ionic/fonts/ionicons.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/ionic-cipher-safe-app/HEAD/www/lib/ionic/fonts/ionicons.eot
--------------------------------------------------------------------------------
/www/lib/ionic/fonts/ionicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/ionic-cipher-safe-app/HEAD/www/lib/ionic/fonts/ionicons.ttf
--------------------------------------------------------------------------------
/www/lib/ionic/fonts/ionicons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/ionic-cipher-safe-app/HEAD/www/lib/ionic/fonts/ionicons.woff
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "CipherSafe",
3 | "private": "true",
4 | "devDependencies": {
5 | "ionic": "driftyco/ionic-bower#1.0.0-beta.14"
6 | }
7 | }
--------------------------------------------------------------------------------
/www/lib/ionic/version.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0-beta.14",
3 | "codename": "magnesium-mongoose",
4 | "date": "2014-12-15",
5 | "time": "20:15:38"
6 | }
7 |
--------------------------------------------------------------------------------
/.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 |
8 | .DS_Store*
9 | ~*
10 | Thumbs.db*
11 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | 0.0.2 - March 6, 2015
2 |
3 | * Update AngularFire to 1.0.0
4 | * Update Firebase to 2.2.2
5 | * Update all code to reflect new versions of AngularFire and Firebase
6 |
7 | 0.0.1 - March 4, 2015
8 |
9 | * Initial release to GitHub as part of the AirPair 100K Competition
10 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ciphersafe",
3 | "version": "1.0.0",
4 | "description": "Cipher Safe: An Ionic project",
5 | "dependencies": {
6 | "gulp": "^3.5.6",
7 | "gulp-concat": "^2.2.0",
8 | "gulp-minify-css": "^0.3.0",
9 | "gulp-rename": "^1.2.0",
10 | "gulp-sass": "^0.7.1",
11 | "jshint": "^2.6.0"
12 | },
13 | "devDependencies": {
14 | "bower": "^1.3.3",
15 | "gulp-util": "^2.2.14",
16 | "shelljs": "^0.3.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/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: $loading-backdrop-bg-color;
12 |
13 | visibility: hidden;
14 | opacity: 0;
15 |
16 | &.visible {
17 | visibility: visible;
18 | }
19 | &.active {
20 | opacity: 1;
21 | }
22 |
23 | @include transition($loading-backdrop-fadein-duration opacity linear);
24 | }
25 |
--------------------------------------------------------------------------------
/www/templates/categories.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{item.category}}
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/www/templates/password_view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{password.title}}
9 |
10 |
11 | {{password.username}}
12 |
13 |
14 | {{password.password}}
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/hooks/before_platform_add/init_directories.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * On a fresh clone, the local platforms/ and plugins/ directories will be
5 | * missing, so ensure they get created before the first platform is added.
6 | */
7 | var fs = require('fs');
8 | var path = require('path');
9 |
10 | var platformsDir = path.resolve(__dirname, '../../platforms');
11 | var pluginsDir = path.resolve(__dirname, '../../plugins');
12 |
13 | try {
14 | fs.mkdirSync(platformsDir, function (err) {
15 | if (err) { console.error(err); }
16 | });
17 | } catch(ex) {}
18 |
19 | try {
20 | fs.mkdirSync(pluginsDir, function (err) {
21 | if (err) { console.error(err); }
22 | });
23 | } catch(ex) {}
24 |
--------------------------------------------------------------------------------
/www/templates/password_list.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | {{item.password.title}}
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/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 | "grid",
46 | "util",
47 | "platform",
48 |
49 | // Animations
50 | "animations",
51 | "transitions";
52 |
--------------------------------------------------------------------------------
/www/templates/locked.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Welcome
5 |
6 |
7 |
8 |
11 |
12 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Cipher Safe
4 |
5 | Store passwords on Firebase using a master password and AES strength encryption
6 |
7 |
8 | Nic Raboy
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/www/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cipher Safe
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/www/templates/create_vault.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Welcome
5 |
6 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/hooks/after_plugin_rm/010_deregister_plugin.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Remove plugins from cordovaPlugins array after_plugin_rm
5 | */
6 | var fs = require('fs');
7 | var packageJSON = require('../../package.json');
8 |
9 | packageJSON.cordovaPlugins = packageJSON.cordovaPlugins || [];
10 |
11 | process.env.CORDOVA_PLUGINS.split(',').forEach(function (plugin) {
12 | var index = packageJSON.cordovaPlugins.indexOf(plugin);
13 | if (index > -1) {
14 | packageJSON.cordovaPlugins.splice(index, 1);
15 | } else {
16 | //If it didnt find a match, it may be listed as {id,locator}
17 | for(var i = 0, j = packageJSON.cordovaPlugins.length; i < j; i++) {
18 | var packagePlugin = packageJSON.cordovaPlugins[i];
19 | if(typeof packagePlugin == 'object' && packagePlugin.id == plugin) {
20 | packageJSON.cordovaPlugins.splice(index, 1);
21 | break;
22 | }
23 | }
24 | }
25 | });
26 |
27 | fs.writeFile('package.json', JSON.stringify(packageJSON, null, 2));
28 |
--------------------------------------------------------------------------------
/www/templates/firebase.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
11 |
12 |
13 |
17 |
18 | If the account does not exist, it will be created
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/hooks/after_platform_add/010_install_plugins.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Install all plugins listed in package.json
5 | * https://raw.githubusercontent.com/diegonetto/generator-ionic/master/templates/hooks/after_platform_add/install_plugins.js
6 | */
7 | var exec = require('child_process').exec;
8 | var path = require('path');
9 | var sys = require('sys');
10 |
11 | var packageJSON = null;
12 |
13 | try {
14 | packageJSON = require('../../package.json');
15 | } catch(ex) {
16 | console.log('\nThere was an error fetching your package.json file.')
17 | console.log('\nPlease ensure a valid package.json is in the root of this project\n')
18 | return;
19 | }
20 |
21 | var cmd = process.platform === 'win32' ? 'cordova.cmd' : 'cordova';
22 | // var script = path.resolve(__dirname, '../../node_modules/cordova/bin', cmd);
23 |
24 | packageJSON.cordovaPlugins = packageJSON.cordovaPlugins || [];
25 | packageJSON.cordovaPlugins.forEach(function (plugin) {
26 | exec('cordova plugin add ' + plugin, function (error, stdout, stderr) {
27 | sys.puts(stdout);
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/hooks/after_prepare/020_remove_sass_from_platforms.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * After prepare, files are copied to the platforms/ios and platforms/android folders.
5 | * Lets clean up some of those files that arent needed with this hook.
6 | */
7 | var fs = require('fs');
8 | var path = require('path');
9 |
10 | var deleteFolderRecursive = function(removePath) {
11 | if( fs.existsSync(removePath) ) {
12 | fs.readdirSync(removePath).forEach(function(file,index){
13 | var curPath = path.join(removePath, file);
14 | if(fs.lstatSync(curPath).isDirectory()) { // recurse
15 | deleteFolderRecursive(curPath);
16 | } else { // delete file
17 | fs.unlinkSync(curPath);
18 | }
19 | });
20 | fs.rmdirSync(removePath);
21 | }
22 | };
23 |
24 | var iosPlatformsDir = path.resolve(__dirname, '../../platforms/ios/www/lib/ionic/scss');
25 | var androidPlatformsDir = path.resolve(__dirname, '../../platforms/android/assets/www/lib/ionic/scss');
26 |
27 | deleteFolderRecursive(iosPlatformsDir);
28 | deleteFolderRecursive(androidPlatformsDir);
29 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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: $button-border-radius 0px 0px $button-border-radius;
49 | }
50 | &:last-child {
51 | border-right-width: 1px;
52 | border-radius: 0px $button-border-radius $button-border-radius 0px;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/www/templates/password_new.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Nic Raboy
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/www/lib/ionic/scss/_animations.scss:
--------------------------------------------------------------------------------
1 |
2 | // Slide up from the bottom, used for modals
3 | // -------------------------------
4 |
5 | .slide-in-up {
6 | @include translate3d(0, 100%, 0);
7 | }
8 | .slide-in-up.ng-enter,
9 | .slide-in-up > .ng-enter {
10 | @include transition(all cubic-bezier(.1, .7, .1, 1) 400ms);
11 | }
12 | .slide-in-up.ng-enter-active,
13 | .slide-in-up > .ng-enter-active {
14 | @include translate3d(0, 0, 0);
15 | }
16 |
17 | .slide-in-up.ng-leave,
18 | .slide-in-up > .ng-leave {
19 | @include transition(all ease-in-out 250ms);
20 | }
21 |
22 |
23 | // Scale Out
24 | // Scale from hero (1 in this case) to zero
25 | // -------------------------------
26 |
27 | @-webkit-keyframes scaleOut {
28 | from { -webkit-transform: scale(1); opacity: 1; }
29 | to { -webkit-transform: scale(0.8); opacity: 0; }
30 | }
31 | @keyframes scaleOut {
32 | from { transform: scale(1); opacity: 1; }
33 | to { transform: scale(0.8); opacity: 0; }
34 | }
35 |
36 |
37 | // Super Scale In
38 | // Scale from super (1.x) to duper (1 in this case)
39 | // -------------------------------
40 |
41 | @-webkit-keyframes superScaleIn {
42 | from { -webkit-transform: scale(1.2); opacity: 0; }
43 | to { -webkit-transform: scale(1); opacity: 1 }
44 | }
45 | @keyframes superScaleIn {
46 | from { transform: scale(1.2); opacity: 0; }
47 | to { transform: scale(1); opacity: 1; }
48 | }
49 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/www/lib/ionic/scss/_platform.scss:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Platform
4 | * --------------------------------------------------
5 | * Platform specific tweaks
6 | */
7 |
8 | .platform-ios.platform-cordova {
9 | // iOS7/8 has a status bar which sits on top of the header.
10 | // Bump down everything to make room for it. However, if
11 | // if its in Cordova, and set to fullscreen, then disregard the bump.
12 | &:not(.fullscreen) {
13 | .bar-header:not(.bar-subheader) {
14 | height: $bar-height + $ios-statusbar-height;
15 |
16 | &.item-input-inset .item-input-wrapper {
17 | margin-top: 19px !important;
18 | }
19 |
20 | > * {
21 | margin-top: $ios-statusbar-height;
22 | }
23 | }
24 | .tabs-top > .tabs,
25 | .tabs.tabs-top {
26 | top: $bar-height + $ios-statusbar-height;
27 | }
28 |
29 | .has-header,
30 | .bar-subheader {
31 | top: $bar-height + $ios-statusbar-height;
32 | }
33 | .has-subheader {
34 | top: $bar-height + $bar-subheader-height + $ios-statusbar-height;
35 | }
36 | .has-tabs-top {
37 | top: $bar-height + $tabs-height + $ios-statusbar-height;
38 | }
39 | .has-header.has-subheader.has-tabs-top {
40 | top: $bar-height + $bar-subheader-height + $tabs-height + $ios-statusbar-height;
41 | }
42 | }
43 | &.status-bar-hide {
44 | // Cordova doesn't adjust the body height correctly, this makes up for it
45 | margin-bottom: 20px;
46 | }
47 | }
48 |
49 | @media (orientation:landscape) {
50 | .platform-ios.platform-browser.platform-ipad {
51 | position: fixed; // required for iPad 7 Safari
52 | }
53 | }
54 |
55 | .platform-c:not(.enable-transitions) * {
56 | // disable transitions on grade-c devices (Android 2)
57 | -webkit-transition: none !important;
58 | transition: none !important;
59 | }
60 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/hooks/after_plugin_add/010_register_plugin.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Push plugins to cordovaPlugins array after_plugin_add
5 | */
6 | var fs = require('fs'),
7 | packageJSON = require('../../package.json'),
8 | path = require('path');
9 |
10 | packageJSON.cordovaPlugins = packageJSON.cordovaPlugins || [];
11 | process.env.CORDOVA_PLUGINS.split(',').forEach(function (plugin) {
12 | var configString,
13 | idRegEx,
14 | id,
15 | pluginXmlPath,
16 | pluginToAdd;
17 |
18 | if(plugin.indexOf('https') != -1 || plugin.indexOf('git') != -1) {
19 | console.log('Installing plugin from url');
20 | }
21 |
22 | if(plugin.indexOf('/') != -1) {
23 | try {
24 | pluginXmlPath = path.resolve(plugin, 'plugin.xml');
25 | console.log('got pluginXmlPath:', pluginXmlPath);
26 | if (!fs.existsSync(pluginXmlPath)) {
27 | var errorMessage = ['There was no plugin.xml file found for path: ', pluginXmlPath].join('');
28 | return;
29 | }
30 |
31 | configString = fs.readFileSync(pluginXmlPath,{encoding: 'utf8'});
32 | idRegEx = new RegExp(']*id="(.*)"', 'i');
33 | id = idRegEx.exec(configString)[1]
34 | pluginToAdd = {id: id, locator: plugin};
35 | } catch(ex) {
36 | console.log('There was an error retrieving the plugin.xml filr from the 010_register_plugin.js hook', ex);
37 | }
38 | } else {
39 | pluginToAdd = plugin;
40 | }
41 |
42 | if(typeof pluginToAdd == 'string' && packageJSON.cordovaPlugins.indexOf(pluginToAdd) == -1) {
43 | packageJSON.cordovaPlugins.push(pluginToAdd);
44 | } else if (typeof pluginToAdd == 'object') {
45 | var pluginExists = false;
46 | packageJSON.cordovaPlugins.forEach(function(checkPlugin) {
47 | if(typeof checkPlugin == 'object' && checkPlugin.id == pluginToAdd.id) {
48 | pluginExists = true;
49 | }
50 | })
51 | if(!pluginExists) {
52 | packageJSON.cordovaPlugins.push(pluginToAdd);
53 | }
54 | }
55 | });
56 |
57 | fs.writeFileSync('package.json', JSON.stringify(packageJSON, null, 2));
58 |
--------------------------------------------------------------------------------
/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 | // must spin entire element for android 4.3 and below
42 | @extend .#{$ionicons-prefix}spin;
43 | }
44 |
45 | .#{$ionicons-prefix}loading-a {
46 | -webkit-animation-timing-function: steps(8, start);
47 | -moz-animation-timing-function: steps(8, start);
48 | animation-timing-function: steps(8, start);
49 | }
50 |
51 | .#{$ionicons-prefix}loading-a:before {
52 | @extend .#{$ionicons-prefix}load-a:before;
53 | }
54 |
55 | .#{$ionicons-prefix}loading-b:before {
56 | @extend .#{$ionicons-prefix}load-b:before;
57 | }
58 |
59 | .#{$ionicons-prefix}loading-c:before {
60 | @extend .#{$ionicons-prefix}load-c:before;
61 | }
62 |
63 | .#{$ionicons-prefix}loading-d:before {
64 | @extend .#{$ionicons-prefix}load-d:before;
65 | }
66 |
67 | .#{$ionicons-prefix}looping:before {
68 | @extend .#{$ionicons-prefix}loop:before;
69 | }
70 |
71 | .#{$ionicons-prefix}refreshing:before {
72 | @extend .#{$ionicons-prefix}refresh:before;
73 | }
74 |
75 | .#{$ionicons-prefix}ios7-reloading:before {
76 | @extend .#{$ionicons-prefix}ios7-reload:before;
77 | }
78 |
--------------------------------------------------------------------------------
/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: $bar-height + $bar-subheader-height;
69 | }
70 | .has-tabs-top {
71 | top: $bar-height + $tabs-height;
72 | }
73 | .has-header.has-subheader.has-tabs-top {
74 | top: $bar-height + $bar-subheader-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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cipher Safe for Android and iOS
2 |
3 | This project was created to compliment an article I wrote on [AirPair.com](http://www.airpair.com). It demonstrates how to use the Forge JavaScript cipher-text library and Firebase to create a 1Password, LastPass, or KeePass competitor with Ionic Framework.
4 |
5 |
6 | ## Requirements
7 |
8 | * Apache Cordova 4.0+
9 | * Firebase 2.2.2+
10 | * AngularFire 1.0.0+
11 | * Ionic CLI 1.3.11+
12 |
13 |
14 | ## Configuration
15 |
16 | Download this example project from GitHub and run the following commands:
17 |
18 | $ ionic platform add android
19 |
20 | The above command will add the Android build platform.
21 |
22 | This application requires you to have your own Firebase instance registered with **Email & Password** authentication enabled.
23 | Firebase permissions must be set as follows in the **Security & Roles** section:
24 |
25 | {
26 | "rules": {
27 | "users": {
28 | ".write": true,
29 | "$uid": {
30 | ".read": "auth != null && auth.uid == $uid"
31 | }
32 | }
33 | }
34 | }
35 |
36 | With your Firebase instance id in hand, open **www/js/app.js** and find the following line:
37 |
38 | fb = new Firebase("https://INSTANCE_ID_HERE.firebaseio.com/");
39 |
40 | You will want to replace **INSTANCE_ID_HERE** with your actual instance id.
41 |
42 |
43 | ## Usage
44 |
45 | With this example project configured on your computer, run the following from the Terminal or command prompt:
46 |
47 | $ ionic build android
48 |
49 | Install the application binary to your device or simulator.
50 |
51 | The application is currently composed of six parts:
52 |
53 | 1. Firebase sign in
54 | 2. Master password creation
55 | 3. Master password unlocking
56 | 4. Password categories
57 | 5. Password lists
58 | 6. Password creation and viewing
59 |
60 | You will be required to sign in to Firebase to use this application. There is no offline compatibility in the current release.
61 |
62 | Passwords are encrypted before storing on Firebase and transferred over a secure HTTPS connection.
63 |
64 |
65 | ## Have a question or found a bug (compliments work too)?
66 |
67 | Tweet me on Twitter - [@nraboy](https://www.twitter.com/nraboy)
68 |
69 |
70 | ## Resources
71 |
72 | Nic Raboy's Code Blog - [https://blog.nraboy.com](https://blog.nraboy.com)
73 |
74 | Ionic Framework - [http://www.ionicframework.com](http://www.ionicframework.com)
75 |
76 | AngularJS - [http://www.angularjs.org](http://www.angularjs.org)
77 |
78 | Apache Cordova - [http://cordova.apache.org](http://cordova.apache.org)
79 |
--------------------------------------------------------------------------------
/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: $card-box-shadow;
60 |
61 | .item {
62 | border-left: 0;
63 | border-right: 0;
64 | }
65 | .item:first-child {
66 | border-top: 0;
67 | }
68 | .item:last-child {
69 | border-bottom: 0;
70 | }
71 | }
72 |
73 | .padding {
74 | .card, .list-inset {
75 | margin-left: 0;
76 | margin-right: 0;
77 | }
78 | }
79 |
80 | .card .item,
81 | .list-inset .item,
82 | .padding > .list .item
83 | {
84 | &:first-child {
85 | border-top-left-radius: $card-border-radius;
86 | border-top-right-radius: $card-border-radius;
87 |
88 | .item-content {
89 | border-top-left-radius: $card-border-radius;
90 | border-top-right-radius: $card-border-radius;
91 | }
92 | }
93 | &:last-child {
94 | border-bottom-right-radius: $card-border-radius;
95 | border-bottom-left-radius: $card-border-radius;
96 |
97 | .item-content {
98 | border-bottom-right-radius: $card-border-radius;
99 | border-bottom-left-radius: $card-border-radius;
100 | }
101 | }
102 | }
103 |
104 | .card .item:last-child,
105 | .list-inset .item:last-child {
106 | margin-bottom: $item-border-width * -1;
107 | }
108 |
109 | .card .item,
110 | .list-inset .item,
111 | .padding > .list .item,
112 | .padding-horizontal > .list .item {
113 | margin-right: 0;
114 | margin-left: 0;
115 |
116 | &.item-input input {
117 | padding-right: 44px;
118 | }
119 | }
120 | .padding-left > .list .item {
121 | margin-left: 0;
122 | }
123 | .padding-right > .list .item {
124 | margin-right: 0;
125 | }
126 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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: $range-slider-box-shadow;
31 | cursor: pointer;
32 | -webkit-appearance: none;
33 | border: 0;
34 | }
35 |
36 | &::-webkit-slider-thumb:before {
37 | /* what creates the colorful line on the left side of the slider */
38 | position: absolute;
39 | top: ($range-slider-height / 2) - ($range-track-height / 2);
40 | left: -2001px;
41 | width: 2000px;
42 | height: $range-track-height;
43 | background: $dark;
44 | content: ' ';
45 | }
46 |
47 | &::-webkit-slider-thumb:after {
48 | /* create a larger (but hidden) hit area */
49 | position: absolute;
50 | top: -15px;
51 | left: -15px;
52 | padding: 30px;
53 | content: ' ';
54 | //background: red;
55 | //opacity: .5;
56 | }
57 |
58 | }
59 |
60 | .range {
61 | @include display-flex();
62 | @include align-items(center);
63 | padding: 2px 11px;
64 |
65 | &.range-light {
66 | input { @include range-style($range-light-track-bg); }
67 | }
68 | &.range-stable {
69 | input { @include range-style($range-stable-track-bg); }
70 | }
71 | &.range-positive {
72 | input { @include range-style($range-positive-track-bg); }
73 | }
74 | &.range-calm {
75 | input { @include range-style($range-calm-track-bg); }
76 | }
77 | &.range-balanced {
78 | input { @include range-style($range-balanced-track-bg); }
79 | }
80 | &.range-assertive {
81 | input { @include range-style($range-assertive-track-bg); }
82 | }
83 | &.range-energized {
84 | input { @include range-style($range-energized-track-bg); }
85 | }
86 | &.range-royal {
87 | input { @include range-style($range-royal-track-bg); }
88 | }
89 | &.range-dark {
90 | input { @include range-style($range-dark-track-bg); }
91 | }
92 | }
93 |
94 | .range .icon {
95 | @include flex(0);
96 | display: block;
97 | min-width: $range-icon-size;
98 | text-align: center;
99 | font-size: $range-icon-size;
100 | }
101 |
102 | .range input {
103 | @include flex(1);
104 | display: block;
105 | margin-right: 10px;
106 | margin-left: 10px;
107 | }
108 |
109 | .range-label {
110 | @include flex(0, 0, auto);
111 | display: block;
112 | white-space: nowrap;
113 | }
114 |
115 | .range-label:first-child {
116 | padding-left: 5px;
117 | }
118 | .range input + .range-label {
119 | padding-right: 5px;
120 | padding-left: 0;
121 | }
122 |
123 |
--------------------------------------------------------------------------------
/www/lib/ionic/js/angular/angular-resource.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | AngularJS v1.3.6
3 | (c) 2010-2014 Google, Inc. http://angularjs.org
4 | License: MIT
5 | */
6 | (function(I,d,B){'use strict';function D(f,q){q=q||{};d.forEach(q,function(d,h){delete q[h]});for(var h in f)!f.hasOwnProperty(h)||"$"===h.charAt(0)&&"$"===h.charAt(1)||(q[h]=f[h]);return q}var w=d.$$minErr("$resource"),C=/^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;d.module("ngResource",["ng"]).provider("$resource",function(){var f=this;this.defaults={stripTrailingSlashes:!0,actions:{get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}}};
7 | this.$get=["$http","$q",function(q,h){function t(d,g){this.template=d;this.defaults=s({},f.defaults,g);this.urlParams={}}function v(x,g,l,m){function c(b,k){var c={};k=s({},g,k);r(k,function(a,k){u(a)&&(a=a());var d;if(a&&a.charAt&&"@"==a.charAt(0)){d=b;var e=a.substr(1);if(null==e||""===e||"hasOwnProperty"===e||!C.test("."+e))throw w("badmember",e);for(var e=e.split("."),n=0,g=e.length;n=c;e--)d.end&&d.end(f[e]);f.length=c}}"string"!==typeof a&&(a=null===a||"undefined"===typeof a?"":""+a);var b,k,f=[],m=a,l;for(f.last=function(){return f[f.length-1]};a;){l="";k=!0;if(f.last()&&x[f.last()])a=a.replace(new RegExp("(.*)<\\s*\\/\\s*"+f.last()+"[^>]*>","i"),function(a,b){b=b.replace(H,"$1").replace(I,"$1");d.chars&&d.chars(r(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),k=!1);else if(y.test(a)){if(b=a.match(y))a=a.replace(b[0],""),k=!1}else if(J.test(a)){if(b=a.match(z))a=a.substring(b[0].length),b[0].replace(z,e),k=!1}else K.test(a)&&((b=a.match(A))?(b[4]&&(a=a.substring(b[0].length),b[0].replace(A,c)),k=!1):(l+="<",a=a.substring(1)));k&&(b=a.indexOf("<"),l+=0>b?a:a.substring(0,b),a=0>b?"":a.substring(b),d.chars&&d.chars(r(l)))}if(a==m)throw L("badparse",a);m=a}e()}function r(a){if(!a)return"";var d=M.exec(a);a=d[1];var c=d[3];if(d=d[2])q.innerHTML=
9 | d.replace(//g,">")}function s(a,d){var c=!1,e=h.bind(a,a.push);return{start:function(a,k,f){a=h.lowercase(a);!c&&x[a]&&(c=a);c||!0!==C[a]||(e("<"),e(a),h.forEach(k,function(c,f){var k=
10 | h.lowercase(f),g="img"===a&&"src"===k||"background"===k;!0!==P[k]||!0===D[k]&&!d(c,g)||(e(" "),e(f),e('="'),e(B(c)),e('"'))}),e(f?"/>":">"))},end:function(a){a=h.lowercase(a);c||!0!==C[a]||(e(""),e(a),e(">"));a==c&&(c=!1)},chars:function(a){c||e(B(a))}}}var L=h.$$minErr("$sanitize"),A=/^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,z=/^<\/\s*([\w:-]+)[^>]*>/,G=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,K=/^,
11 | J=/^<\//,H=/\x3c!--(.*?)--\x3e/g,y=/]*?)>/i,I=/"\u201d\u2019]/,c=/^mailto:/;return function(e,b){function k(a){a&&g.push(E(a))}
15 | function f(a,c){g.push("');k(c);g.push("")}if(!e)return e;for(var m,l=e,g=[],n,p;m=l.match(d);)n=m[0],m[2]||m[4]||(n=(m[3]?"http://":"mailto:")+n),p=m.index,k(l.substr(0,p)),f(n,m[0].replace(c,"")),l=l.substring(p+m[0].length);k(l);return a(g.join(""))}}])})(window,window.angular);
16 | //# sourceMappingURL=angular-sanitize.min.js.map
17 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | .platform-android .scroll-bar-indicator {
147 | // android doesn't have rounded ends on scrollbar
148 | border-radius: 0;
149 | }
150 | .grade-b .scroll-bar-indicator,
151 | .grade-c .scroll-bar-indicator {
152 | // disable rgba background and border radius for low end devices
153 | background: #aaa;
154 |
155 | &.scroll-bar-fade-out {
156 | @include transition(none);
157 | }
158 | }
159 |
160 | @keyframes refresh-spin {
161 | 0% { transform: translate3d(0,0,0) rotate(0); }
162 | 100% { transform: translate3d(0,0,0) rotate(180deg); }
163 | }
164 |
165 | @-webkit-keyframes refresh-spin {
166 | 0% {-webkit-transform: translate3d(0,0,0) rotate(0); }
167 | 100% {-webkit-transform: translate3d(0,0,0) rotate(180deg); }
168 | }
169 |
170 | @keyframes refresh-spin-back {
171 | 0% { transform: translate3d(0,0,0) rotate(180deg); }
172 | 100% { transform: translate3d(0,0,0) rotate(0); }
173 | }
174 |
175 | @-webkit-keyframes refresh-spin-back {
176 | 0% {-webkit-transform: translate3d(0,0,0) rotate(180deg); }
177 | 100% {-webkit-transform: translate3d(0,0,0) rotate(0); }
178 | }
179 |
180 | // Scroll refresher (for pull to refresh)
181 | .scroll-refresher {
182 | position: absolute;
183 | top: -60px;
184 | right: 0;
185 | left: 0;
186 | overflow: hidden;
187 | margin: auto;
188 | height: 60px;
189 |
190 | .ionic-refresher-content {
191 | position: absolute;
192 | bottom: 15px;
193 | left: 0;
194 | width: 100%;
195 | color: $scroll-refresh-icon-color;
196 | text-align: center;
197 |
198 | font-size: 30px;
199 |
200 | .text-refreshing,
201 | .text-pulling {
202 | font-size: 16px;
203 | line-height: 16px;
204 | }
205 | &.ionic-refresher-with-text {
206 | bottom: 10px;
207 | }
208 | }
209 |
210 | .icon-refreshing,
211 | .icon-pulling {
212 | width: 100%;
213 | -webkit-backface-visibility: hidden;
214 | -webkit-transform-style: preserve-3d;
215 | backface-visibility: hidden;
216 | transform-style: preserve-3d;
217 | }
218 | .icon-pulling {
219 | @include animation-name(refresh-spin-back);
220 | @include animation-duration(200ms);
221 | @include animation-timing-function(linear);
222 | @include animation-fill-mode(none);
223 | -webkit-transform: translate3d(0,0,0) rotate(0deg);
224 | transform: translate3d(0,0,0) rotate(0deg);
225 | }
226 | .icon-refreshing,
227 | .text-refreshing {
228 | display: none;
229 | }
230 | .icon-refreshing {
231 | @include animation-duration(1.5s);
232 | }
233 |
234 | &.active {
235 | .icon-pulling:not(.pulling-rotation-disabled) {
236 | @include animation-name(refresh-spin);
237 | -webkit-transform: translate3d(0,0,0) rotate(-180deg);
238 | transform: translate3d(0,0,0) rotate(-180deg);
239 | }
240 | &.refreshing {
241 | @include transition(transform .2s);
242 | @include transition(-webkit-transform .2s);
243 | -webkit-transform: scale(1,1);
244 | transform: scale(1,1);
245 | .icon-pulling,
246 | .text-pulling {
247 | display: none;
248 | }
249 | .icon-refreshing,
250 | .text-refreshing {
251 | display: block;
252 | }
253 | &.refreshing-tail{
254 | -webkit-transform: scale(0,0);
255 | transform: scale(0,0);
256 | }
257 | }
258 | }
259 | }
260 |
261 | ion-infinite-scroll {
262 | height: 60px;
263 | width: 100%;
264 | opacity: 0;
265 | display: block;
266 |
267 | @include transition(opacity 0.25s);
268 | @include display-flex();
269 | @include flex-direction(row);
270 | @include justify-content(center);
271 | @include align-items(center);
272 |
273 | .icon {
274 | color: #666666;
275 | font-size: 30px;
276 | color: $scroll-refresh-icon-color;
277 | }
278 |
279 | &.active {
280 | opacity: 1;
281 | }
282 | }
283 |
284 | .overflow-scroll {
285 | overflow-x: hidden;
286 | overflow-y: scroll;
287 | -webkit-overflow-scrolling: touch;
288 | top: 0;
289 | right: 0;
290 | bottom: 0;
291 | left: 0;
292 | position: absolute;
293 |
294 | .scroll {
295 | position: static;
296 | height: 100%;
297 | -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
298 | }
299 | }
300 |
301 |
302 | // Pad top/bottom of content so it doesn't hide behind .bar-title and .bar-tab.
303 | // Note: For these to work, content must come after both bars in the markup
304 | /* If you change these, change platform.scss as well */
305 | .has-header {
306 | top: $bar-height;
307 | }
308 | // Force no header
309 | .no-header {
310 | top: 0;
311 | }
312 |
313 | .has-subheader {
314 | top: $bar-height + $bar-subheader-height;
315 | }
316 | .has-tabs-top {
317 | top: $bar-height + $tabs-height;
318 | }
319 | .has-header.has-subheader.has-tabs-top {
320 | top: $bar-height + $bar-subheader-height + $tabs-height;
321 | }
322 |
323 | .has-footer {
324 | bottom: $bar-footer-height;
325 | }
326 | .has-subfooter {
327 | bottom: $bar-footer-height + $bar-subfooter-height;
328 | }
329 |
330 | .has-tabs,
331 | .bar-footer.has-tabs {
332 | bottom: $tabs-height;
333 | }
334 |
335 | .has-footer.has-tabs {
336 | bottom: $tabs-height + $bar-footer-height;
337 | }
338 |
339 | // A full screen section with a solid background
340 | .pane {
341 | @include translate3d(0,0,0);
342 | @include transition-duration(0);
343 | z-index: $z-index-pane;
344 | }
345 | .view {
346 | z-index: $z-index-view;
347 | }
348 | .pane,
349 | .view {
350 | position: absolute;
351 | top: 0;
352 | right: 0;
353 | bottom: 0;
354 | left: 0;
355 | width: 100%;
356 | height: 100%;
357 | background-color: $base-background-color;
358 | overflow: hidden;
359 | }
360 | .view-container {
361 | position: absolute;
362 | display: block;
363 | width: 100%;
364 | height: 100%;
365 | }
366 |
--------------------------------------------------------------------------------
/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 | font-weight: $headings-font-weight;
147 |
148 | line-height: $bar-height;
149 |
150 | &.title-left {
151 | text-align: left;
152 | }
153 | &.title-right {
154 | text-align: right;
155 | }
156 | }
157 |
158 | .title a {
159 | color: inherit;
160 | }
161 |
162 | .button {
163 | z-index: $z-index-bar-button;
164 | padding: 0 $button-bar-button-padding;
165 | min-width: initial;
166 | min-height: $button-bar-button-height - 1;
167 | font-weight: 400;
168 | font-size: $button-bar-button-font-size;
169 | line-height: $button-bar-button-height;
170 |
171 | &.button-icon:before,
172 | .icon:before,
173 | &.icon:before,
174 | &.icon-left:before,
175 | &.icon-right:before {
176 | padding-right: 2px;
177 | padding-left: 2px;
178 | font-size: $button-bar-button-icon-size;
179 | line-height: $button-bar-button-height;
180 | }
181 |
182 | &.button-icon {
183 | font-size: $bar-title-font-size;
184 | .icon:before,
185 | &:before,
186 | &.icon-left:before,
187 | &.icon-right:before {
188 | vertical-align: top;
189 | font-size: $button-large-icon-size;
190 | line-height: $button-bar-button-height;
191 | }
192 | }
193 | &.button-clear {
194 | padding-right: 2px;
195 | padding-left: 2px;
196 | font-weight: 300;
197 | font-size: $bar-title-font-size;
198 |
199 | .icon:before,
200 | &.icon:before,
201 | &.icon-left:before,
202 | &.icon-right:before {
203 | font-size: $button-large-icon-size;
204 | line-height: $button-bar-button-height;
205 | }
206 | }
207 |
208 | &.back-button {
209 | display: block;
210 | margin-right: 5px;
211 | padding: 0;
212 | white-space: nowrap;
213 | font-weight: 400;
214 | }
215 |
216 | &.back-button.active,
217 | &.back-button.activated {
218 | opacity: 0.2;
219 | }
220 | }
221 |
222 | .button-bar > .button,
223 | .buttons > .button {
224 | min-height: $button-bar-button-height - 1;
225 | line-height: $button-bar-button-height;
226 | }
227 |
228 | .button-bar + .button,
229 | .button + .button-bar {
230 | margin-left: 5px;
231 | }
232 |
233 | // Android 4.4 messes with the display property
234 | .buttons,
235 | .buttons.primary-buttons,
236 | .buttons.secondary-buttons {
237 | display: inherit;
238 | }
239 | .buttons span {
240 | display: inline-block;
241 | }
242 | .buttons-left span {
243 | margin-right: 5px;
244 | }
245 | .buttons-right span {
246 | margin-left: 5px;
247 | }
248 |
249 | // Place the last button in a bar on the right of the bar
250 | .title + .button:last-child,
251 | > .button + .button:last-child,
252 | > .button.pull-right,
253 | .buttons.pull-right,
254 | .title + .buttons {
255 | position: absolute;
256 | top: 5px;
257 | right: 5px;
258 | bottom: 5px;
259 | }
260 |
261 | }
262 |
263 | // Default styles for buttons inside of styled bars
264 | .bar-light {
265 | .button {
266 | @include button-style($bar-light-bg, $bar-light-border, $bar-light-active-bg, $bar-light-active-border, $bar-light-text);
267 | @include button-clear($bar-light-text, $bar-title-font-size);
268 | }
269 | }
270 | .bar-stable {
271 | .button {
272 | @include button-style($bar-stable-bg, $bar-stable-border, $bar-stable-active-bg, $bar-stable-active-border, $bar-stable-text);
273 | @include button-clear($bar-stable-text, $bar-title-font-size);
274 | }
275 | }
276 | .bar-positive {
277 | .button {
278 | @include button-style($bar-positive-bg, $bar-positive-border, $bar-positive-active-bg, $bar-positive-active-border, $bar-positive-text);
279 | @include button-clear(#fff, $bar-title-font-size);
280 | }
281 | }
282 | .bar-calm {
283 | .button {
284 | @include button-style($bar-calm-bg, $bar-calm-border, $bar-calm-active-bg, $bar-calm-active-border, $bar-calm-text);
285 | @include button-clear(#fff, $bar-title-font-size);
286 | }
287 | }
288 | .bar-assertive {
289 | .button {
290 | @include button-style($bar-assertive-bg, $bar-assertive-border, $bar-assertive-active-bg, $bar-assertive-active-border, $bar-assertive-text);
291 | @include button-clear(#fff, $bar-title-font-size);
292 | }
293 | }
294 | .bar-balanced {
295 | .button {
296 | @include button-style($bar-balanced-bg, $bar-balanced-border, $bar-balanced-active-bg, $bar-balanced-active-border, $bar-balanced-text);
297 | @include button-clear(#fff, $bar-title-font-size);
298 | }
299 | }
300 | .bar-energized {
301 | .button {
302 | @include button-style($bar-energized-bg, $bar-energized-border, $bar-energized-active-bg, $bar-energized-active-border, $bar-energized-text);
303 | @include button-clear(#fff, $bar-title-font-size);
304 | }
305 | }
306 | .bar-royal {
307 | .button {
308 | @include button-style($bar-royal-bg, $bar-royal-border, $bar-royal-active-bg, $bar-royal-active-border, $bar-royal-text);
309 | @include button-clear(#fff, $bar-title-font-size);
310 | }
311 | }
312 | .bar-dark {
313 | .button {
314 | @include button-style($bar-dark-bg, $bar-dark-border, $bar-dark-active-bg, $bar-dark-active-border, $bar-dark-text);
315 | @include button-clear(#fff, $bar-title-font-size);
316 | }
317 | }
318 |
319 | // Header at top
320 | .bar-header {
321 | top: 0;
322 | border-top-width: 0;
323 | border-bottom-width: 1px;
324 | &.has-tabs-top{
325 | border-bottom-width: 0px;
326 | background-image: none;
327 | }
328 | }
329 |
330 | // Footer at bottom
331 | .bar-footer {
332 | bottom: 0;
333 | border-top-width: 1px;
334 | border-bottom-width: 0;
335 | background-position: top;
336 |
337 | height: $bar-footer-height;
338 |
339 | &.item-input-inset {
340 | position: absolute;
341 | }
342 | }
343 |
344 | // Don't render padding if the bar is just for tabs
345 | .bar-tabs {
346 | padding: 0;
347 | }
348 |
349 | .bar-subheader {
350 | top: $bar-height;
351 | display: block;
352 |
353 | height: $bar-subheader-height;
354 | }
355 | .bar-subfooter {
356 | bottom: $bar-footer-height;
357 | display: block;
358 |
359 | height: $bar-subfooter-height;
360 | }
361 |
362 | .nav-bar-block {
363 | position: absolute;
364 | top: 0;
365 | right: 0;
366 | left: 0;
367 | z-index: $z-index-bar;
368 | }
369 |
370 | .bar .back-button.hide,
371 | .bar .buttons .hide {
372 | display: none;
373 | }
374 |
--------------------------------------------------------------------------------
/www/js/app.js:
--------------------------------------------------------------------------------
1 | var cipherSafe = angular.module("ciphersafe", ["ionic", "firebase"]);
2 | var fb = new Firebase("https://INSTANCE_ID_HERE.firebaseio.com/");
3 |
4 | cipherSafe.run(function($ionicPlatform, $state) {
5 | $ionicPlatform.ready(function() {
6 | if(window.cordova && window.cordova.plugins.Keyboard) {
7 | cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
8 | }
9 | if(window.StatusBar) {
10 | StatusBar.styleDefault();
11 | }
12 | });
13 | document.addEventListener("resume", function() {
14 | $state.go("locked", {}, {location: "replace"});
15 | }, false);
16 | });
17 |
18 | cipherSafe.config(function($stateProvider, $urlRouterProvider) {
19 | $stateProvider
20 | .state("locked", {
21 | url: "/locked",
22 | templateUrl: "templates/locked.html",
23 | controller: "VaultController",
24 | cache: false
25 | })
26 | .state("createvault", {
27 | url: "/createvault",
28 | templateUrl: "templates/create_vault.html",
29 | controller: "VaultController"
30 | })
31 | .state("firebase", {
32 | url: "/firebase",
33 | templateUrl: "templates/firebase.html",
34 | controller: "FirebaseController"
35 | })
36 | .state("categories", {
37 | url: "/categories/:masterPassword",
38 | templateUrl: "templates/categories.html",
39 | controller: "CategoryController"
40 | })
41 | .state("passwords", {
42 | url: "/passwords/:categoryId/:masterPassword",
43 | templateUrl: "templates/password_list.html",
44 | controller: "PasswordController",
45 | cache: false
46 | })
47 | .state("newpassword", {
48 | url: "/newpassword/:categoryId/:masterPassword",
49 | templateUrl: "templates/password_new.html",
50 | controller: "PasswordController"
51 | })
52 | .state("viewpassword", {
53 | url: "/viewpassword/:categoryId/:masterPassword/:passwordId",
54 | templateUrl: "templates/password_view.html",
55 | controller: "PasswordController"
56 | });
57 | $urlRouterProvider.otherwise('/locked');
58 | });
59 |
60 |
61 | cipherSafe.controller("VaultController", function($scope, $state, $ionicHistory, $firebaseObject, $cipherFactory) {
62 |
63 | $ionicHistory.clearHistory();
64 |
65 | $ionicHistory.nextViewOptions({
66 | disableAnimate: true,
67 | disableBack: true
68 | });
69 |
70 | var fbAuth = fb.getAuth();
71 | if(fbAuth) {
72 | var userReference = fb.child("users/" + fbAuth.uid);
73 | var syncObject = $firebaseObject(userReference);
74 | syncObject.$bindTo($scope, "data");
75 | } else {
76 | $state.go("firebase");
77 | }
78 |
79 | $scope.unlock = function(masterPassword) {
80 | syncObject.$loaded().then(function() {
81 | var decipherPhrase = $cipherFactory.decrypt($scope.data.masterPassword.cipher_text, masterPassword, $scope.data.masterPassword.salt, $scope.data.masterPassword.iv, {output: "hex"});
82 | if(decipherPhrase === "Authenticated".toHex()) {
83 | $state.go("categories", {masterPassword: masterPassword});
84 | }
85 | });
86 | }
87 |
88 | $scope.create = function(masterPassword) {
89 | syncObject.$loaded().then(function() {
90 | userReference.child("masterPassword").set($cipherFactory.encrypt("Authenticated", masterPassword), function(error) {
91 | $state.go("locked");
92 | });
93 | });
94 | }
95 |
96 | $scope.reset = function() {
97 | userReference.remove(function(error) {
98 | if(error) {
99 | console.error("ERROR: " + error);
100 | } else {
101 | $state.go("createvault");
102 | }
103 | });
104 | }
105 |
106 | });
107 |
108 | cipherSafe.controller("FirebaseController", function($scope, $state, $ionicHistory, $firebaseAuth) {
109 |
110 | $ionicHistory.nextViewOptions({
111 | disableAnimate: true,
112 | disableBack: true
113 | });
114 |
115 | var fbAuth = $firebaseAuth(fb);
116 |
117 | $scope.login = function(username, password) {
118 | fbAuth.$authWithPassword({
119 | email: username,
120 | password: password
121 | }).then(function(authData) {
122 | $state.go("locked");
123 | }).catch(function(error) {
124 | console.error("ERROR: " + error);
125 | });
126 | }
127 |
128 | $scope.register = function(username, password) {
129 | fbAuth.$createUser({email: username, password: password}).then(function(userData) {
130 | return fbAuth.$authWithPassword({
131 | email: username,
132 | password: password
133 | });
134 | }).then(function(authData) {
135 | $state.go("createvault");
136 | }).catch(function(error) {
137 | console.error("ERROR: " + error);
138 | });
139 | }
140 |
141 | });
142 |
143 | cipherSafe.controller("CategoryController", function($scope, $ionicPopup, $firebaseObject, $stateParams, $cipherFactory) {
144 |
145 | $scope.masterPassword = $stateParams.masterPassword;
146 | $scope.categories = [];
147 |
148 | var fbAuth = fb.getAuth();
149 | if(fbAuth) {
150 | var categoriesReference = fb.child("users/" + fbAuth.uid);
151 | var syncObject = $firebaseObject(categoriesReference);
152 | syncObject.$bindTo($scope, "data");
153 | } else {
154 | $state.go("firebase");
155 | }
156 |
157 | $scope.list = function() {
158 | syncObject.$loaded().then(function() {
159 | for(var key in $scope.data.categories) {
160 | if($scope.data.categories.hasOwnProperty(key)) {
161 | $scope.categories.push({
162 | id: key,
163 | category: $cipherFactory.decrypt($scope.data.categories[key].category.cipher_text, $stateParams.masterPassword, $scope.data.categories[key].category.salt, $scope.data.categories[key].category.iv)
164 | });
165 | }
166 | }
167 | });
168 | }
169 |
170 | $scope.add = function() {
171 | $ionicPopup.prompt({
172 | title: 'Enter a new category',
173 | inputType: 'text'
174 | })
175 | .then(function(result) {
176 | if(result !== undefined) {
177 | if($scope.data.categories === undefined) {
178 | $scope.data.categories = {};
179 | }
180 | if($scope.data.categories[result.toSHA1()] === undefined) {
181 | $scope.data.categories[result.toSHA1()] = {
182 | category: $cipherFactory.encrypt(result, $stateParams.masterPassword),
183 | passwords: {}
184 | };
185 | $scope.categories.push({
186 | id: result.toSHA1(),
187 | category: result
188 | });
189 | }
190 | } else {
191 | console.log("Action not completed");
192 | }
193 | });
194 | }
195 |
196 | });
197 |
198 | cipherSafe.controller("PasswordController", function($scope, $stateParams, $firebaseObject, $state, $cipherFactory, $ionicHistory) {
199 |
200 | $scope.masterPassword = $stateParams.masterPassword;
201 | $scope.categoryId = $stateParams.categoryId;
202 | $scope.passwords = [];
203 |
204 | var fbAuth = fb.getAuth();
205 | if(fbAuth) {
206 | var categoryReference = fb.child("users/" + fbAuth.uid + "/categories/" + $stateParams.categoryId);
207 | var passwordsReference = fb.child("users/" + fbAuth.uid + "/categories/" + $stateParams.categoryId + "/passwords");
208 | var syncObject = $firebaseObject(categoryReference);
209 | syncObject.$bindTo($scope, "data");
210 | } else {
211 | $state.go("firebase");
212 | }
213 |
214 | $scope.list = function() {
215 | syncObject.$loaded().then(function() {
216 | var encryptedPasswords = $scope.data.passwords;
217 | for(var key in encryptedPasswords) {
218 | if(encryptedPasswords.hasOwnProperty(key)) {
219 | $scope.passwords.push({
220 | id: key,
221 | password: JSON.parse($cipherFactory.decrypt(encryptedPasswords[key].cipher_text, $stateParams.masterPassword, encryptedPasswords[key].salt, encryptedPasswords[key].iv))
222 | });
223 | }
224 | }
225 | });
226 | }
227 |
228 | $scope.view = function() {
229 | syncObject.$loaded().then(function() {
230 | var encryptedPassword = $scope.data.passwords[$stateParams.passwordId];
231 | $scope.password = JSON.parse($cipherFactory.decrypt(encryptedPassword.cipher_text, $stateParams.masterPassword, encryptedPassword.salt, encryptedPassword.iv));
232 | });
233 | }
234 |
235 | $scope.save = function(title, username, password) {
236 | var passwordObject = {
237 | title: title,
238 | username: username,
239 | password: password
240 | };
241 | syncObject.$loaded().then(function() {
242 | passwordsReference.child(JSON.stringify(passwordObject).toSHA1()).set($cipherFactory.encrypt(JSON.stringify(passwordObject), $stateParams.masterPassword), function(ref) {
243 | $state.go("passwords", $stateParams);
244 | });
245 | });
246 | }
247 |
248 | $scope.back = function() {
249 | $ionicHistory.goBack();
250 | }
251 |
252 | });
253 |
254 |
255 | cipherSafe.factory("$cipherFactory", function() {
256 |
257 | return {
258 |
259 | /*
260 | * Encrypt a message with a passphrase or password
261 | *
262 | * @param string message
263 | * @param string password
264 | * @return object
265 | */
266 | encrypt: function(message, password) {
267 | var salt = forge.random.getBytesSync(128);
268 | var key = forge.pkcs5.pbkdf2(password, salt, 4, 16);
269 | var iv = forge.random.getBytesSync(16);
270 | var cipher = forge.cipher.createCipher('AES-CBC', key);
271 | cipher.start({iv: iv});
272 | cipher.update(forge.util.createBuffer(message));
273 | cipher.finish();
274 | var cipherText = forge.util.encode64(cipher.output.getBytes());
275 | return {cipher_text: cipherText, salt: forge.util.encode64(salt), iv: forge.util.encode64(iv)};
276 | },
277 |
278 | /*
279 | * Decrypt cipher text using a password or passphrase and a corresponding salt and iv
280 | *
281 | * @param string (Base64) cipherText
282 | * @param string password
283 | * @param string (Base64) salt
284 | * @param string (Base64) iv
285 | * @param object
286 | * @return string
287 | */
288 | decrypt: function(cipherText, password, salt, iv, options) {
289 | var key = forge.pkcs5.pbkdf2(password, forge.util.decode64(salt), 4, 16);
290 | var decipher = forge.cipher.createDecipher('AES-CBC', key);
291 | decipher.start({iv: forge.util.decode64(iv)});
292 | decipher.update(forge.util.createBuffer(forge.util.decode64(cipherText)));
293 | decipher.finish();
294 | if(options !== undefined && options.hasOwnProperty("output") && options.output === "hex") {
295 | return decipher.output.toHex();
296 | } else {
297 | return decipher.output.toString();
298 | }
299 | }
300 |
301 | };
302 |
303 | });
304 |
305 | String.prototype.toHex = function() {
306 | var buffer = forge.util.createBuffer(this.toString());
307 | return buffer.toHex();
308 | }
309 |
310 | String.prototype.toSHA1 = function() {
311 | var md = forge.md.sha1.create();
312 | md.update(this);
313 | return md.digest().toHex();
314 | }
315 |
--------------------------------------------------------------------------------
/www/lib/ionic/js/angular/angular-animate.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | AngularJS v1.3.6
3 | (c) 2010-2014 Google, Inc. http://angularjs.org
4 | License: MIT
5 | */
6 | (function(N,f,W){'use strict';f.module("ngAnimate",["ng"]).directive("ngAnimateChildren",function(){return function(X,C,g){g=g.ngAnimateChildren;f.isString(g)&&0===g.length?C.data("$$ngAnimateChildren",!0):X.$watch(g,function(f){C.data("$$ngAnimateChildren",!!f)})}}).factory("$$animateReflow",["$$rAF","$document",function(f,C){return function(g){return f(function(){g()})}}]).config(["$provide","$animateProvider",function(X,C){function g(f){for(var n=0;n=C&&b>=x&&c()}var m=g(e);a=e.data("$$ngAnimateCSS3Data");if(-1!=m.getAttribute("class").indexOf(b)&&a){var k="",t="";n(b.split(" "),function(a,
26 | b){var e=(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 | }
111 | }
112 | }
113 | &.tabs-top{
114 | .tab-item {
115 | &.tab-item-active,
116 | &.active,
117 | &.activated {
118 | .badge {
119 | top: 4%;
120 | }
121 | }
122 | }
123 | }
124 | }
125 |
126 | @mixin tabs-background($style, $color, $border-color) {
127 | .#{$style} {
128 | .tabs,
129 | &> .tabs{
130 | background-color: $color;
131 | background-image: linear-gradient(0deg, $border-color, $border-color 50%, transparent 50%);
132 | border-color: $border-color;
133 | }
134 | }
135 | }
136 |
137 | @mixin tabs-striped-background($style, $color) {
138 | &.#{$style} {
139 | .tabs {
140 | background-color: $color;
141 | background-image:none;
142 | }
143 | }
144 | }
145 |
146 | @mixin tabs-color($style, $color) {
147 | .#{$style} {
148 | .tab-item {
149 | color: rgba($color, $tabs-off-opacity);
150 | opacity: 1;
151 | .badge{
152 | opacity:$tabs-off-opacity;
153 | }
154 | &.tab-item-active,
155 | &.active,
156 | &.activated {
157 | color: $color;
158 | border: 0 solid $color;
159 | .badge{
160 | opacity: 1;
161 | }
162 | }
163 | }
164 | }
165 | }
166 |
167 | @mixin tabs-striped-color($style, $color) {
168 | &.#{$style} {
169 | .tab-item {
170 | color: rgba($color, $tabs-striped-off-opacity);
171 | opacity: 1;
172 | .badge{
173 | opacity:$tabs-striped-off-opacity;
174 | }
175 | &.tab-item-active,
176 | &.active,
177 | &.activated {
178 | margin-top: -$tabs-striped-border-width;
179 | color: $color;
180 | border: 0 solid $color;
181 | border-top-width: $tabs-striped-border-width;
182 | .badge{
183 | top:$tabs-striped-border-width;
184 | opacity: 1;
185 | }
186 | }
187 | }
188 | }
189 | }
190 |
191 | .tabs-striped {
192 | .tabs {
193 | background-color: white;
194 | background-image: none;
195 | border: none;
196 | border-bottom: 1px solid #ddd;
197 | padding-top: $tabs-striped-border-width;
198 | }
199 | .tab-item {
200 | // default android tab style
201 | &.tab-item-active,
202 | &.active,
203 | &.activated {
204 | margin-top: -$tabs-striped-border-width;
205 | border-style: solid;
206 | border-width: $tabs-striped-border-width 0 0 0;
207 | border-color: $dark;
208 | .badge{
209 | top:$tabs-striped-border-width;
210 | opacity: 1;
211 | }
212 | }
213 | }
214 | @include tabs-striped('tabs-light', $dark, $light);
215 | @include tabs-striped('tabs-stable', $dark, $stable);
216 | @include tabs-striped('tabs-positive', $light, $positive);
217 | @include tabs-striped('tabs-calm', $light, $calm);
218 | @include tabs-striped('tabs-assertive', $light, $assertive);
219 | @include tabs-striped('tabs-balanced', $light, $balanced);
220 | @include tabs-striped('tabs-energized', $light, $energized);
221 | @include tabs-striped('tabs-royal', $light, $royal);
222 | @include tabs-striped('tabs-dark', $light, $dark);
223 |
224 | // doing this twice so striped tabs styles don't override specific bg and color vals
225 | @include tabs-striped-background('tabs-background-light', $light);
226 | @include tabs-striped-background('tabs-background-stable', $stable);
227 | @include tabs-striped-background('tabs-background-positive', $positive);
228 | @include tabs-striped-background('tabs-background-calm', $calm);
229 | @include tabs-striped-background('tabs-background-assertive', $assertive);
230 | @include tabs-striped-background('tabs-background-balanced', $balanced);
231 | @include tabs-striped-background('tabs-background-energized',$energized);
232 | @include tabs-striped-background('tabs-background-royal', $royal);
233 | @include tabs-striped-background('tabs-background-dark', $dark);
234 |
235 | @include tabs-striped-color('tabs-color-light', $light);
236 | @include tabs-striped-color('tabs-color-stable', $stable);
237 | @include tabs-striped-color('tabs-color-positive', $positive);
238 | @include tabs-striped-color('tabs-color-calm', $calm);
239 | @include tabs-striped-color('tabs-color-assertive', $assertive);
240 | @include tabs-striped-color('tabs-color-balanced', $balanced);
241 | @include tabs-striped-color('tabs-color-energized',$energized);
242 | @include tabs-striped-color('tabs-color-royal', $royal);
243 | @include tabs-striped-color('tabs-color-dark', $dark);
244 |
245 | }
246 |
247 | @include tabs-background('tabs-background-light', $light, $bar-light-border);
248 | @include tabs-background('tabs-background-stable', $stable, $bar-stable-border);
249 | @include tabs-background('tabs-background-positive', $positive, $bar-positive-border);
250 | @include tabs-background('tabs-background-calm', $calm, $bar-calm-border);
251 | @include tabs-background('tabs-background-assertive', $assertive, $bar-assertive-border);
252 | @include tabs-background('tabs-background-balanced', $balanced, $bar-balanced-border);
253 | @include tabs-background('tabs-background-energized',$energized, $bar-energized-border);
254 | @include tabs-background('tabs-background-royal', $royal, $bar-royal-border);
255 | @include tabs-background('tabs-background-dark', $dark, $bar-dark-border);
256 |
257 | @include tabs-color('tabs-color-light', $light);
258 | @include tabs-color('tabs-color-stable', $stable);
259 | @include tabs-color('tabs-color-positive', $positive);
260 | @include tabs-color('tabs-color-calm', $calm);
261 | @include tabs-color('tabs-color-assertive', $assertive);
262 | @include tabs-color('tabs-color-balanced', $balanced);
263 | @include tabs-color('tabs-color-energized',$energized);
264 | @include tabs-color('tabs-color-royal', $royal);
265 | @include tabs-color('tabs-color-dark', $dark);
266 |
267 | @mixin tabs-standard-color($style, $color, $off-color:$dark) {
268 | &.#{$style} {
269 | .tab-item {
270 | color: $off-color;
271 | &.tab-item-active,
272 | &.active,
273 | &.activated {
274 | color: $color;
275 | }
276 | }
277 | }
278 | }
279 |
280 | ion-tabs {
281 | @include tabs-standard-color('tabs-color-active-light', $light, $dark);
282 | @include tabs-standard-color('tabs-color-active-stable', $stable, $dark);
283 | @include tabs-standard-color('tabs-color-active-positive', $positive, $dark);
284 | @include tabs-standard-color('tabs-color-active-calm', $calm, $dark);
285 | @include tabs-standard-color('tabs-color-active-assertive', $assertive, $dark);
286 | @include tabs-standard-color('tabs-color-active-balanced', $balanced, $dark);
287 | @include tabs-standard-color('tabs-color-active-energized',$energized, $dark);
288 | @include tabs-standard-color('tabs-color-active-royal', $royal, $dark);
289 | @include tabs-standard-color('tabs-color-active-dark', $dark, $light);
290 | }
291 |
292 | .tabs-top {
293 | &.tabs-striped {
294 | padding-bottom:0;
295 | .tab-item{
296 | background: transparent;
297 | // animate the top bar, leave bottom for platform consistency
298 | -webkit-transition: all .1s ease;
299 | -moz-transition: all .1s ease;
300 | -ms-transition: all .1s ease;
301 | -o-transition: all .1s ease;
302 | transition: all .1s ease;
303 | &.tab-item-active,
304 | &.active,
305 | &.activated {
306 | margin-top: 0;
307 | margin-bottom: -$tabs-striped-border-width;
308 | border-width: 0px 0px $tabs-striped-border-width 0px !important;
309 | border-style: solid;
310 | }
311 | .badge{
312 | -webkit-transition: all .2s ease;
313 | -moz-transition: all .2s ease;
314 | -ms-transition: all .2s ease;
315 | -o-transition: all .2s ease;
316 | transition: all .2s ease;
317 | }
318 | }
319 | }
320 | }
321 |
322 | /* Allow parent element to have tabs-top */
323 | /* If you change this, change platform.scss as well */
324 | .tabs-top > .tabs,
325 | .tabs.tabs-top {
326 | top: $bar-height;
327 | padding-top: 0;
328 | background-position: bottom;
329 | border-top-width: 0;
330 | border-bottom-width: 1px;
331 | .tab-item {
332 | &.tab-item-active,
333 | &.active,
334 | &.activated {
335 | .badge {
336 | top: 4%;
337 | }
338 | }
339 | }
340 | }
341 | .tabs-top ~ .bar-header {
342 | border-bottom-width: 0;
343 | }
344 |
345 | .tab-item {
346 | @include flex(1);
347 | display: block;
348 | overflow: hidden;
349 |
350 | max-width: $tab-item-max-width;
351 | height: 100%;
352 |
353 | color: inherit;
354 | text-align: center;
355 | text-decoration: none;
356 | text-overflow: ellipsis;
357 | white-space: nowrap;
358 |
359 | font-weight: 400;
360 | font-size: $tabs-text-font-size;
361 | font-family: $font-family-sans-serif;
362 |
363 | opacity: 0.7;
364 |
365 | &:hover {
366 | cursor: pointer;
367 | }
368 | &.tab-hidden{
369 | display:none;
370 | }
371 | }
372 |
373 | .tabs-item-hide > .tabs,
374 | .tabs.tabs-item-hide {
375 | display: none;
376 | }
377 |
378 | .tabs-icon-top > .tabs .tab-item,
379 | .tabs-icon-top.tabs .tab-item,
380 | .tabs-icon-bottom > .tabs .tab-item,
381 | .tabs-icon-bottom.tabs .tab-item {
382 | font-size: $tabs-text-font-size-side-icon;
383 | line-height: $tabs-text-font-size;
384 | }
385 |
386 | .tab-item .icon {
387 | display: block;
388 | margin: 0 auto;
389 | height: $tabs-icon-size;
390 | font-size: $tabs-icon-size;
391 | }
392 |
393 | .tabs-icon-left.tabs .tab-item,
394 | .tabs-icon-left > .tabs .tab-item,
395 | .tabs-icon-right.tabs .tab-item,
396 | .tabs-icon-right > .tabs .tab-item {
397 | font-size: $tabs-text-font-size-side-icon;
398 |
399 | .icon {
400 | display: inline-block;
401 | vertical-align: top;
402 | margin-top: -.1em;
403 |
404 | &:before {
405 | font-size: $tabs-icon-size - 8;
406 | line-height: $tabs-height;
407 | }
408 | }
409 | }
410 |
411 | .tabs-icon-left > .tabs .tab-item .icon,
412 | .tabs-icon-left.tabs .tab-item .icon {
413 | padding-right: 3px;
414 | }
415 |
416 | .tabs-icon-right > .tabs .tab-item .icon,
417 | .tabs-icon-right.tabs .tab-item .icon {
418 | padding-left: 3px;
419 | }
420 |
421 | .tabs-icon-only > .tabs .icon,
422 | .tabs-icon-only.tabs .icon {
423 | line-height: inherit;
424 | }
425 |
426 |
427 | .tab-item.has-badge {
428 | position: relative;
429 | }
430 |
431 | .tab-item .badge {
432 | position: absolute;
433 | top: 4%;
434 | right: 33%; // fallback
435 | right: calc(50% - 26px);
436 | padding: $tabs-badge-padding;
437 | height: auto;
438 | font-size: $tabs-badge-font-size;
439 | line-height: $tabs-badge-font-size + 4;
440 | }
441 |
442 |
443 | /* Navigational tab */
444 |
445 | /* Active state for tab */
446 | .tab-item.tab-item-active,
447 | .tab-item.active,
448 | .tab-item.activated {
449 | opacity: 1;
450 |
451 | &.tab-item-light {
452 | color: $light;
453 | }
454 | &.tab-item-stable {
455 | color: $stable;
456 | }
457 | &.tab-item-positive {
458 | color: $positive;
459 | }
460 | &.tab-item-calm {
461 | color: $calm;
462 | }
463 | &.tab-item-assertive {
464 | color: $assertive;
465 | }
466 | &.tab-item-balanced {
467 | color: $balanced;
468 | }
469 | &.tab-item-energized {
470 | color: $energized;
471 | }
472 | &.tab-item-royal {
473 | color: $royal;
474 | }
475 | &.tab-item-dark {
476 | color: $dark;
477 | }
478 | }
479 |
480 | .item.tabs {
481 | @include display-flex();
482 | padding: 0;
483 |
484 | .icon:before {
485 | position: relative;
486 | }
487 | }
488 |
489 | .tab-item.disabled,
490 | .tab-item[disabled] {
491 | opacity: .4;
492 | cursor: default;
493 | pointer-events: none;
494 | }
495 |
--------------------------------------------------------------------------------
/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 0 1px 4px rgba(0,0,0,0.1);
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, $on-border-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 | border-color: $on-border-color;
139 | }
140 | }
141 |
142 |
143 | // Toggle Mixins
144 | // --------------------------------------------------
145 |
146 | @mixin toggle-style($on-border-color, $on-bg-color) {
147 | // the track when the toggle is "on"
148 | & input:checked + .track {
149 | border-color: $on-border-color;
150 | background-color: $on-bg-color;
151 | }
152 | }
153 |
154 |
155 | // Clearfix
156 | // --------------------------------------------------
157 |
158 | @mixin clearfix {
159 | *zoom: 1;
160 | &:before,
161 | &:after {
162 | display: table;
163 | content: "";
164 | line-height: 0;
165 | }
166 | &:after {
167 | clear: both;
168 | }
169 | }
170 |
171 |
172 | // Placeholder text
173 | // --------------------------------------------------
174 |
175 | @mixin placeholder($color: $input-color-placeholder, $text-indent: 0) {
176 | &::-moz-placeholder { // Firefox 19+
177 | color: $color;
178 | }
179 | &:-ms-input-placeholder {
180 | color: $color;
181 | }
182 | &::-webkit-input-placeholder {
183 | color: $color;
184 | // Safari placeholder margin issue
185 | text-indent: $text-indent;
186 | }
187 | }
188 |
189 |
190 | // Text Mixins
191 | // --------------------------------------------------
192 |
193 | @mixin text-size-adjust($value: none) {
194 | -webkit-text-size-adjust: $value;
195 | -moz-text-size-adjust: $value;
196 | text-size-adjust: $value;
197 | }
198 | @mixin tap-highlight-transparent() {
199 | -webkit-tap-highlight-color: rgba(0,0,0,0);
200 | -webkit-tap-highlight-color: transparent; // For some Androids
201 | }
202 | @mixin touch-callout($value: none) {
203 | -webkit-touch-callout: $value;
204 | }
205 |
206 |
207 | // Font Mixins
208 | // --------------------------------------------------
209 |
210 | @mixin font-family-serif() {
211 | font-family: $serif-font-family;
212 | }
213 | @mixin font-family-sans-serif() {
214 | font-family: $sans-font-family;
215 | }
216 | @mixin font-family-monospace() {
217 | font-family: $mono-font-family;
218 | }
219 | @mixin font-shorthand($size: $base-font-size, $weight: normal, $line-height: $base-line-height) {
220 | font-weight: $weight;
221 | font-size: $size;
222 | line-height: $line-height;
223 | }
224 | @mixin font-serif($size: $base-font-size, $weight: normal, $line-height: $base-line-height) {
225 | @include font-family-serif();
226 | @include font-shorthand($size, $weight, $line-height);
227 | }
228 | @mixin font-sans-serif($size: $base-font-size, $weight: normal, $line-height: $base-line-height) {
229 | @include font-family-sans-serif();
230 | @include font-shorthand($size, $weight, $line-height);
231 | }
232 | @mixin font-monospace($size: $base-font-size, $weight: normal, $line-height: $base-line-height) {
233 | @include font-family-monospace();
234 | @include font-shorthand($size, $weight, $line-height);
235 | }
236 | @mixin font-smoothing($font-smoothing) {
237 | -webkit-font-smoothing: $font-smoothing;
238 | font-smoothing: $font-smoothing;
239 | }
240 |
241 |
242 | // Appearance
243 | // --------------------------------------------------
244 |
245 | @mixin appearance($val) {
246 | -webkit-appearance: $val;
247 | -moz-appearance: $val;
248 | appearance: $val;
249 | }
250 |
251 |
252 | // Border Radius Mixins
253 | // --------------------------------------------------
254 |
255 | @mixin border-radius($radius) {
256 | -webkit-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 | border-top-left-radius: $radius;
264 | }
265 | @mixin border-top-right-radius($radius) {
266 | -webkit-border-top-right-radius: $radius;
267 | border-top-right-radius: $radius;
268 | }
269 | @mixin border-bottom-right-radius($radius) {
270 | -webkit-border-bottom-right-radius: $radius;
271 | border-bottom-right-radius: $radius;
272 | }
273 | @mixin border-bottom-left-radius($radius) {
274 | -webkit-border-bottom-left-radius: $radius;
275 | border-bottom-left-radius: $radius;
276 | }
277 |
278 | // Single Side Border Radius
279 | @mixin border-top-radius($radius) {
280 | @include border-top-right-radius($radius);
281 | @include border-top-left-radius($radius);
282 | }
283 | @mixin border-right-radius($radius) {
284 | @include border-top-right-radius($radius);
285 | @include border-bottom-right-radius($radius);
286 | }
287 | @mixin border-bottom-radius($radius) {
288 | @include border-bottom-right-radius($radius);
289 | @include border-bottom-left-radius($radius);
290 | }
291 | @mixin border-left-radius($radius) {
292 | @include border-top-left-radius($radius);
293 | @include border-bottom-left-radius($radius);
294 | }
295 |
296 |
297 | // Box shadows
298 | // --------------------------------------------------
299 |
300 | @mixin box-shadow($shadow...) {
301 | -webkit-box-shadow: $shadow;
302 | box-shadow: $shadow;
303 | }
304 |
305 |
306 | // Transition Mixins
307 | // --------------------------------------------------
308 |
309 | @mixin transition($transition...) {
310 | -webkit-transition: $transition;
311 | transition: $transition;
312 | }
313 | @mixin transition-delay($transition-delay) {
314 | -webkit-transition-delay: $transition-delay;
315 | transition-delay: $transition-delay;
316 | }
317 | @mixin transition-duration($transition-duration) {
318 | -webkit-transition-duration: $transition-duration;
319 | transition-duration: $transition-duration;
320 | }
321 | @mixin transition-timing-function($transition-timing) {
322 | -webkit-transition-timing-function: $transition-timing;
323 | transition-timing-function: $transition-timing;
324 | }
325 | @mixin transition-property($property) {
326 | -webkit-transition-property: $property;
327 | transition-property: $property;
328 | }
329 | @mixin transition-transform($properties...) {
330 | // special case cuz of transform vendor prefixes
331 | -webkit-transition: -webkit-transform $properties;
332 | transition: transform $properties;
333 | }
334 |
335 |
336 | // Animation Mixins
337 | // --------------------------------------------------
338 |
339 | @mixin animation($animation) {
340 | -webkit-animation: $animation;
341 | animation: $animation;
342 | }
343 | @mixin animation-duration($duration) {
344 | -webkit-animation-duration: $duration;
345 | animation-duration: $duration;
346 | }
347 | @mixin animation-direction($direction) {
348 | -webkit-animation-direction: $direction;
349 | animation-direction: $direction;
350 | }
351 | @mixin animation-timing-function($animation-timing) {
352 | -webkit-animation-timing-function: $animation-timing;
353 | animation-timing-function: $animation-timing;
354 | }
355 | @mixin animation-fill-mode($fill-mode) {
356 | -webkit-animation-fill-mode: $fill-mode;
357 | animation-fill-mode: $fill-mode;
358 | }
359 | @mixin animation-name($name...) {
360 | -webkit-animation-name: $name;
361 | animation-name: $name;
362 | }
363 | @mixin animation-iteration-count($count) {
364 | -webkit-animation-iteration-count: $count;
365 | animation-iteration-count: $count;
366 | }
367 |
368 |
369 | // Transformation Mixins
370 | // --------------------------------------------------
371 |
372 | @mixin rotate($degrees) {
373 | @include transform( rotate($degrees) );
374 | }
375 | @mixin scale($ratio) {
376 | @include transform( scale($ratio) );
377 | }
378 | @mixin translate($x, $y) {
379 | @include transform( translate($x, $y) );
380 | }
381 | @mixin skew($x, $y) {
382 | @include transform( skew($x, $y) );
383 | -webkit-backface-visibility: hidden;
384 | }
385 | @mixin translate3d($x, $y, $z) {
386 | @include transform( translate3d($x, $y, $z) );
387 | }
388 | @mixin translateZ($z) {
389 | @include transform( translateZ($z) );
390 | }
391 | @mixin transform($val) {
392 | -webkit-transform: $val;
393 | transform: $val;
394 | }
395 |
396 | @mixin transform-origin($left, $top) {
397 | -webkit-transform-origin: $left $top;
398 | transform-origin: $left $top;
399 | }
400 |
401 |
402 | // Backface visibility
403 | // --------------------------------------------------
404 | // Prevent browsers from flickering when using CSS 3D transforms.
405 | // Default value is `visible`, but can be changed to `hidden
406 |
407 | @mixin backface-visibility($visibility){
408 | -webkit-backface-visibility: $visibility;
409 | backface-visibility: $visibility;
410 | }
411 |
412 |
413 | // Background clipping
414 | // --------------------------------------------------
415 |
416 | @mixin background-clip($clip) {
417 | -webkit-background-clip: $clip;
418 | background-clip: $clip;
419 | }
420 |
421 |
422 | // Background sizing
423 | // --------------------------------------------------
424 |
425 | @mixin background-size($size) {
426 | -webkit-background-size: $size;
427 | background-size: $size;
428 | }
429 |
430 |
431 | // Box sizing
432 | // --------------------------------------------------
433 |
434 | @mixin box-sizing($boxmodel) {
435 | -webkit-box-sizing: $boxmodel;
436 | -moz-box-sizing: $boxmodel;
437 | box-sizing: $boxmodel;
438 | }
439 |
440 |
441 | // User select
442 | // --------------------------------------------------
443 |
444 | @mixin user-select($select) {
445 | -webkit-user-select: $select;
446 | -moz-user-select: $select;
447 | -ms-user-select: $select;
448 | user-select: $select;
449 | }
450 |
451 |
452 | // Content Columns
453 | // --------------------------------------------------
454 |
455 | @mixin content-columns($columnCount, $columnGap: $grid-gutter-width) {
456 | -webkit-column-count: $columnCount;
457 | -moz-column-count: $columnCount;
458 | column-count: $columnCount;
459 | -webkit-column-gap: $columnGap;
460 | -moz-column-gap: $columnGap;
461 | column-gap: $columnGap;
462 | }
463 |
464 |
465 | // Flexbox Mixins
466 | // --------------------------------------------------
467 | // http://philipwalton.github.io/solved-by-flexbox/
468 | // https://github.com/philipwalton/solved-by-flexbox
469 |
470 | @mixin display-flex {
471 | display: -webkit-box;
472 | display: -webkit-flex;
473 | display: -moz-box;
474 | display: -moz-flex;
475 | display: -ms-flexbox;
476 | display: flex;
477 | }
478 |
479 | @mixin display-inline-flex {
480 | display: -webkit-inline-box;
481 | display: -webkit-inline-flex;
482 | display: -moz-inline-flex;
483 | display: -ms-inline-flexbox;
484 | display: inline-flex;
485 | }
486 |
487 | @mixin flex-direction($value: row) {
488 | @if $value == row-reverse {
489 | -webkit-box-direction: reverse;
490 | -webkit-box-orient: horizontal;
491 | } @else if $value == column {
492 | -webkit-box-direction: normal;
493 | -webkit-box-orient: vertical;
494 | } @else if $value == column-reverse {
495 | -webkit-box-direction: reverse;
496 | -webkit-box-orient: vertical;
497 | } @else {
498 | -webkit-box-direction: normal;
499 | -webkit-box-orient: horizontal;
500 | }
501 | -webkit-flex-direction: $value;
502 | -moz-flex-direction: $value;
503 | -ms-flex-direction: $value;
504 | flex-direction: $value;
505 | }
506 |
507 | @mixin flex-wrap($value: nowrap) {
508 | // No Webkit Box fallback.
509 | -webkit-flex-wrap: $value;
510 | -moz-flex-wrap: $value;
511 | @if $value == nowrap {
512 | -ms-flex-wrap: none;
513 | } @else {
514 | -ms-flex-wrap: $value;
515 | }
516 | flex-wrap: $value;
517 | }
518 |
519 | @mixin flex($fg: 1, $fs: null, $fb: null) {
520 | -webkit-box-flex: $fg;
521 | -webkit-flex: $fg $fs $fb;
522 | -moz-box-flex: $fg;
523 | -moz-flex: $fg $fs $fb;
524 | -ms-flex: $fg $fs $fb;
525 | flex: $fg $fs $fb;
526 | }
527 |
528 | @mixin flex-flow($values: (row nowrap)) {
529 | // No Webkit Box fallback.
530 | -webkit-flex-flow: $values;
531 | -moz-flex-flow: $values;
532 | -ms-flex-flow: $values;
533 | flex-flow: $values;
534 | }
535 |
536 | @mixin align-items($value: stretch) {
537 | @if $value == flex-start {
538 | -webkit-box-align: start;
539 | -ms-flex-align: start;
540 | } @else if $value == flex-end {
541 | -webkit-box-align: end;
542 | -ms-flex-align: end;
543 | } @else {
544 | -webkit-box-align: $value;
545 | -ms-flex-align: $value;
546 | }
547 | -webkit-align-items: $value;
548 | -moz-align-items: $value;
549 | align-items: $value;
550 | }
551 |
552 | @mixin align-self($value: auto) {
553 | -webkit-align-self: $value;
554 | -moz-align-self: $value;
555 | @if $value == flex-start {
556 | -ms-flex-item-align: start;
557 | } @else if $value == flex-end {
558 | -ms-flex-item-align: end;
559 | } @else {
560 | -ms-flex-item-align: $value;
561 | }
562 | align-self: $value;
563 | }
564 |
565 | @mixin align-content($value: stretch) {
566 | -webkit-align-content: $value;
567 | -moz-align-content: $value;
568 | @if $value == flex-start {
569 | -ms-flex-line-pack: start;
570 | } @else if $value == flex-end {
571 | -ms-flex-line-pack: end;
572 | } @else {
573 | -ms-flex-line-pack: $value;
574 | }
575 | align-content: $value;
576 | }
577 |
578 | @mixin justify-content($value: stretch) {
579 | @if $value == flex-start {
580 | -webkit-box-pack: start;
581 | -ms-flex-pack: start;
582 | } @else if $value == flex-end {
583 | -webkit-box-pack: end;
584 | -ms-flex-pack: end;
585 | } @else if $value == space-between {
586 | -webkit-box-pack: justify;
587 | -ms-flex-pack: justify;
588 | } @else {
589 | -webkit-box-pack: $value;
590 | -ms-flex-pack: $value;
591 | }
592 | -webkit-justify-content: $value;
593 | -moz-justify-content: $value;
594 | justify-content: $value;
595 | }
596 |
597 | @mixin flex-order($n) {
598 | -webkit-order: $n;
599 | -ms-flex-order: $n;
600 | order: $n;
601 | -webkit-box-ordinal-group: $n;
602 | }
603 |
604 | @mixin responsive-grid-break($selector, $max-width) {
605 | @media (max-width: $max-width) {
606 | #{$selector} {
607 | -webkit-box-direction: normal;
608 | -moz-box-direction: normal;
609 | -webkit-box-orient: vertical;
610 | -moz-box-orient: vertical;
611 | -webkit-flex-direction: column;
612 | -ms-flex-direction: column;
613 | flex-direction: column;
614 |
615 | .col, .col-10, .col-20, .col-25, .col-33, .col-34, .col-50, .col-66, .col-67, .col-75, .col-80, .col-90 {
616 | @include flex(1);
617 | margin-bottom: ($grid-padding-width * 3) / 2;
618 | margin-left: 0;
619 | max-width: 100%;
620 | width: 100%;
621 | }
622 | }
623 | }
624 | }
625 |
--------------------------------------------------------------------------------
/www/js/angularfire.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * AngularFire is the officially supported AngularJS binding for Firebase. Firebase
3 | * is a full backend so you don't need servers to build your Angular app. AngularFire
4 | * provides you with the $firebase service which allows you to easily keep your $scope
5 | * variables in sync with your Firebase backend.
6 | *
7 | * AngularFire 1.0.0
8 | * https://github.com/firebase/angularfire/
9 | * Date: 03/04/2015
10 | * License: MIT
11 | */
12 | !function(a){"use strict";angular.module("firebase",[]).value("Firebase",a.Firebase).value("firebaseBatchDelay",50)}(window),function(){"use strict";angular.module("firebase").factory("$firebaseArray",["$log","$firebaseUtils",function(a,b){function c(a){if(!(this instanceof c))return new c(a);var e=this;return this._observers=[],this.$list=[],this._ref=a,this._sync=new d(this),b.assertValidRef(a,"Must pass a valid Firebase reference to $firebaseArray (not a string or URL)"),this._indexCache={},b.getPublicMethods(e,function(a,b){e.$list[b]=a.bind(e)}),this._sync.init(this.$list),this.$list}function d(c){function d(a){if(!p.isDestroyed){p.isDestroyed=!0;var b=c.$ref();b.off("child_added",i),b.off("child_moved",k),b.off("child_changed",j),b.off("child_removed",l),c=null,o(a||"destroyed")}}function e(b){var d=c.$ref();d.on("child_added",i,n),d.on("child_moved",k,n),d.on("child_changed",j,n),d.on("child_removed",l,n),d.once("value",function(c){angular.isArray(c.val())&&a.warn("Storing data using array indices in Firebase can result in unexpected behavior. See https://www.firebase.com/docs/web/guide/understanding-data.html#section-arrays-in-firebase for more information."),o(null,b)},o)}function f(a,b){m||(m=!0,a?g.reject(a):g.resolve(b))}var g=b.defer(),h=b.batch(),i=h(function(a,b){var d=c.$$added(a,b);d&&c.$$process("child_added",d,b)}),j=h(function(a){var d=c.$getRecord(b.getKey(a));if(d){var e=c.$$updated(a);e&&c.$$process("child_changed",d)}}),k=h(function(a,d){var e=c.$getRecord(b.getKey(a));if(e){var f=c.$$moved(a,d);f&&c.$$process("child_moved",e,d)}}),l=h(function(a){var d=c.$getRecord(b.getKey(a));if(d){var e=c.$$removed(a);e&&c.$$process("child_removed",d)}}),m=!1,n=h(function(a){f(a),c.$$error(a)}),o=h(f),p={destroy:d,isDestroyed:!1,init:e,ready:function(){return g.promise}};return p}return c.prototype={$add:function(a){this._assertNotDestroyed("$add");var c=b.defer(),d=this.$ref().ref().push();return d.set(b.toJSON(a),b.makeNodeResolver(c)),c.promise.then(function(){return d})},$save:function(a){this._assertNotDestroyed("$save");var c=this,d=c._resolveItem(a),e=c.$keyAt(d);if(null!==e){var f=c.$ref().ref().child(e),g=b.toJSON(d);return b.doSet(f,g).then(function(){return c.$$notify("child_changed",e),f})}return b.reject("Invalid record; could determine key for "+a)},$remove:function(a){this._assertNotDestroyed("$remove");var c=this.$keyAt(a);if(null!==c){var d=this.$ref().ref().child(c);return b.doRemove(d).then(function(){return d})}return b.reject("Invalid record; could not determine key for "+a)},$keyAt:function(a){var b=this._resolveItem(a);return this.$$getKey(b)},$indexFor:function(a){var b=this,c=b._indexCache;if(!c.hasOwnProperty(a)||b.$keyAt(c[a])!==a){var d=b.$list.findIndex(function(c){return b.$$getKey(c)===a});-1!==d&&(c[a]=d)}return c.hasOwnProperty(a)?c[a]:-1},$loaded:function(a,b){var c=this._sync.ready();return arguments.length&&(c=c.then.call(c,a,b)),c},$ref:function(){return this._ref},$watch:function(a,b){var c=this._observers;return c.push([a,b]),function(){var d=c.findIndex(function(c){return c[0]===a&&c[1]===b});d>-1&&c.splice(d,1)}},$destroy:function(b){this._isDestroyed||(this._isDestroyed=!0,this._sync.destroy(b),this.$list.length=0,a.debug("destroy called for FirebaseArray: "+this.$ref().ref().toString()))},$getRecord:function(a){var b=this.$indexFor(a);return b>-1?this.$list[b]:null},$$added:function(a){var c=this.$indexFor(b.getKey(a));if(-1===c){var d=a.val();return angular.isObject(d)||(d={$value:d}),d.$id=b.getKey(a),d.$priority=a.getPriority(),b.applyDefaults(d,this.$$defaults),d}return!1},$$removed:function(a){return this.$indexFor(b.getKey(a))>-1},$$updated:function(a){var c=!1,d=this.$getRecord(b.getKey(a));return angular.isObject(d)&&(c=b.updateRec(d,a),b.applyDefaults(d,this.$$defaults)),c},$$moved:function(a){var c=this.$getRecord(b.getKey(a));return angular.isObject(c)?(c.$priority=a.getPriority(),!0):!1},$$error:function(b){a.error(b),this.$destroy(b)},$$getKey:function(a){return angular.isObject(a)?a.$id:null},$$process:function(a,b,c){var d,e=this.$$getKey(b),f=!1;switch(a){case"child_added":d=this.$indexFor(e);break;case"child_moved":d=this.$indexFor(e),this._spliceOut(e);break;case"child_removed":f=null!==this._spliceOut(e);break;case"child_changed":f=!0;break;default:throw new Error("Invalid event type: "+a)}return angular.isDefined(d)&&(f=this._addAfter(b,c)!==d),f&&this.$$notify(a,e,c),f},$$notify:function(a,b,c){var d={event:a,key:b};angular.isDefined(c)&&(d.prevChild=c),angular.forEach(this._observers,function(a){a[0].call(a[1],d)})},_addAfter:function(a,b){var c;return null===b?c=0:(c=this.$indexFor(b)+1,0===c&&(c=this.$list.length)),this.$list.splice(c,0,a),this._indexCache[this.$$getKey(a)]=c,c},_spliceOut:function(a){var b=this.$indexFor(a);return b>-1?(delete this._indexCache[a],this.$list.splice(b,1)[0]):null},_resolveItem:function(a){var b=this.$list;if(angular.isNumber(a)&&a>=0&&b.length>=a)return b[a];if(angular.isObject(a)){var c=this.$$getKey(a),d=this.$getRecord(c);return d===a?d:null}return null},_assertNotDestroyed:function(a){if(this._isDestroyed)throw new Error("Cannot call "+a+" method on a destroyed $firebaseArray object")}},c.$extend=function(a,d){return 1===arguments.length&&angular.isObject(a)&&(d=a,a=function(){return c.apply(this,arguments)}),b.inherit(a,c,d)},c}]),angular.module("firebase").factory("$FirebaseArray",["$log","$firebaseArray",function(a,b){return function(){return a.warn("$FirebaseArray has been renamed. Use $firebaseArray instead."),b.apply(null,arguments)}}])}(),function(){"use strict";var a;angular.module("firebase").factory("$firebaseAuth",["$q","$firebaseUtils","$log",function(b,c,d){return function(e){var f=new a(b,c,d,e);return f.construct()}}]),a=function(a,b,c,d){if(this._q=a,this._utils=b,this._log=c,"string"==typeof d)throw new Error("Please provide a Firebase reference instead of a URL when creating a `$firebaseAuth` object.");this._ref=d},a.prototype={construct:function(){return this._object={$authWithCustomToken:this.authWithCustomToken.bind(this),$authAnonymously:this.authAnonymously.bind(this),$authWithPassword:this.authWithPassword.bind(this),$authWithOAuthPopup:this.authWithOAuthPopup.bind(this),$authWithOAuthRedirect:this.authWithOAuthRedirect.bind(this),$authWithOAuthToken:this.authWithOAuthToken.bind(this),$unauth:this.unauth.bind(this),$onAuth:this.onAuth.bind(this),$getAuth:this.getAuth.bind(this),$requireAuth:this.requireAuth.bind(this),$waitForAuth:this.waitForAuth.bind(this),$createUser:this.createUser.bind(this),$changePassword:this.changePassword.bind(this),$changeEmail:this.changeEmail.bind(this),$removeUser:this.removeUser.bind(this),$resetPassword:this.resetPassword.bind(this)},this._object},authWithCustomToken:function(a,b){var c=this._q.defer();try{this._ref.authWithCustomToken(a,this._utils.makeNodeResolver(c),b)}catch(d){c.reject(d)}return c.promise},authAnonymously:function(a){var b=this._q.defer();try{this._ref.authAnonymously(this._utils.makeNodeResolver(b),a)}catch(c){b.reject(c)}return b.promise},authWithPassword:function(a,b){var c=this._q.defer();try{this._ref.authWithPassword(a,this._utils.makeNodeResolver(c),b)}catch(d){c.reject(d)}return c.promise},authWithOAuthPopup:function(a,b){var c=this._q.defer();try{this._ref.authWithOAuthPopup(a,this._utils.makeNodeResolver(c),b)}catch(d){c.reject(d)}return c.promise},authWithOAuthRedirect:function(a,b){var c=this._q.defer();try{this._ref.authWithOAuthRedirect(a,this._utils.makeNodeResolver(c),b)}catch(d){c.reject(d)}return c.promise},authWithOAuthToken:function(a,b,c){var d=this._q.defer();try{this._ref.authWithOAuthToken(a,b,this._utils.makeNodeResolver(d),c)}catch(e){d.reject(e)}return d.promise},unauth:function(){null!==this.getAuth()&&this._ref.unauth()},onAuth:function(a,b){var c=this,d=this._utils.debounce(a,b,0);return this._ref.onAuth(d),function(){c._ref.offAuth(d)}},getAuth:function(){return this._ref.getAuth()},_routerMethodOnAuthPromise:function(a){var b=this._ref;return this._utils.promise(function(c,d){function e(f){return b.offAuth(e),null!==f?void c(f):a?void d("AUTH_REQUIRED"):void c(null)}b.onAuth(e)})},requireAuth:function(){return this._routerMethodOnAuthPromise(!0)},waitForAuth:function(){return this._routerMethodOnAuthPromise(!1)},createUser:function(a){var b=this._q.defer();if("string"==typeof a)throw new Error("$createUser() expects an object containing 'email' and 'password', but got a string.");try{this._ref.createUser(a,this._utils.makeNodeResolver(b))}catch(c){b.reject(c)}return b.promise},changePassword:function(a){var b=this._q.defer();if("string"==typeof a)throw new Error("$changePassword() expects an object containing 'email', 'oldPassword', and 'newPassword', but got a string.");try{this._ref.changePassword(a,this._utils.makeNodeResolver(b))}catch(c){b.reject(c)}return b.promise},changeEmail:function(a){if("function"!=typeof this._ref.changeEmail)throw new Error("$changeEmail() expects an object containing 'oldEmail', 'newEmail', and 'password', but got a string.");var b=this._q.defer();try{this._ref.changeEmail(a,this._utils.makeNodeResolver(b))}catch(c){b.reject(c)}return b.promise},removeUser:function(a){var b=this._q.defer();if("string"==typeof a)throw new Error("$removeUser() expects an object containing 'email' and 'password', but got a string.");try{this._ref.removeUser(a,this._utils.makeNodeResolver(b))}catch(c){b.reject(c)}return b.promise},resetPassword:function(a){var b=this._q.defer();if("string"==typeof a)throw new Error("$resetPassword() expects an object containing 'email', but got a string.");try{this._ref.resetPassword(a,this._utils.makeNodeResolver(b))}catch(c){b.reject(c)}return b.promise}}}(),function(){"use strict";angular.module("firebase").factory("$firebaseObject",["$parse","$firebaseUtils","$log",function(a,b,c){function d(a){return this instanceof d?(this.$$conf={sync:new f(this,a),ref:a,binding:new e(this),listeners:[]},Object.defineProperty(this,"$$conf",{value:this.$$conf}),this.$id=b.getKey(a.ref()),this.$priority=null,b.applyDefaults(this,this.$$defaults),void this.$$conf.sync.init()):new d(a)}function e(a){this.subs=[],this.scope=null,this.key=null,this.rec=a}function f(a,d){function e(b){n.isDestroyed||(n.isDestroyed=!0,d.off("value",k),a=null,m(b||"destroyed"))}function f(){d.on("value",k,l),d.once("value",function(a){angular.isArray(a.val())&&c.warn("Storing data using array indices in Firebase can result in unexpected behavior. See https://www.firebase.com/docs/web/guide/understanding-data.html#section-arrays-in-firebase for more information. Also note that you probably wanted $firebaseArray and not $firebaseObject."),m(null)},m)}function g(b){h||(h=!0,b?i.reject(b):i.resolve(a))}var h=!1,i=b.defer(),j=b.batch(),k=j(function(b){var c=a.$$updated(b);c&&a.$$notify()}),l=j(a.$$error,a),m=j(g),n={isDestroyed:!1,destroy:e,init:f,ready:function(){return i.promise}};return n}return d.prototype={$save:function(){var a=this,c=a.$ref(),d=b.toJSON(a);return b.doSet(c,d).then(function(){return a.$$notify(),a.$ref()})},$remove:function(){var a=this;return b.trimKeys(a,{}),a.$value=null,b.doRemove(a.$ref()).then(function(){return a.$$notify(),a.$ref()})},$loaded:function(a,b){var c=this.$$conf.sync.ready();return arguments.length&&(c=c.then.call(c,a,b)),c},$ref:function(){return this.$$conf.ref},$bindTo:function(a,b){var c=this;return c.$loaded().then(function(){return c.$$conf.binding.bindTo(a,b)})},$watch:function(a,b){var c=this.$$conf.listeners;return c.push([a,b]),function(){var d=c.findIndex(function(c){return c[0]===a&&c[1]===b});d>-1&&c.splice(d,1)}},$destroy:function(a){var c=this;c.$isDestroyed||(c.$isDestroyed=!0,c.$$conf.sync.destroy(a),c.$$conf.binding.destroy(),b.each(c,function(a,b){delete c[b]}))},$$updated:function(a){var c=b.updateRec(this,a);return b.applyDefaults(this,this.$$defaults),c},$$error:function(a){c.error(a),this.$destroy(a)},$$scopeUpdated:function(a){var c=b.defer();return this.$ref().set(b.toJSON(a),b.makeNodeResolver(c)),c.promise},$$notify:function(){var a=this,b=this.$$conf.listeners.slice();angular.forEach(b,function(b){b[0].call(b[1],{event:"value",key:a.$id})})},forEach:function(a,c){return b.each(this,a,c)}},d.$extend=function(a,c){return 1===arguments.length&&angular.isObject(a)&&(c=a,a=function(){d.apply(this,arguments)}),b.inherit(a,d,c)},e.prototype={assertNotBound:function(a){if(this.scope){var d="Cannot bind to "+a+" because this instance is already bound to "+this.key+"; one binding per instance (call unbind method or create another FirebaseObject instance)";return c.error(d),b.reject(d)}},bindTo:function(c,d){function e(e){function f(a){return angular.equals(a,k)&&a.$priority===k.$priority&&a.$value===k.$value}function g(a){j.assign(c,b.scopeData(a))}function h(){var a=j(c);return[a,a.$priority,a.$value]}var i=!1,j=a(d),k=e.rec;e.scope=c,e.varName=d;var l=b.debounce(function(a){var d=b.scopeData(a);k.$$scopeUpdated(d)["finally"](function(){i=!1,d.hasOwnProperty("$value")||(delete k.$value,delete j(c).$value)})},50,500),m=function(a){a=a[0],f(a)||(i=!0,l(a))},n=function(){i||f(j(c))||g(k)};return g(k),e.subs.push(c.$on("$destroy",e.unbind.bind(e))),e.subs.push(c.$watch(h,m,!0)),e.subs.push(k.$watch(n)),e.unbind.bind(e)}return this.assertNotBound(d)||e(this)},unbind:function(){this.scope&&(angular.forEach(this.subs,function(a){a()}),this.subs=[],this.scope=null,this.key=null)},destroy:function(){this.unbind(),this.rec=null}},d}]),angular.module("firebase").factory("$FirebaseObject",["$log","$firebaseObject",function(a,b){return function(){return a.warn("$FirebaseObject has been renamed. Use $firebaseObject instead."),b.apply(null,arguments)}}])}(),function(){"use strict";angular.module("firebase").factory("$firebase",function(){return function(){throw new Error("$firebase has been removed. You may instantiate $firebaseArray and $firebaseObject directly now. For simple write operations, just use the Firebase ref directly. See the AngularFire 1.0.0 changelog for details: https://www.firebase.com/docs/web/libraries/angular/changelog.html")}})}(),Array.prototype.indexOf||(Array.prototype.indexOf=function(a,b){if(void 0===this||null===this)throw new TypeError("'this' is null or not defined");var c=this.length>>>0;for(b=+b||0,1/0===Math.abs(b)&&(b=0),0>b&&(b+=c,0>b&&(b=0));c>b;b++)if(this[b]===a)return b;return-1}),Function.prototype.bind||(Function.prototype.bind=function(a){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var b=Array.prototype.slice.call(arguments,1),c=this,d=function(){},e=function(){return c.apply(this instanceof d&&a?this:a,b.concat(Array.prototype.slice.call(arguments)))};return d.prototype=this.prototype,e.prototype=new d,e}),Array.prototype.findIndex||Object.defineProperty(Array.prototype,"findIndex",{enumerable:!1,configurable:!0,writable:!0,value:function(a){if(null==this)throw new TypeError("Array.prototype.find called on null or undefined");if("function"!=typeof a)throw new TypeError("predicate must be a function");for(var b,c=Object(this),d=c.length>>>0,e=arguments[1],f=0;d>f;f++)if(f in c&&(b=c[f],a.call(e,b,f,c)))return f;return-1}}),"function"!=typeof Object.create&&!function(){var a=function(){};Object.create=function(b){if(arguments.length>1)throw new Error("Second argument not supported");if(null===b)throw new Error("Cannot set a null [[Prototype]]");if("object"!=typeof b)throw new TypeError("Argument must be an object");return a.prototype=b,new a}}(),Object.keys||(Object.keys=function(){"use strict";var a=Object.prototype.hasOwnProperty,b=!{toString:null}.propertyIsEnumerable("toString"),c=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],d=c.length;return function(e){if("object"!=typeof e&&("function"!=typeof e||null===e))throw new TypeError("Object.keys called on non-object");var f,g,h=[];for(f in e)a.call(e,f)&&h.push(f);if(b)for(g=0;d>g;g++)a.call(e,c[g])&&h.push(c[g]);return h}}()),"function"!=typeof Object.getPrototypeOf&&(Object.getPrototypeOf="object"==typeof"test".__proto__?function(a){return a.__proto__}:function(a){return a.constructor.prototype}),function(){"use strict";function a(b){if(!angular.isObject(b))return b;var c=angular.isArray(b)?[]:{};return angular.forEach(b,function(b,d){("string"!=typeof d||"$"!==d.charAt(0))&&(c[d]=a(b))}),c}angular.module("firebase").factory("$firebaseConfig",["$firebaseArray","$firebaseObject","$injector",function(a,b,c){return function(d){var e=angular.extend({},d);return"string"==typeof e.objectFactory&&(e.objectFactory=c.get(e.objectFactory)),"string"==typeof e.arrayFactory&&(e.arrayFactory=c.get(e.arrayFactory)),angular.extend({arrayFactory:a,objectFactory:b},e)}}]).factory("$firebaseUtils",["$q","$timeout","firebaseBatchDelay",function(b,c,d){function e(a){function c(a){e.resolve(a)}function d(a){e.reject(a)}if(!angular.isFunction(a))throw new Error("missing resolver function");var e=b.defer();return a(c,d),e.promise}var f={batch:function(a,b){function c(a,b){if("function"!=typeof a)throw new Error("Must provide a function to be batched. Got "+a);return function(){var c=Array.prototype.slice.call(arguments,0);k.push([a,b,c]),e()}}function e(){i&&(i(),i=null),h&&Date.now()-h>b?j||(j=!0,f.compile(g)):(h||(h=Date.now()),i=f.wait(g,a))}function g(){i=null,h=null,j=!1;var a=k.slice(0);k=[],angular.forEach(a,function(a){a[0].apply(a[1],a[2])})}a=d,b||(b=10*a||100);var h,i,j,k=[];return c},debounce:function(a,b,c,d){function e(){j&&(j(),j=null),i&&Date.now()-i>d?l||(l=!0,f.compile(g)):(i||(i=Date.now()),j=f.wait(g,c))}function g(){j=null,i=null,l=!1,a.apply(b,k)}function h(){k=Array.prototype.slice.call(arguments,0),e()}var i,j,k,l;if("number"==typeof b&&(d=c,c=b,b=null),"number"!=typeof c)throw new Error("Must provide a valid integer for wait. Try 0 for a default");if("function"!=typeof a)throw new Error("Must provide a valid function to debounce");return d||(d=10*c||100),h.running=function(){return i>0},h},assertValidRef:function(a,b){if(!angular.isObject(a)||"function"!=typeof a.ref||"function"!=typeof a.ref().transaction)throw new Error(b||"Invalid Firebase reference")},inherit:function(a,b,c){var d=a.prototype;return a.prototype=Object.create(b.prototype),a.prototype.constructor=a,angular.forEach(Object.keys(d),function(b){a.prototype[b]=d[b]}),angular.isObject(c)&&angular.extend(a.prototype,c),a},getPrototypeMethods:function(a,b,c){for(var d={},e=Object.getPrototypeOf({}),f=angular.isFunction(a)&&angular.isObject(a.prototype)?a.prototype:Object.getPrototypeOf(a);f&&f!==e;){for(var g in f)f.hasOwnProperty(g)&&!d.hasOwnProperty(g)&&(d[g]=!0,b.call(c,f[g],g,f));f=Object.getPrototypeOf(f)}},getPublicMethods:function(a,b,c){f.getPrototypeMethods(a,function(a,d){"function"==typeof a&&"_"!==d.charAt(0)&&b.call(c,a,d)})},defer:b.defer,reject:b.reject,resolve:b.when,promise:angular.isFunction(b)?b:e,makeNodeResolver:function(a){return function(b,c){null===b?(arguments.length>2&&(c=Array.prototype.slice.call(arguments,1)),a.resolve(c)):a.reject(b)}},wait:function(a,b){var d=c(a,b||0);return function(){d&&(c.cancel(d),d=null)}},compile:function(a){return c(a||function(){})},deepCopy:function(a){if(!angular.isObject(a))return a;var b=angular.isArray(a)?a.slice():angular.extend({},a);for(var c in b)b.hasOwnProperty(c)&&angular.isObject(b[c])&&(b[c]=f.deepCopy(b[c]));return b},trimKeys:function(a,b){f.each(a,function(c,d){b.hasOwnProperty(d)||delete a[d]})},scopeData:function(a){var b={$id:a.$id,$priority:a.$priority},c=!1;return f.each(a,function(a,d){c=!0,b[d]=f.deepCopy(a)}),!c&&a.hasOwnProperty("$value")&&(b.$value=a.$value),b},updateRec:function(a,b){var c=b.val(),d=angular.extend({},a);return angular.isObject(c)?delete a.$value:(a.$value=c,c={}),f.trimKeys(a,c),angular.extend(a,c),a.$priority=b.getPriority(),!angular.equals(d,a)||d.$value!==a.$value||d.$priority!==a.$priority},applyDefaults:function(a,b){return angular.isObject(b)&&angular.forEach(b,function(b,c){a.hasOwnProperty(c)||(a[c]=b)}),a},dataKeys:function(a){var b=[];return f.each(a,function(a,c){b.push(c)}),b},each:function(a,b,c){if(angular.isObject(a)){for(var d in a)if(a.hasOwnProperty(d)){var e=d.charAt(0);"_"!==e&&"$"!==e&&"."!==e&&b.call(c,a[d],d,a)}}else if(angular.isArray(a))for(var f=0,g=a.length;g>f;f++)b.call(c,a[f],f,a);return a},getKey:function(a){return"function"==typeof a.key?a.key():a.name()},toJSON:function(b){var c;return angular.isObject(b)||(b={$value:b}),angular.isFunction(b.toJSON)?c=b.toJSON():(c={},f.each(b,function(b,d){c[d]=a(b)})),angular.isDefined(b.$value)&&0===Object.keys(c).length&&null!==b.$value&&(c[".value"]=b.$value),angular.isDefined(b.$priority)&&Object.keys(c).length>0&&null!==b.$priority&&(c[".priority"]=b.$priority),angular.forEach(c,function(a,b){if(b.match(/[.$\[\]#\/]/)&&".value"!==b&&".priority"!==b)throw new Error("Invalid key "+b+" (cannot contain .$[]#)");if(angular.isUndefined(a))throw new Error("Key "+b+" was undefined. Cannot pass undefined in JSON. Use null instead.")}),c},doSet:function(a,b){var c=f.defer();if(angular.isFunction(a.set)||!angular.isObject(b))a.set(b,f.makeNodeResolver(c));else{var d=angular.extend({},b);a.once("value",function(b){b.forEach(function(a){d.hasOwnProperty(f.getKey(a))||(d[f.getKey(a)]=null)}),a.ref().update(d,f.makeNodeResolver(c))},function(a){c.reject(a)})}return c.promise},doRemove:function(a){var b=f.defer();return angular.isFunction(a.remove)?a.remove(f.makeNodeResolver(b)):a.once("value",function(c){var d=[];c.forEach(function(a){var c=f.defer();d.push(c.promise),a.ref().remove(f.makeNodeResolver(b))}),f.allPromises(d).then(function(){b.resolve(a)},function(a){b.reject(a)})},function(a){b.reject(a)}),b.promise},VERSION:"1.0.0",batchDelay:d,allPromises:b.all.bind(b)};return f}])}();
--------------------------------------------------------------------------------