├── Procfile ├── _.gitattributes ├── .bowerrc ├── tests ├── matches │ ├── match-profile │ │ └── match-profile-controller.tests.js │ ├── matches-factory.tests.js │ └── matches-controller.tests.js ├── profile │ ├── profile-services.tests.js │ └── profile-controller.tests.js ├── swipe │ └── swipe-services.tests.js ├── protractor.config.js ├── login │ ├── login-controller.tests.js │ └── login-services.tests.js ├── data │ ├── SkippedFactory.tests.js │ ├── RoommateFactory.tests.js │ ├── ProfileFactory.tests.js │ ├── MatchesFactory.tests.js │ └── PlaceFactory.tests.js └── karma.conf.js ├── www ├── img │ ├── face1.png │ ├── face2.png │ ├── face3.jpeg │ ├── face4.jpeg │ ├── face5.jpeg │ ├── ionic.png │ ├── bunk-bed.png │ ├── faceDaniel.png │ └── itschilly.jpg ├── lib │ └── ionic │ │ ├── fonts │ │ ├── ionicons.eot │ │ ├── ionicons.ttf │ │ └── ionicons.woff │ │ └── scss │ │ ├── _progress.scss │ │ ├── _backdrop.scss │ │ ├── ionicons │ │ ├── ionicons.scss │ │ └── _ionicons-font.scss │ │ ├── ionic.scss │ │ ├── _loading.scss │ │ ├── _slide-box.scss │ │ ├── _button-bar.scss │ │ ├── _menu.scss │ │ ├── _animations.scss │ │ ├── _radio.scss │ │ ├── _spinner.scss │ │ ├── _badge.scss │ │ ├── _platform.scss │ │ ├── _popup.scss │ │ ├── _modal.scss │ │ ├── _list.scss │ │ ├── _refresher.scss │ │ ├── _select.scss │ │ ├── _range.scss │ │ ├── _grid.scss │ │ ├── _type.scss │ │ ├── _action-sheet.scss │ │ ├── _popover.scss │ │ ├── _transitions.scss │ │ ├── _checkbox.scss │ │ ├── _toggle.scss │ │ ├── _util.scss │ │ ├── _scaffolding.scss │ │ └── _form.scss ├── profiles │ ├── directives │ │ ├── user-top-box.html │ │ ├── candidate-top-box.html │ │ ├── edit-profile.html │ │ ├── contact-info.html │ │ ├── match-top-box.html │ │ └── like-dislike.html │ ├── profile.directive.js │ ├── profile.js │ └── profile.html ├── swipe │ ├── td-cards-helpers │ │ ├── ionic.tdcards.min.css │ │ └── ionic.tdcards.min.js │ ├── swipe.html │ └── swipe.js ├── matches │ ├── matches.js │ └── matches.html ├── login │ ├── login.html │ ├── login.service.js │ └── login.js ├── chats │ ├── chats.html │ ├── chats.service.js │ └── chats.js ├── account │ ├── people │ │ ├── people.js │ │ └── people.html │ ├── userProfile │ │ ├── icons.txt │ │ ├── userProfile.js │ │ └── userProfile.html │ ├── account.js │ ├── account.html │ └── place │ │ ├── place.js │ │ └── place.html ├── tabs.html ├── map │ ├── map.html │ └── map.js ├── index.html ├── css │ └── style.css └── app.js ├── ionic.project ├── .travis.yml ├── index.js ├── server ├── candidates │ ├── candidateController.js │ ├── candidateModel.js │ └── candidateRoutes.js ├── middleware.js ├── config.js └── users │ ├── mockUserData │ ├── mockApp.js │ ├── README.md │ └── mockUser.schema.json │ ├── userModel.js │ ├── userRoutes.js │ └── userController.js ├── bower.json ├── .jshintrc ├── _.editorconfig ├── scss └── ionic.app.scss ├── .gitignore ├── package.json ├── License.txt ├── README.md ├── gulpfile.js ├── hooks ├── after_prepare │ └── 010_add_platform_class.js └── README.md ├── _PRESS-RELEASE.md └── config.xml /Procfile: -------------------------------------------------------------------------------- 1 | web: node index.js 2 | -------------------------------------------------------------------------------- /_.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "www/lib" 3 | } 4 | -------------------------------------------------------------------------------- /tests/matches/match-profile/match-profile-controller.tests.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /www/img/face1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack829/tfr/HEAD/www/img/face1.png -------------------------------------------------------------------------------- /www/img/face2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack829/tfr/HEAD/www/img/face2.png -------------------------------------------------------------------------------- /www/img/face3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack829/tfr/HEAD/www/img/face3.jpeg -------------------------------------------------------------------------------- /www/img/face4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack829/tfr/HEAD/www/img/face4.jpeg -------------------------------------------------------------------------------- /www/img/face5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack829/tfr/HEAD/www/img/face5.jpeg -------------------------------------------------------------------------------- /www/img/ionic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack829/tfr/HEAD/www/img/ionic.png -------------------------------------------------------------------------------- /www/img/bunk-bed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack829/tfr/HEAD/www/img/bunk-bed.png -------------------------------------------------------------------------------- /www/img/faceDaniel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack829/tfr/HEAD/www/img/faceDaniel.png -------------------------------------------------------------------------------- /www/img/itschilly.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack829/tfr/HEAD/www/img/itschilly.jpg -------------------------------------------------------------------------------- /ionic.project: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tfr", 3 | "app_id": "", 4 | "defaultBrowser": "chrome" 5 | } -------------------------------------------------------------------------------- /www/lib/ionic/fonts/ionicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack829/tfr/HEAD/www/lib/ionic/fonts/ionicons.eot -------------------------------------------------------------------------------- /www/lib/ionic/fonts/ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack829/tfr/HEAD/www/lib/ionic/fonts/ionicons.ttf -------------------------------------------------------------------------------- /www/lib/ionic/fonts/ionicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jack829/tfr/HEAD/www/lib/ionic/fonts/ionicons.woff -------------------------------------------------------------------------------- /www/profiles/directives/user-top-box.html: -------------------------------------------------------------------------------- 1 |
2 |

Your profile

3 |

4 |
-------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | before_script: 5 | - npm install -g gulp 6 | - gulp install 7 | script: gulp test 8 | -------------------------------------------------------------------------------- /www/profiles/directives/candidate-top-box.html: -------------------------------------------------------------------------------- 1 |
2 |

Your next roomate

3 |

4 |
-------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var app = require('./server/config.js') 2 | var port = 8888; 3 | app.listen(port, function() { 4 | console.log("On port ", port); 5 | }) 6 | 7 | -------------------------------------------------------------------------------- /www/profiles/directives/edit-profile.html: -------------------------------------------------------------------------------- 1 |
2 | Edit 3 |
-------------------------------------------------------------------------------- /www/swipe/td-cards-helpers/ionic.tdcards.min.css: -------------------------------------------------------------------------------- 1 | td-cards{display:block}td-card{position:absolute;margin-top:30px}td-card .image{position:relative}td-card img{max-width:100%;max-height:100%} -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /www/profiles/directives/contact-info.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | Email: 4 |

{{boxprofile.email}}

5 |

Phone:
{{boxprofile.phone}}

6 |
7 |
8 | -------------------------------------------------------------------------------- /www/profiles/directives/match-top-box.html: -------------------------------------------------------------------------------- 1 |
2 |

We\'re a match!

3 |

4 | 5 | 6 | 7 |

8 |
-------------------------------------------------------------------------------- /server/candidates/candidateController.js: -------------------------------------------------------------------------------- 1 | var Users = require('../users/userModel.js'); 2 | var Q = require('q'); 3 | 4 | module.exports = { 5 | updateCandidates: function(req, res) { 6 | console.log("candidateController: updateCandidates ", req.body); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /www/profiles/directives/like-dislike.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
-------------------------------------------------------------------------------- /server/candidates/candidateModel.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var CandidateSchema = new mongoose.Schema({ 4 | id: Number, 5 | likesUser: Boolean, 6 | userLikes: Boolean, 7 | seen: Boolean, 8 | isMatch: Boolean 9 | }); 10 | 11 | 12 | var Candidate = mongoose.model('Candidate', CandidateSchema) 13 | module.exports = Candidate; 14 | -------------------------------------------------------------------------------- /tests/matches/matches-factory.tests.js: -------------------------------------------------------------------------------- 1 | describe('services: matches', function() { 2 | 3 | var MatchesFact; 4 | 5 | beforeEach(module('data', 'ui.router')); 6 | 7 | beforeEach(inject(function(_MatchesFactory_) { 8 | MatchesFactory = _MatchesFactory_; 9 | })); 10 | 11 | it('should have a remove function', function(){ 12 | expect(MatchesFactory.remove).toBeDefined(); 13 | }); 14 | }) -------------------------------------------------------------------------------- /tests/profile/profile-services.tests.js: -------------------------------------------------------------------------------- 1 | describe('services: profile', function() { 2 | 3 | var ProfileFact; 4 | 5 | beforeEach(module('data', 'ui.router')); 6 | 7 | beforeEach(inject(function(_ProfileFactory_) { 8 | ProfileFactory = _ProfileFactory_; 9 | })); 10 | 11 | // it('should have a remove function', function(){ 12 | // expect(ProfileFact.remove).toBeDefined(); 13 | // }); 14 | }) -------------------------------------------------------------------------------- /tests/swipe/swipe-services.tests.js: -------------------------------------------------------------------------------- 1 | describe('services: swipe', function() { 2 | 3 | var Candidates; 4 | 5 | beforeEach(module('data', 'ui.router')); 6 | 7 | beforeEach(inject(function(_CandidatesFactory_) { 8 | CandidatesFactory = _CandidatesFactory_; 9 | })); 10 | 11 | it('should have a remove function', function(){ 12 | expect(CandidatesFactory.removeFirst).toBeDefined(); 13 | }); 14 | }) -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tfr", 3 | "private": "true", 4 | "devDependencies": { 5 | "ionic": "driftyco/ionic-bower#1.0.0-rc.2", 6 | "platform": "~1.3.0" 7 | }, 8 | "resolutions": { 9 | "angular": "1.3.15", 10 | "firebase": "2.2.4" 11 | }, 12 | "dependencies": { 13 | "angular-resource": "~1.3.15", 14 | "firebase": "~2.2.4", 15 | "angularfire": "~1.0.0", 16 | "firebase-simple-login": "~1.6.4" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": true, 5 | "camelcase": true, 6 | "curly": true, 7 | "eqeqeq": true, 8 | "immed": true, 9 | "indent": 2, 10 | "latedef": true, 11 | "newcap": true, 12 | "noarg": true, 13 | "quotmark": "single", 14 | "regexp": true, 15 | "undef": true, 16 | "unused": true, 17 | "strict": true, 18 | "trailing": true, 19 | "smarttabs": true, 20 | "white": true 21 | } 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /_.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /www/matches/matches.js: -------------------------------------------------------------------------------- 1 | angular.module('matches.controllers', []) 2 | 3 | 4 | .controller('MatchesCtrl', [ 5 | '$scope', 6 | 'MatchesFactory', 7 | 'User', 8 | 'CandidatesFactory', 9 | 10 | function($scope, MatchesFactory, User, CandidatesFactory){ 11 | $scope.fbId = User.fbId; 12 | $scope.id = User._id; 13 | 14 | $scope.matches = MatchesFactory.all(); 15 | console.log('MATCHES: ', $scope.matches); 16 | 17 | $scope.remove = function(match){ 18 | MatchesFactory.remove(match); 19 | }; 20 | 21 | }]); 22 | -------------------------------------------------------------------------------- /tests/protractor.config.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | capabilities: { 3 | // You can use other browsers 4 | // like firefox, phantoms, safari, IE (-_-) 5 | 'browserName': 'chrome' 6 | }, 7 | specs: [ 8 | // We are going to make this file in a minute 9 | 'e2e/becomeAwesome.spec.js' 10 | ], 11 | jasmineNodeOpts: { 12 | showColors: true, 13 | defaultTimeoutInterval: 30000, 14 | isVerbose: true, 15 | }, 16 | allScriptsTimeout: 20000, 17 | onPrepare: function(){ 18 | browser.driver.get('http://localhost:3000'); 19 | } 20 | }; -------------------------------------------------------------------------------- /www/login/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |


6 |
7 |
8 |

RooMe

9 |

10 | 11 |
12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /tests/matches/matches-controller.tests.js: -------------------------------------------------------------------------------- 1 | describe('controllers: matches', function() { 2 | // var scope, $login, controller; 3 | var scope, User; 4 | 5 | //load controller's module and other necessary modules 6 | beforeEach(module('matches.controllers')); 7 | 8 | beforeEach(inject(function($rootScope, $controller, _User_) { 9 | scope = $rootScope.$new(); 10 | var controller = $controller('MatchesCtrl', { $scope: scope}); 11 | User = _User_; 12 | })); 13 | 14 | //tests start here 15 | // it('should have a remove matches function', function() { 16 | // expect(scope.remove).toBeDefined(); 17 | // }); 18 | 19 | }); 20 | -------------------------------------------------------------------------------- /www/lib/ionic/scss/ionicons/ionicons.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | @import "ionicons-variables"; 3 | /*! 4 | Ionicons, v2.0.1 5 | Created by Ben Sperry for the Ionic Framework, http://ionicons.com/ 6 | https://twitter.com/benjsperry https://twitter.com/ionicframework 7 | MIT License: https://github.com/driftyco/ionicons 8 | 9 | Android-style icons originally built by Google’s 10 | Material Design Icons: https://github.com/google/material-design-icons 11 | used under CC BY http://creativecommons.org/licenses/by/4.0/ 12 | Modified icons to fit ionicon’s grid from original. 13 | */ 14 | 15 | @import "ionicons-font"; 16 | @import "ionicons-icons"; 17 | -------------------------------------------------------------------------------- /server/candidates/candidateRoutes.js: -------------------------------------------------------------------------------- 1 | var candidateController = require('./candidateController.js') 2 | 3 | module.exports = function(app) { 4 | 5 | app.route('/matches') 6 | .put(candidateController.updateCandidates) 7 | }; 8 | 9 | // console.log("in user router") 10 | // var findOrCreate = q.nbind(Users.findOneAndUpdate, Users); 11 | 12 | 13 | // Get request to candidates will return the 10 closest candidates 14 | // Another thought with distance is to go back to using zip codes on the backend 15 | // would be easier to queary the database for users that have a zip code of 'x', 'y', or 'z' 16 | // as opposed to checking if their latitude and longitude are within the range 17 | -------------------------------------------------------------------------------- /www/chats/chats.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

{{chat.from}}

7 |

"{{chat.message}}"

8 | 9 |
10 |
11 | 12 | 13 | 16 | 17 | 18 | 19 |
20 |
21 | -------------------------------------------------------------------------------- /server/middleware.js: -------------------------------------------------------------------------------- 1 | var morgan = require('morgan'); 2 | var bodyParser = require('body-parser'); 3 | var app = require('./config.js'); 4 | 5 | module.exports = function(app, express) { 6 | var userRouter = express.Router(); 7 | var candidateRouter = express.Router(); 8 | 9 | app.use(morgan('dev')); 10 | app.use(bodyParser.json()); 11 | app.use(express.static('www')); 12 | app.get('/', function(req, res) { 13 | res.sendFile('index.html'); 14 | }); 15 | 16 | //user router for user requests (ie. initial login, update prefs) 17 | app.use('/user', userRouter); 18 | 19 | // candidate router for user's potential candidates 20 | app.use('/candidates', candidateRouter); 21 | 22 | require('./users/userRoutes.js')(userRouter); 23 | require('./candidates/candidateRoutes.js')(candidateRouter); 24 | }; 25 | -------------------------------------------------------------------------------- /server/config.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var mongoose = require('mongoose'); 3 | 4 | var app = express(); 5 | 6 | var mongoURI = process.env.MONGOLAB_URI || 'mongodb://localhost/roome'; 7 | 8 | mongoose.connect(mongoURI); 9 | var db = mongoose.connection; 10 | 11 | db.on('error', console.error.bind(console, 'connection error:')); 12 | db.once('open', function() { 13 | console.log('Mongodb connection open'); 14 | }); 15 | 16 | app.all('/*', function(req, res, next) { 17 | res.header('Access-Control-Allow-Origin', '*'); 18 | res.header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type'); 19 | res.header('Access-Control-Allow-Methods', 'GET, POST,PUT, OPTIONS'); 20 | next(); 21 | }); 22 | 23 | 24 | module.exports = db; 25 | module.exports = app; 26 | require('./middleware.js')(app, express); 27 | -------------------------------------------------------------------------------- /www/account/people/people.js: -------------------------------------------------------------------------------- 1 | angular.module('people.controllers', []) 2 | 3 | .controller('PeopleCtrl', [ 4 | '$scope', 5 | '$state', 6 | 'User', 7 | 'PlaceFactory', 8 | 'RoommateFactory', 9 | 10 | function($scope, $state, User, PlaceFactory, RoommateFactory){ 11 | 12 | $scope.fbId = User.fbid; 13 | $scope.username = User.name; 14 | 15 | $scope.people = User.roommatePreferences || RoommateFactory.all(); 16 | 17 | $scope.savePeople = function(){ 18 | RoommateFactory.initialize($scope.people, User) 19 | .then(function(res) { 20 | console.log('roommatePreferences response ', res); 21 | User.roommatePreferences = res; 22 | console.log('User after roommate update ', User) 23 | }) 24 | $state.go('tab.account'); 25 | }; 26 | 27 | }]); 28 | -------------------------------------------------------------------------------- /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 | "refresher", 31 | "spinner", 32 | 33 | // Forms 34 | "form", 35 | "checkbox", 36 | "toggle", 37 | "radio", 38 | "range", 39 | "select", 40 | "progress", 41 | 42 | // Buttons 43 | "button", 44 | "button-bar", 45 | 46 | // Util 47 | "grid", 48 | "util", 49 | "platform", 50 | 51 | // Animations 52 | "animations", 53 | "transitions"; 54 | -------------------------------------------------------------------------------- /www/matches/matches.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

{{match.name}}

7 | 8 | 9 | Delete 10 | 11 |
12 |
13 | 14 |
15 | 16 |

You don't have any matches, better get to swiping!

17 |
18 |
19 | 20 |
21 |
-------------------------------------------------------------------------------- /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/tabs.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tests/login/login-controller.tests.js: -------------------------------------------------------------------------------- 1 | describe('Controller: Login', function() { 2 | // var scope, $login, controller; 3 | var scope, createController, state; 4 | 5 | //load controller's module and other necessary modules 6 | beforeEach(module('login.controllers', 'login.services', 'ui.router')); 7 | 8 | beforeEach(inject(function($rootScope, $controller, $state) { 9 | state = $state; 10 | scope = $rootScope.$new(); 11 | createController = function() { 12 | return $controller('LoginCtrl', { $scope: scope}) 13 | } 14 | })); 15 | 16 | //issues with firebaseAuthProvider 17 | xit('should have a save user function', function() { 18 | spyOn($state, 'go').andCallFake(function(state, params){ 19 | 20 | }) 21 | var controller = createController(); 22 | //expect(scope.login).to.be.a('function'); 23 | // console.log("scope ", scope.fbLogin); 24 | expect(scope.login).toBeDefined(); 25 | }); 26 | 27 | }); 28 | -------------------------------------------------------------------------------- /www/chats/chats.service.js: -------------------------------------------------------------------------------- 1 | angular.module('chats.services', ['firebase']) 2 | 3 | .factory('Chats', [ 4 | '$firebaseArray', 5 | '$firebaseObject', 6 | 'FIREBASE_REF', 7 | 'userSession', 8 | '$stateParams', 9 | 10 | function($firebaseArray, $firebaseObject, FIREBASE_REF, userSession, $stateParams) { 11 | 12 | return { 13 | setChats: function(chatURL){ 14 | var ref = new Firebase(FIREBASE_REF+"/chats"); 15 | return $firebaseArray(ref.child(chatURL)); 16 | }, 17 | setUserAccess: function(chatURL,userId,matchId){ 18 | var ref = new Firebase(FIREBASE_REF+"/chatAccessIDs"); 19 | ref.child(chatURL).set({ 20 | user1:"facebook:"+userId, 21 | user2:"facebook:"+matchId 22 | }); 23 | }, 24 | add: function(firebaseArr,from,message,fbId){ 25 | var photoUri = "//graph.facebook.com/"+fbId+"/picture"; 26 | firebaseArr.$add({from:from,message:message,photoUri:photoUri}); 27 | } 28 | }; 29 | }]); 30 | -------------------------------------------------------------------------------- /server/users/mockUserData/mockApp.js: -------------------------------------------------------------------------------- 1 | var MongoClient = require('mongodb').MongoClient; 2 | var assert = require('assert'); 3 | var mockData = require('./mockData.json'); 4 | 5 | // Connection URL 6 | var url = 'mongodb://localhost:27017/roome'; 7 | // Use connect method to connect to the Server 8 | MongoClient.connect(url, function(err, db) { 9 | assert.equal(null, err); 10 | console.log("Connected correctly to server"); 11 | 12 | insertDocuments(db, function() { 13 | db.close(); 14 | }); 15 | }); 16 | 17 | 18 | var insertDocuments = function(db, callback) { 19 | // Get the documents collection 20 | var collection = db.collection('users'); 21 | // Insert some documents 22 | collection.insert(mockData, function(err, result) { 23 | assert.equal(err, null); 24 | assert.equal(result.ops.length, result.result.n); 25 | console.log('result - ', result); 26 | console.log("Inserted " + result.ops.length + " documents into the users collection"); 27 | callback(result); 28 | }); 29 | } -------------------------------------------------------------------------------- /www/account/userProfile/icons.txt: -------------------------------------------------------------------------------- 1 | icons: ["ion-thumbsdown","ion-bowtie","ion-tshirt-outline","ion-play","ion-ios-box"], 2 | icons: ["ion-shuffle","ion-ios-musical-notes","ion-usb","ion-aperture","ion-pound"], 3 | icons: ["ion-compose","ion-fork-repo","ion-social-reddit-outline","ion-bowtie","ion-soup-can-outline"], 4 | icons: ["ion-ios-rose-outline","ion-egg","ion-ios-cart","ion-model-s","ion-ios-game-controller-a"], 5 | icons: ["ion-ios-pint","ion-asterisk","ion-bag","ion-model-s","ion-ios-world"], 6 | icons: ["ion-happy-outline","ion-social-yen","ion-ios-pricetag","ion-chevron-up","ion-model-s"], 7 | icons: ["ion-android-chat","ion-ios-albums","ion-android-bicycle","ion-social-javascript-outline","ion-ios-list"], 8 | icons: ["ion-calendar","ion-social-pinterest","ion-social-github-outline","ion-social-html5-outline","ion-ios-glasses"], 9 | icons: ["ion-ios-undo","ion-university","ion-social-css3","ion-ios-photos-outline","ion-model-s"], 10 | icons: ["ion-hammer","ion-ribbon-a","ion-iphone","ion-pricetags","ion-laptop"], 11 | -------------------------------------------------------------------------------- /tests/login/login-services.tests.js: -------------------------------------------------------------------------------- 1 | describe('Services: login:', function() { 2 | 3 | var LoginFact, httpBackend; 4 | 5 | beforeEach(module('login.services')); 6 | 7 | beforeEach(inject(function(_LoginFact_, $httpBackend) { 8 | LoginFact = _LoginFact_; 9 | httpBackend = $httpBackend; 10 | })); 11 | 12 | afterEach(function() { 13 | httpBackend.verifyNoOutstandingExpectation(); 14 | httpBackend.verifyNoOutstandingRequest(); 15 | }); 16 | 17 | it('should have a get fb info function and saveuser function', function(){ 18 | expect(LoginFact.getFbInfo).toBeDefined(); 19 | expect(LoginFact.saveUser).toBeDefined(); 20 | }); 21 | 22 | describe('saveUser:', function() { 23 | 24 | it('should get send a post request with the user data', function() { 25 | LoginFact.saveUser({id: 1234, name: "Jack"}); 26 | httpBackend.expectPOST('http://localhost:8888/user/1234', {name: "Jack"}) 27 | .respond(200); 28 | httpBackend.flush(); 29 | }) 30 | 31 | }) 32 | 33 | }) -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | www/lib/ 2 | www/swipe/td-cards-helpers/non-min 3 | !www/lib/ionic 4 | bower_components/ 5 | *.log 6 | server/users/secret.js 7 | 8 | build/ 9 | dist/ 10 | resources/ 11 | 12 | # Specifies intentionally untracked files to ignore when using Git 13 | # http://git-scm.com/docs/gitignore 14 | 15 | node_modules/ 16 | platforms/ 17 | plugins/ 18 | hooks/ 19 | 20 | ### node etc ### 21 | 22 | # Logs 23 | logs 24 | *.log 25 | 26 | # Runtime data 27 | pids 28 | *.pid 29 | *.seed 30 | 31 | # Directory for instrumented libs generated by jscoverage/JSCover 32 | lib-cov 33 | 34 | # Coverage directory used by tools like istanbul 35 | coverage 36 | 37 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 38 | .grunt 39 | 40 | # Compiled Dirs (http://nodejs.org/api/addons.html) 41 | build/ 42 | dist/ 43 | 44 | # Dependency directorys 45 | # Deployed apps should consider commenting these lines out: 46 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 47 | node_modules/ 48 | bower_components/ 49 | 50 | #Build resources 51 | resources/ -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tfr", 3 | "version": "1.0.0", 4 | "description": "ionic-testing: An Ionic project", 5 | "repository": { 6 | "type": "git", 7 | "url": "http://github.com/puppies-and-kitties/tfr.git" 8 | }, 9 | "dependencies": { 10 | "body-parser": "^1.12.2", 11 | "express": "^4.12.3", 12 | "gulp": "^3.5.6", 13 | "gulp-concat": "^2.2.0", 14 | "gulp-minify-css": "^0.3.0", 15 | "gulp-nodemon": "^2.0.2", 16 | "gulp-rename": "^1.2.0", 17 | "gulp-sass": "^1.3.3", 18 | "jwt-simple": "^0.3.0", 19 | "mongodb": "^2.0.27", 20 | "mongodb-datasets": "^0.1.5", 21 | "mongoose": "^4.0.1", 22 | "morgan": "^1.5.2", 23 | "protractor": "^2.0.0", 24 | "q": "^1.2.0" 25 | }, 26 | "devDependencies": { 27 | "angular-mocks": "^1.3.15", 28 | "bower": "^1.3.3", 29 | "gulp-util": "^2.2.14", 30 | "jasmine-core": "^2.2.0", 31 | "karma": "^0.12.31", 32 | "karma-chrome-launcher": "^0.1.7", 33 | "karma-jasmine": "^0.3.5", 34 | "karma-phantomjs-launcher": "^0.1.4", 35 | "shelljs": "^0.3.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | License 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2015 David Blanchard, Daniel Miller, Jack Peterson 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /www/login/login.service.js: -------------------------------------------------------------------------------- 1 | angular.module('login.services', ['ngResource']) 2 | 3 | .factory('LoginFact', [ 4 | '$resource', 5 | '$http', 6 | '$rootScope', 7 | 8 | function($resource, $http, $rootScope){ 9 | 10 | var UserProfile = $resource('https://graph.facebook.com/me') 11 | var fbToken = window.sessionStorage['fbtoken']; 12 | 13 | var getFbInfo = function(){ 14 | // Returns a promise with the logged in user's basic FB info, this is resolved in the app.js stateprovider 15 | return UserProfile.get({access_token: fbToken}).$promise; 16 | } 17 | 18 | var saveUser = function(user) { 19 | var baseUrl = 'http://localhost:8888'; 20 | return $http({ 21 | method: 'POST', 22 | url: baseUrl + '/user/' + user.id, 23 | data: { 24 | name: user.name, 25 | token: user.token 26 | } 27 | }) 28 | .then(function(data) { 29 | console.log('saveUser returned data',data); 30 | $rootScope.$emit('userRetrieved', data); 31 | return data; 32 | }) 33 | }; 34 | 35 | return { 36 | saveUser: saveUser, 37 | getFbInfo: getFbInfo 38 | }; 39 | 40 | }]) 41 | 42 | .value('FIREBASE_REF','https://tinderforroomies.firebaseio.com') 43 | .value('userSession',{}); 44 | -------------------------------------------------------------------------------- /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/account/account.js: -------------------------------------------------------------------------------- 1 | angular.module('account.controllers', []) 2 | 3 | .controller('AccountCtrl', [ 4 | '$scope', 5 | '$state', 6 | 'userSession', 7 | 'User', 8 | '$rootScope', 9 | 'ProfileFactory', 10 | '$ionicPopup', 11 | 12 | function($scope, $state, userSession, User, $rootScope, ProfileFactory, $ionicPopup){ 13 | 14 | $scope.username = User.name; 15 | 16 | $scope.fbId = User.fbid; 17 | 18 | $scope.logout = function(){ 19 | userSession.auth.$unauth(); 20 | }; 21 | 22 | $scope.alert = function() { 23 | $ionicPopup.show({ 24 | subTitle: 'Delete account? All of your account data will be removed.', 25 | // subTitle: , 26 | buttons: [ 27 | { 28 | text: 'Delete', 29 | type: 'button-assertive', 30 | onTap: function() { 31 | $scope.deleteAccount(true); 32 | } 33 | }, 34 | { 35 | text: 'Cancel', 36 | onTap: function () { 37 | $scope.deleteAccount(false); 38 | } 39 | } 40 | ] 41 | }) 42 | }; 43 | 44 | $scope.deleteAccount = function(bool) { 45 | if(bool) { 46 | ProfileFactory.deleteAccount(User); 47 | $state.go('login'); 48 | } 49 | }; 50 | 51 | }]); 52 | -------------------------------------------------------------------------------- /www/account/account.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

Welcome, {{username}}

7 |
8 | 9 | 12 | 13 | 16 | 17 | 20 |
21 |
22 | 23 | 24 | 32 | 33 |
34 | -------------------------------------------------------------------------------- /server/users/userModel.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var UserSchema = new mongoose.Schema({ 4 | loc: { type: [Number], index: '2dsphere'}, 5 | fbid: String, 6 | token: String, 7 | name: String, 8 | face: String, 9 | email: String, 10 | profile: { 11 | gender: String, 12 | age: Number, 13 | icons: [], 14 | keywords: [] 15 | }, 16 | location: { 17 | host: Boolean, 18 | myPlace: { 19 | rent: Number, 20 | zipCode: Number, 21 | genders: Number, 22 | openRooms: Number, 23 | roomType: String, 24 | occupants: Number, 25 | zipCode: Number, 26 | city: String, 27 | state: String, 28 | latitude: Number, 29 | longitude: Number 30 | }, 31 | desiredPlace: { 32 | rent: Number, 33 | zipCode: Number, 34 | radius: Number, 35 | roomType: String, 36 | city: String, 37 | state: String, 38 | latitude: Number, 39 | longitude: Number 40 | } 41 | }, 42 | roommatePreferences: { 43 | gender: String, 44 | ageMin: Number, 45 | ageMax: Number 46 | }, 47 | candidates: [{type: mongoose.Schema.Types.ObjectId}], 48 | liked: [{type: mongoose.Schema.Types.ObjectId}], 49 | skipped: [], 50 | matched: {} 51 | }) 52 | 53 | var User = mongoose.model('User', UserSchema) 54 | 55 | module.exports = User; 56 | 57 | -------------------------------------------------------------------------------- /www/account/place/place.js: -------------------------------------------------------------------------------- 1 | angular.module('preferences.controllers', []) 2 | 3 | .controller('PlaceCtrl', [ 4 | '$scope', 5 | '$state', 6 | 'User', 7 | 'PlaceFactory', 8 | 'ProfileFactory', 9 | 'userSession', 10 | 'CandidatesFactory', 11 | 12 | function($scope, $state, User, PlaceFactory, ProfileFactory, userSession, CandidatesFactory){ 13 | 14 | $scope.fbId = User.fbid; 15 | $scope.username = User.name; 16 | $scope.notHost = {"checked": true}; 17 | 18 | $scope.location = User.location || PlaceFactory.all(); 19 | console.log("initial location ", $scope.location) 20 | 21 | $scope.toggleHost = function(status, input) { 22 | console.log('status pre click ', status); 23 | if(status === null) { 24 | $scope.location.host = input; 25 | } 26 | else { 27 | $scope.location.host = null; 28 | } 29 | }; 30 | 31 | $scope.savePreferences = function(){ 32 | PlaceFactory.initialize($scope.location, User) 33 | .then(function(res) { 34 | console.log('Account: Place: response from database after saving ', res); 35 | User.location = res; 36 | CandidatesFactory.initialize(User); 37 | }) 38 | $state.go('tab.account'); 39 | }; 40 | 41 | $scope.logout = function(){ 42 | userSession.auth.$logout(); 43 | }; 44 | 45 | }]); 46 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /www/account/userProfile/userProfile.js: -------------------------------------------------------------------------------- 1 | angular.module('userProfile.controllers', []) 2 | 3 | .controller('UserProfileCtrl', [ 4 | '$scope', 5 | '$state', 6 | 'User', 7 | 'ProfileFactory', 8 | 'IoniconsFact', 9 | 10 | function($scope, $state, User, ProfileFactory, IoniconsFact){ 11 | 12 | $scope.fbId = User.fbid; 13 | $scope.username = User.name; 14 | 15 | $scope.profile = User.profile || ProfileFactory.all(); 16 | $scope.profile.icons = []; 17 | 18 | $scope.ionicons = []; 19 | 20 | $scope.toggleHost = function(status, input) { 21 | if(status === null) { 22 | $scope.profile.host = input; 23 | } 24 | else { 25 | $scope.profile.host = null; 26 | } 27 | }; 28 | 29 | $scope.saveProfile = function(){ 30 | ProfileFactory.initialize($scope.profile, User) 31 | .then(function(res) { 32 | console.log('Profile Data in userProfileCtrl saveProfile: ', res); 33 | User.profile = res; 34 | }) 35 | $state.go('tab.account'); 36 | }; 37 | 38 | $scope.getIonicons = function(word){ 39 | return IoniconsFact.get(word); 40 | }; 41 | 42 | $scope.addToSelected = function(ionicon){ 43 | $scope.profile.icons.push(ionicon); 44 | }; 45 | 46 | $scope.removeFromSelected = function(ionicon){ 47 | var indexToRemove = $scope.profile.selectedIcons.indexOf(ionicon); 48 | $scope.profile.icons.splice(indexToRemove,1); 49 | }; 50 | 51 | }]); 52 | -------------------------------------------------------------------------------- /www/login/login.js: -------------------------------------------------------------------------------- 1 | angular.module('login.controllers', ['firebase', 'ui.router']) 2 | 3 | 4 | .controller('LoginCtrl', [ 5 | '$scope', 6 | '$state', 7 | '$firebaseAuth', 8 | 'FIREBASE_REF', 9 | 'userSession', 10 | '$http', 11 | 12 | function($scope, $state, $firebaseAuth, FIREBASE_REF, userSession,$http){ 13 | 14 | var ref = new Firebase(FIREBASE_REF); 15 | 16 | ref.onAuth(function(authData){ 17 | if (authData) { 18 | console.log('onAuth auth data',authData); 19 | $http({ 20 | method: 'POST', 21 | url: 'http://localhost:8888/user/auth/'+authData.facebook.id, 22 | data: {token: authData.token} 23 | }) 24 | .then(function(user){ 25 | console.log('Authenicated on server',user.data); 26 | userSession.user = authData.facebook.cachedUserProfile; 27 | userSession.user.token = user.data.token; 28 | $state.go('tab.account'); 29 | }); 30 | 31 | 32 | } 33 | else { 34 | $state.go('login'); 35 | } 36 | }); 37 | 38 | userSession.auth = $firebaseAuth(ref); 39 | 40 | $scope.login = function(provider){ 41 | userSession.auth.$authWithOAuthPopup('facebook', function(error, authData) { 42 | if (error) { 43 | console.log('Login Failed!', error); 44 | } 45 | else { 46 | console.log('Authenticated successfully with payload:', authData); 47 | } 48 | }); 49 | }; 50 | 51 | }]); 52 | -------------------------------------------------------------------------------- /www/account/people/people.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

{{username}}

7 |
8 | 9 | 10 |

Who Do You Want to Live With?

11 | 12 |
13 |

Gender

14 | 19 |
20 |
21 |

Age Range

22 | 25 | 28 |
29 |
30 | 31 | 32 | 35 | 36 | 37 |
38 |
39 | -------------------------------------------------------------------------------- /server/users/mockUserData/README.md: -------------------------------------------------------------------------------- 1 | Use instructions: 2 | first make sure you have mongodb-datasets and the mongodb client installed 3 | From the mongodb-datasets readme: 4 | "To use the `mongodb-datasets` command, install mongodb-datasets globally: 5 | $ npm install -g mongodb-datasets" 6 | To use the Javascript API, just run: 7 | npm install 8 | And mongodb-datasets will be installed via package.json 9 | 10 | cd into this file's directory and run the following command 11 | mongodb-datasets mockUser.schema.json -n 10 -o mockData.json --pretty 12 | -Note that running this command will overwrite all the previous data in the mock-data.json file 13 | The command breaks down like so: 14 | mongodb-datasets 15 | (invokes the mongodb-datasets package) 16 | mock-user-schema.json 17 | (tells the package what schema to follow when creating the fake data points) 18 | -n 10 19 | (specifies the number of fake data points to make) 20 | -o mock-data.json 21 | (tells the package where to write the fake data to, will create the file if it doesn't already exist) 22 | --pretty 23 | (optional, but it will write the data in a human readable format. ie similar to how this schema currently looks) 24 | 25 | Now you have all the mock data ready to insert into the database, but how do you get it in there? 26 | Great question, I'm glad you asked. 27 | Now all you need to do is run: 28 | node mockApp.js 29 | you should then see a log informing you of how many documents you just inserted into the database -------------------------------------------------------------------------------- /www/chats/chats.js: -------------------------------------------------------------------------------- 1 | angular.module('chats.controllers', []) 2 | 3 | 4 | .controller('ChatsCtrl', [ 5 | '$scope', 6 | 'User', 7 | 'Chats', 8 | 'MatchesFactory', 9 | 'userSession', 10 | '$stateParams', 11 | 12 | function($scope, User, Chats, MatchesFactory, userSession, $stateParams) { 13 | 14 | var matchId = $stateParams.matchId; 15 | console.log('match id in chats controller - ', $stateParams); 16 | console.log("ChatCTRL: USER ", User) 17 | $scope.match = MatchesFactory.get(matchId); 18 | 19 | if($scope.match.matched[User._id] && $scope.match.matched[User._id] !== 'isMatch') { 20 | var chatURL = $scope.match.matched[User._id] 21 | } else if (User.matched[$scope.match._id] && User.matched[$scope.match._id] !== 'isMatch') { 22 | var chatURL = User.matched[$scope.match._id]; 23 | } else { 24 | console.log('going to set chat url') 25 | chatURL = userSession.user.id + matchId; 26 | MatchesFactory.updateChatURL(matchId,User._id,chatURL, function(matchWithChat) { 27 | MatchesFactory.updateMatchedUsers(matchWithChat) 28 | .then(function(res){ 29 | console.log('response made it back to chatURL!'); 30 | }) 31 | }); 32 | Chats.setUserAccess(chatURL,userSession.user.id,matchId); 33 | } 34 | 35 | $scope.chats = Chats.setChats(chatURL); 36 | console.log('scope chats ', $scope.chats); 37 | 38 | $scope.remove = function(chat) { 39 | Chats.remove(chat); 40 | }; 41 | 42 | $scope.add = function(message){ 43 | Chats.add($scope.chats,User.name,message,User.fbid); 44 | }; 45 | 46 | }]); 47 | -------------------------------------------------------------------------------- /server/users/mockUserData/mockUser.schema.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "fbid": "{{counter()}}", 4 | "name": "{{chance.name()}}", 5 | "face": "{{faker.Image.avatar()}}", 6 | "email": "{{chance.email()}}", 7 | "profile": { 8 | "gender": "String", 9 | "age": "Number", 10 | "keywords": "{{ faker.random.catch_phrase_noun() }}" 11 | }, 12 | "location": { 13 | "host": "{{chance.bool()}}", 14 | "myPlace": { 15 | "rent": "{{chance.integer({min:500, max:3000})}}", 16 | "genders": "{{chance.gender()}}", 17 | "openRooms": "{{chance.integer({min:1, max:5})}}", 18 | "roomType": "{{util.sample(['Shared', 'Private'])}}", 19 | "occupants": "{{chance.integer({min:1, max:5})}}", 20 | "zipCode": "{{faker.Address.zipCode()}}", 21 | "city": "{{faker.Address.city()}}", 22 | "state": "{{faker.Address.usState()}}", 23 | "latitude": "{{faker.Address.latitude()}}", 24 | "longitude": "{{faker.Address.longitude()}}" 25 | }, 26 | "desiredPlace": { 27 | "rent": "{{chance.integer({min:500, max:3000})}}", 28 | "zipCode": "{{faker.Address.zipCode()}}", 29 | "radius": "{{chance.integer({min:1, max:25})}}", 30 | "roomType": "{{util.sample(['Shared', 'Private'])}}", 31 | "city": "{{faker.Address.city()}}", 32 | "state": "{{faker.Address.usState()}}", 33 | "latitude": "{{faker.Address.latitude()}}", 34 | "longitude": "{{faker.Address.longitude()}}" 35 | } 36 | }, 37 | "roommatePreferences": { 38 | "gender": "{{chance.gender()}}", 39 | "ageMin": "{{chance.integer({min:18, max:25})}}", 40 | "ageMax": "{{chance.integer({min:25, max:45})}}" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /server/users/userRoutes.js: -------------------------------------------------------------------------------- 1 | var userController = require('./userController.js'); 2 | 3 | module.exports = function(app) { 4 | 5 | app.route('/:id/location') 6 | .get(userController.getCandidates) 7 | 8 | app.route('/:id') 9 | .post(userController.addOrFindCurrentUser) 10 | .delete(userController.deleteAccount) 11 | 12 | app.route('/:id/location') 13 | .put(userController.updateLocation) 14 | 15 | app.route('/:id/roommatePreferences') 16 | .put(userController.updateRoommatePreferences) 17 | 18 | app.route('/:id/profile') 19 | .put(userController.updateProfile) 20 | 21 | app.route('/:id/matches') 22 | .get(userController.getMatches) 23 | .put(userController.updateUserMatches) 24 | 25 | app.route('/:id/:location') 26 | .get(userController.getCandidates) 27 | 28 | app.route('/auth/:id/') 29 | .post(userController.auth) 30 | 31 | } 32 | ////////////////FOR MOCK DATA/////////////// 33 | // .put(function(req, res) { 34 | // findOrCreate( 35 | // {fbid: req.params.id}, 36 | // {$set: { 37 | // candidate: req.body 38 | // // fbid: req.params.id, 39 | // // name: req.body.candidate.name, 40 | // // profile: req.body.candidate.profile, 41 | // // location: req.body.candidate.location, 42 | // // roommatePreferences: req.body.candidate.roommatePreferences, 43 | // // liked: req.body.candidate.liked 44 | // } 45 | // }, 46 | // {upsert: true, new: true} 47 | // ) 48 | // .then(function(user) { 49 | // res.send(user); 50 | // }); 51 | // }) 52 | 53 | 54 | -------------------------------------------------------------------------------- /www/swipe/swipe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 |
9 | 10 | 11 | 12 |
13 | 14 |
15 |

{{ candidate.name }}

16 |
17 |
18 | 19 |
20 |
21 | 22 | 23 |
24 |
25 |
26 | 27 |
28 |
29 | 30 |
31 | 32 |

You don't have any more candidates, please come back later!

33 |

Please set your search preferences!

34 |
35 |
36 | 37 |
38 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [RooMe](http://roome.azurewebsites.net/) 2 | 3 | > Imagine you've just got a new job and are primed to move to a new city. You're feeling crazy excited, nervous, curious, and all the other emotions that accompany moments of big change. The last thing you want to worry about at this point, is finding a place to live and someone to split rent with.

4 | With RooMe, you can toss all those cares away. Just sign-up, start swiping, and you'll have a new roommate before you finish breakfast.

5 | RooMe, it's Tinder for Roomies! 6 | 7 | ## [The Team](http://roome.azurewebsites.net/#team) 8 | 9 | - __Product Owner__: David Blanchard 10 | - __Scrum Master__: Jack Peterson 11 | - __Lead Developer__: Daniel Miller 12 | 13 | ## Table of Contents 14 | 15 | 1. [Usage](#Usage) 16 | 1. [Requirements](#requirements) 17 | 1. [Development](#development) 18 | 1. [Installing Dependencies](#installing-dependencies) 19 | 1. [Team](#team) 20 | 1. [Contributing](#contributing) 21 | 22 | ## Usage 23 | 24 | > In order to run this locally, you will need to have a MongoDB instance running and then you will need to run gulp serve from the root directory. Then simply navigate to localhost:8888 in your browser, and voila! 25 | 26 | ## Requirements 27 | 28 | - Node 29 | - Ionic 30 | - Steroids (for testing on a device) 31 | 32 | ## Development 33 | 34 | ### Installing Dependencies 35 | 36 | From within the root directory: 37 | 38 | ```sh 39 | sudo npm install -g bower 40 | npm install 41 | bower install 42 | ``` 43 | 44 | ### Roadmap 45 | 46 | View the project roadmap [here](LINK_TO_PROJECT_ISSUES) 47 | 48 | 49 | ## Contributing 50 | 51 | See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines. 52 | 53 | -------------------------------------------------------------------------------- /www/map/map.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 |
7 | 10 | 11 |
12 | 13 |
14 | 17 | 18 |
19 | 20 | 21 | Search radius: {{input.radius}} miles 22 |
23 | 24 | 25 |
26 |
27 | 28 | 29 | Toggle Radius 30 | 36 | 37 | 38 | 39 | 42 | 43 |
44 |
45 | -------------------------------------------------------------------------------- /www/lib/ionic/scss/_spinner.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Spinners 3 | * -------------------------------------------------- 4 | */ 5 | 6 | .spinner { 7 | svg { 8 | width: $spinner-width; 9 | height: $spinner-height; 10 | } 11 | 12 | stroke: $spinner-default-stroke; 13 | fill: $spinner-default-fill; 14 | 15 | &.spinner-light { 16 | stroke: $spinner-light-stroke; 17 | fill: $spinner-light-fill; 18 | } 19 | &.spinner-stable { 20 | stroke: $spinner-stable-stroke; 21 | fill: $spinner-stable-fill; 22 | } 23 | &.spinner-positive { 24 | stroke: $spinner-positive-stroke; 25 | fill: $spinner-positive-fill; 26 | } 27 | &.spinner-calm { 28 | stroke: $spinner-calm-stroke; 29 | fill: $spinner-calm-fill; 30 | } 31 | &.spinner-balanced { 32 | stroke: $spinner-balanced-stroke; 33 | fill: $spinner-balanced-fill; 34 | } 35 | &.spinner-assertive { 36 | stroke: $spinner-assertive-stroke; 37 | fill: $spinner-assertive-fill; 38 | } 39 | &.spinner-energized { 40 | stroke: $spinner-energized-stroke; 41 | fill: $spinner-energized-fill; 42 | } 43 | &.spinner-royal { 44 | stroke: $spinner-royal-stroke; 45 | fill: $spinner-royal-fill; 46 | } 47 | &.spinner-dark { 48 | stroke: $spinner-dark-stroke; 49 | fill: $spinner-dark-fill; 50 | } 51 | } 52 | 53 | .spinner-android { 54 | stroke: #4b8bf4; 55 | } 56 | 57 | .spinner-ios, 58 | .spinner-ios-small { 59 | stroke: #69717d; 60 | } 61 | 62 | .spinner-spiral { 63 | .stop1 { 64 | stop-color: $spinner-light-fill; 65 | stop-opacity: 0; 66 | } 67 | 68 | &.spinner-light { 69 | .stop1 { 70 | stop-color: $spinner-default-fill; 71 | } 72 | .stop2 { 73 | stop-color: $spinner-light-fill; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /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/profiles/profile.directive.js: -------------------------------------------------------------------------------- 1 | angular.module('profile.directives', []) 2 | 3 | .directive('profileBox', [ 4 | '$compile', 5 | '$http', 6 | '$templateCache', 7 | 8 | function($compile, $http, $templateCache) { 9 | 10 | var getTemplate = function(boxType) { 11 | console.log("box type in getTemplate ", boxType) 12 | var templateLoader, 13 | baseUrl = './profiles/directives/', 14 | templateMap = { 15 | swipeTop: 'candidate-top-box.html', 16 | matchesBottom: 'contact-info.html', 17 | userBottom: 'edit-profile.html', 18 | swipeBottom: 'like-dislike.html', 19 | matchesTop: 'match-top-box.html', 20 | userTop: 'user-top-box.html', 21 | }; 22 | 23 | var templateUrl = baseUrl + templateMap[boxType]; 24 | templateLoader = $http.get(templateUrl, {cache: $templateCache}); 25 | return templateLoader; 26 | 27 | } 28 | 29 | var linker = function(scope, element, attrs) { 30 | console.log("scope.boxtype.type", scope.boxtype.type); 31 | var loader = getTemplate(scope.boxtype.type); 32 | 33 | var promise = loader.success(function(html) { 34 | element.html(html); 35 | }).then(function (response) { 36 | console.log("element ", element) 37 | element.replaceWith($compile(element.html())(scope)); 38 | console.log("element 2 ", element) 39 | }); 40 | } 41 | 42 | return { 43 | transclude: true, 44 | restrict: 'E', 45 | scope: { 46 | boxtype:'=', 47 | boxaction:"&", 48 | boxprofile:'=' 49 | }, 50 | link: linker, 51 | }; 52 | 53 | }]); 54 | -------------------------------------------------------------------------------- /www/lib/ionic/scss/_platform.scss: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Platform 4 | * -------------------------------------------------- 5 | * Platform specific tweaks 6 | */ 7 | 8 | .platform-ios.platform-cordova { 9 | // iOS 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-header.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/map/map.js: -------------------------------------------------------------------------------- 1 | angular.module('map.controllers', []) 2 | 3 | .controller('MapCtrl', [ 4 | '$scope', 5 | '$ionicLoading', 6 | 'PlaceFactory', 7 | '$state', 8 | 'User', 9 | 'MapFactory', 10 | 11 | function($scope, $ionicLoading, PlaceFactory, $state, User, MapFactory){ 12 | 13 | // set the searchLocation to the User.location if it's already been retrieved, otherwise, get it from the database 14 | $scope.searchLocation = User.location || PlaceFactory.all(); 15 | 16 | $scope.input = MapFactory.input; 17 | 18 | // Save the user's location settings to the database and redirect to the account-place view 19 | $scope.saveLocation = function(){ 20 | User.location = MapFactory.saveLocation(User); 21 | console.log('User after saving location ', User); 22 | $state.go('tab.account-place'); 23 | }; 24 | 25 | // Initializes the map, centering it on the user's search location 26 | // if the user has yet to set a search location, it defaults to Delta Upsilon, Berkeley, Ca 27 | $scope.initialize = function() { 28 | MapFactory.initialize($scope.searchLocation); 29 | }; 30 | 31 | // Calls the geocode function on the user's search string 32 | $scope.search = function(){ 33 | MapFactory.codeIt($scope.input.address, $scope.input.radius); 34 | }; 35 | 36 | // places a search radius cenetered on the currently placed marker 37 | $scope.placeCircle = function(toggled){ 38 | // check to make sure the radius option is toggled to true 39 | if(toggled){ 40 | MapFactory.placeCircle($scope.input.radius); 41 | } 42 | }; 43 | 44 | // hides/shows the search radius 45 | $scope.toggleRadius = function(){ 46 | MapFactory.toggleRadius($scope.input.radius, $scope.input.toggleRadius); 47 | }; 48 | 49 | // invokes the initialize function once the window has loaded 50 | google.maps.event.addDomListener(window, 'load', $scope.initialize()); 51 | 52 | }]); 53 | -------------------------------------------------------------------------------- /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 | var karma = require('karma').server; 10 | var nodemon = require('gulp-nodemon'); 11 | 12 | //add condition for Travis 13 | var isTravis = process.env.TRAVIS || false; 14 | 15 | var paths = { 16 | sass: ['./scss/**/*.scss'] 17 | }; 18 | 19 | gulp.task('test', function(done) { 20 | console.log('isTravis ', isTravis); 21 | karma.start({ 22 | configFile: __dirname + '/tests/karma.conf.js', 23 | singleRun: isTravis 24 | }, done) 25 | 26 | }); 27 | 28 | gulp.task('default', ['sass']); 29 | 30 | gulp.task('sass', function(done) { 31 | gulp.src('./scss/ionic.app.scss') 32 | .pipe(sass()) 33 | .pipe(gulp.dest('./www/css/')) 34 | .pipe(minifyCss({ 35 | keepSpecialComments: 0 36 | })) 37 | .pipe(rename({ extname: '.min.css' })) 38 | .pipe(gulp.dest('./www/css/')) 39 | .on('end', done); 40 | }); 41 | 42 | gulp.task('watch', function() { 43 | gulp.watch(paths.sass, ['sass']); 44 | }); 45 | 46 | gulp.task('install', ['git-check'], function() { 47 | return bower.commands.install() 48 | .on('log', function(data) { 49 | gutil.log('bower', gutil.colors.cyan(data.id), data.message); 50 | }); 51 | }); 52 | 53 | gulp.task('git-check', function(done) { 54 | if (!sh.which('git')) { 55 | console.log( 56 | ' ' + gutil.colors.red('Git is not installed.'), 57 | '\n Git, the version control system, is required to download Ionic.', 58 | '\n Download git here:', gutil.colors.cyan('http://git-scm.com/downloads') + '.', 59 | '\n Once git is installed, run \'' + gutil.colors.cyan('gulp install') + '\' again.' 60 | ); 61 | process.exit(1); 62 | } 63 | done(); 64 | }); 65 | 66 | gulp.task('serve', function() { 67 | nodemon({script: 'index.js'}) 68 | }); 69 | -------------------------------------------------------------------------------- /www/swipe/swipe.js: -------------------------------------------------------------------------------- 1 | angular.module('swipe.controllers', []) 2 | 3 | .controller('SwipeController', [ 4 | '$scope', 5 | '$timeout', 6 | 'CandidatesFactory', 7 | 'MatchesFactory', 8 | 'SkippedFactory', 9 | 'User', 10 | 11 | function($scope, $timeout, CandidatesFactory, MatchesFactory, SkippedFactory, User) { 12 | 13 | $scope.candidates = CandidatesFactory.all(); 14 | console.log('swipectrl: candidates ', $scope.candidates); 15 | $scope.user = User; 16 | 17 | $scope.currentCandidate = angular.copy($scope.candidates[0]); 18 | console.log('currentCandidate in swipe controller ', $scope.currentCandidate); 19 | 20 | $scope.$watch( 21 | function () { 22 | return CandidatesFactory.getFirst(); 23 | }, 24 | function (firstCandidate) { 25 | $scope.currentCandidate = firstCandidate; 26 | } 27 | ); 28 | 29 | $scope.candidateSwipe = function (match, User){ 30 | console.log("Ctrl: candidateSwipe ", $scope.currentCandidate) 31 | if (match) { 32 | MatchesFactory.add($scope.currentCandidate, $scope.user, function(userMatch){ 33 | console.log('MatchCtrl: Res from Matcfact.Add: userMatch ', userMatch); 34 | $scope.user = User = userMatch[0]; 35 | var candidate = userMatch[1]; 36 | 37 | MatchesFactory.saveAllMatches(User) 38 | .then(function(res){ 39 | console.log('SwipeCtrl: res: current user res from saveAllMatches', res); 40 | $scope.user = User = res; 41 | }); 42 | MatchesFactory.updateMatchedUsers(candidate) 43 | .then(function(res) { 44 | console.log('SwipeCtrl: res: last candidate from updateMatchedUsers ', res); 45 | }) 46 | }); 47 | } 48 | else { 49 | SkippedFactory.add($scope.currentCandidate); 50 | } 51 | 52 | $scope.currentCandidate.hide = true; 53 | CandidatesFactory.removeFirst(); 54 | $scope.candidates = CandidatesFactory.all(); 55 | $scope.currentCandidate = angular.copy($scope.candidates[0]); 56 | console.log("new candidate ", $scope.currentCandidate) 57 | }; 58 | 59 | }]); 60 | -------------------------------------------------------------------------------- /www/account/userProfile/userProfile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

{{username}}'s Profile Settings

7 |
8 | 9 |

About Me

10 | 11 | 20 | 28 | 29 |

Custom Icons

30 |
31 |

Start typing words that represent you to select icons

32 | 35 | 36 |
37 |
38 | 39 | 40 | 43 | 44 | 45 |
46 |
47 | -------------------------------------------------------------------------------- /tests/data/SkippedFactory.tests.js: -------------------------------------------------------------------------------- 1 | describe('Factory: SkippedFactory: ', function() { 2 | // var scope, $login, controller; 3 | var scope, SkippedFactory; 4 | 5 | var testSkipped; 6 | //load controller's module and other necessary modules 7 | beforeEach(module('data'/*,'ui.router'*/)); 8 | 9 | beforeEach(inject(function($rootScope, $injector) { 10 | scope = $rootScope.$new(); 11 | SkippedFactory = $injector.get('SkippedFactory'); 12 | testSkipped = [ 13 | {id:123,name:"Test Name"}, 14 | {id:456,name:"Hello World"}, 15 | {id:789,name:"Another Test"} 16 | ]; 17 | })); 18 | 19 | //tests start here 20 | 21 | describe('initialize', function() { 22 | 23 | it("Should initialize skipped to the passed in array", function() { 24 | expect(SkippedFactory.all().length).toEqual(0); 25 | SkippedFactory.initialize(testSkipped); 26 | expect(SkippedFactory.all().length).toEqual(3); 27 | }); 28 | 29 | }); 30 | 31 | describe('all', function() { 32 | 33 | it("Should return an empty array before being initialized", function() { 34 | expect(SkippedFactory.all().length).toEqual(0); 35 | }); 36 | 37 | it("Should return all added objects array after initialization and adding", function() { 38 | SkippedFactory.initialize(testSkipped); 39 | SkippedFactory.add({id:0,name:"Zero",likeCurrentUser:true}); 40 | expect(SkippedFactory.all().length).toEqual(4); 41 | }); 42 | 43 | }); 44 | 45 | describe('add', function() { 46 | 47 | it("Should add a match to MatchesFactory", function() { 48 | SkippedFactory.initialize(testSkipped); 49 | SkippedFactory.add({id:0,name:"Zero",likeCurrentUser:true}); 50 | expect(SkippedFactory.all().length).toEqual(4); 51 | SkippedFactory.add({id:1,name:"One",likeCurrentUser:true}); 52 | SkippedFactory.add({id:2,name:"Two",likeCurrentUser:true}); 53 | expect(SkippedFactory.all().length).toEqual(6); 54 | }); 55 | 56 | }); 57 | 58 | describe('getFirst', function() { 59 | 60 | it("Should return the first user in SkippedFactory", function() { 61 | SkippedFactory.initialize(testSkipped); 62 | var getName = SkippedFactory.getFirst().name; 63 | expect(getName).toEqual("Test Name"); 64 | }); 65 | 66 | }); 67 | 68 | }); 69 | -------------------------------------------------------------------------------- /tests/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Wed Apr 01 2015 19:35:59 GMT-0400 (EDT) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | frameworks: ['jasmine'], 14 | 15 | 16 | // list of files / patterns to load in the browser 17 | files: [ 18 | '../www/lib/angular/angular.js', 19 | '../www/lib/firebase/firebase.js', 20 | '../www/lib/firebase-simple-login/firebase-simple-login.js', 21 | '../www/lib/angularfire/dist/angularfire.js', 22 | '../www/lib/angular-ui-router/**/*.js', 23 | '../www/lib/angular-resource/angular-resource.min.js', 24 | '../www/js/*.js', 25 | '../www/login/*.js', 26 | '../www/matches/*.js', 27 | '../www/profiles/*.js', 28 | '../www/swipe/*.js', 29 | '../www/app.js', 30 | '../www/data.js', 31 | '../node_modules/angular-mocks/angular-mocks.js', 32 | '**/*tests.js' 33 | ], 34 | 35 | 36 | // list of files to exclude 37 | exclude: [ 38 | ], 39 | 40 | 41 | // preprocess matching files before serving them to the browser 42 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 43 | preprocessors: { 44 | }, 45 | 46 | 47 | // test results reporter to use 48 | // possible values: 'dots', 'progress' 49 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 50 | reporters: ['progress'], 51 | 52 | 53 | // web server port 54 | port: 9876, 55 | 56 | 57 | // enable / disable colors in the output (reporters and logs) 58 | colors: true, 59 | 60 | 61 | // level of logging 62 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 63 | logLevel: config.LOG_INFO, 64 | 65 | 66 | // enable / disable watching file and executing tests whenever any file changes 67 | autoWatch: true, 68 | 69 | 70 | // start these browsers 71 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 72 | browsers: ['PhantomJS'], 73 | 74 | 75 | // Continuous Integration mode 76 | // if true, Karma captures browsers, runs the tests and exits 77 | singleRun: false 78 | }); 79 | }; 80 | -------------------------------------------------------------------------------- /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 | input, 54 | textarea { 55 | width: 100%; 56 | } 57 | } 58 | 59 | .popup-head { 60 | padding: 15px 10px; 61 | border-bottom: 1px solid #eee; 62 | text-align: center; 63 | } 64 | .popup-title { 65 | margin: 0; 66 | padding: 0; 67 | font-size: 15px; 68 | } 69 | .popup-sub-title { 70 | margin: 5px 0 0 0; 71 | padding: 0; 72 | font-weight: normal; 73 | font-size: 11px; 74 | } 75 | .popup-body { 76 | padding: 10px; 77 | overflow: auto; 78 | } 79 | 80 | .popup-buttons { 81 | @include display-flex(); 82 | @include flex-direction(row); 83 | padding: 10px; 84 | min-height: $popup-button-min-height + 20; 85 | 86 | .button { 87 | @include flex(1); 88 | display: block; 89 | min-height: $popup-button-min-height; 90 | border-radius: $popup-button-border-radius; 91 | line-height: $popup-button-line-height; 92 | 93 | margin-right: 5px; 94 | &:last-child { 95 | margin-right: 0px; 96 | } 97 | } 98 | } 99 | 100 | .popup-open { 101 | pointer-events: none; 102 | 103 | &.modal-open .modal { 104 | pointer-events: none; 105 | } 106 | 107 | .popup-backdrop, .popup { 108 | pointer-events: auto; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /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 | .modal-backdrop-bg { 10 | position: fixed; 11 | top: 0; 12 | left: 0; 13 | z-index: $z-index-modal; 14 | width: 100%; 15 | height: 100%; 16 | } 17 | 18 | .modal-backdrop-bg { 19 | pointer-events: none; 20 | } 21 | 22 | .modal { 23 | display: block; 24 | position: absolute; 25 | top: 0; 26 | z-index: $z-index-modal; 27 | overflow: hidden; 28 | min-height: 100%; 29 | width: 100%; 30 | background-color: $modal-bg-color; 31 | } 32 | 33 | @media (min-width: $modal-inset-mode-break-point) { 34 | // inset mode is when the modal doesn't fill the entire 35 | // display but instead is centered within a large display 36 | .modal { 37 | top: $modal-inset-mode-top; 38 | right: $modal-inset-mode-right; 39 | bottom: $modal-inset-mode-bottom; 40 | left: $modal-inset-mode-left; 41 | overflow: visible; 42 | min-height: $modal-inset-mode-min-height; 43 | width: (100% - $modal-inset-mode-left - $modal-inset-mode-right); 44 | } 45 | 46 | .modal.ng-leave-active { 47 | bottom: 0; 48 | } 49 | 50 | // remove ios header padding from inset header 51 | .platform-ios.platform-cordova .modal-wrapper .modal { 52 | .bar-header:not(.bar-subheader) { 53 | height: $bar-height; 54 | > * { 55 | margin-top: 0; 56 | } 57 | } 58 | .tabs-top > .tabs, 59 | .tabs.tabs-top { 60 | top: $bar-height; 61 | } 62 | .has-header, 63 | .bar-subheader { 64 | top: $bar-height; 65 | } 66 | .has-subheader { 67 | top: $bar-height + $bar-subheader-height; 68 | } 69 | .has-tabs-top { 70 | top: $bar-height + $tabs-height; 71 | } 72 | .has-header.has-subheader.has-tabs-top { 73 | top: $bar-height + $bar-subheader-height + $tabs-height; 74 | } 75 | } 76 | 77 | .modal-backdrop-bg { 78 | @include transition(opacity 300ms ease-in-out); 79 | background-color: $modal-backdrop-bg-active; 80 | opacity: 0; 81 | } 82 | 83 | .active .modal-backdrop-bg { 84 | opacity: 0.5; 85 | } 86 | } 87 | 88 | // disable clicks on all but the modal 89 | .modal-open { 90 | pointer-events: none; 91 | 92 | .modal, 93 | .modal-backdrop { 94 | pointer-events: auto; 95 | } 96 | // prevent clicks on modal when loading overlay is active though 97 | &.loading-active { 98 | .modal, 99 | .modal-backdrop { 100 | pointer-events: none; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /www/profiles/profile.js: -------------------------------------------------------------------------------- 1 | angular.module('profile.controllers', []) 2 | 3 | .controller('ProfileCtrl', [ 4 | '$scope', 5 | '$stateParams', 6 | '$state', 7 | '$ionicHistory', 8 | 'User', 9 | 'CandidatesFactory', 10 | 'MatchesFactory', 11 | 'SkippedFactory', 12 | 'PlaceFactory', 13 | 'RoommateFactory', 14 | 'ProfileFactory', 15 | 16 | function($scope, $stateParams, $state, $ionicHistory, User, CandidatesFactory, MatchesFactory, SkippedFactory, PlaceFactory, RoommateFactory, ProfileFactory) { 17 | 18 | $scope.User = User; 19 | $scope.fbId = User.fbid; 20 | 21 | $scope.candidates = CandidatesFactory.all(); 22 | $scope.currentCandidate = angular.copy($scope.candidates[0]); 23 | 24 | var profileType = $stateParams.type; 25 | 26 | switch(profileType){ 27 | case 'swipe': 28 | $scope.profile = CandidatesFactory.getFirst(); 29 | break; 30 | case 'matches': 31 | console.log('state.params ', $stateParams); 32 | $scope.profile = MatchesFactory.get($stateParams.id); 33 | console.log('scope profile in matches profile ', $scope.profile); 34 | break; 35 | default: 36 | profileType = 'user' 37 | $scope.profile = User; 38 | break; 39 | } 40 | 41 | $scope.myGoBack = function() { 42 | $ionicHistory.goBack(); 43 | }; 44 | 45 | if ($scope.profile.profile) { 46 | $scope.icons = $scope.profile.profile.icons; 47 | } 48 | 49 | $scope.topBoxType = {'type':profileType+'Top'}; 50 | $scope.bottomBoxType = {'type':profileType+'Bottom'}; 51 | 52 | $scope.profile.type = profileType; 53 | $scope.profile.match = true; 54 | 55 | $scope.candidateSwipe = function (match){ 56 | console.log('!!!!!'); 57 | CandidatesFactory.removeFirst(); 58 | if (match) { 59 | MatchesFactory.add($scope.currentCandidate, $scope.User, function(userMatch){ 60 | console.log('userMatch ', userMatch); 61 | User = userMatch[0]; 62 | var candidate = userMatch[1]; 63 | MatchesFactory.saveAllMatches(User) 64 | .then(function(res){ 65 | console.log('SwipeCtrl: res: current user res from saveAllMatches', res); 66 | $scope.user = User = res; 67 | }); 68 | MatchesFactory.updateMatchedUsers(candidate) 69 | .then(function(res) { 70 | console.log('SwipeCtrl: res: last candidate from updateMatchedUsers ', res); 71 | }) 72 | }); 73 | } 74 | else { 75 | SkippedFactory.add($scope.currentCandidate); 76 | } 77 | $scope.currentCandidate = angular.copy($scope.candidates[0]); 78 | $state.go('tab.swipe'); 79 | 80 | }; 81 | 82 | }]); 83 | -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | RooMe 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /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 .scroll{ 83 | &.overscroll{ 84 | position:fixed; 85 | } 86 | -webkit-overflow-scrolling:touch; 87 | width:100%; 88 | } 89 | 90 | @-webkit-keyframes refresh-spin { 91 | 0% { -webkit-transform: translate3d(0,0,0) rotate(0); } 92 | 100% { -webkit-transform: translate3d(0,0,0) rotate(180deg); } 93 | } 94 | 95 | @keyframes refresh-spin { 96 | 0% { transform: translate3d(0,0,0) rotate(0); } 97 | 100% { transform: translate3d(0,0,0) rotate(180deg); } 98 | } 99 | 100 | @-webkit-keyframes refresh-spin-back { 101 | 0% { -webkit-transform: translate3d(0,0,0) rotate(180deg); } 102 | 100% { -webkit-transform: translate3d(0,0,0) rotate(0); } 103 | } 104 | 105 | @keyframes refresh-spin-back { 106 | 0% { transform: translate3d(0,0,0) rotate(180deg); } 107 | 100% { transform: translate3d(0,0,0) rotate(0); } 108 | } 109 | -------------------------------------------------------------------------------- /tests/data/RoommateFactory.tests.js: -------------------------------------------------------------------------------- 1 | describe('Factory: RoommateFactory', function() { 2 | // var scope, $login, controller; 3 | var scope, RoommateFactory, httpBackend; 4 | 5 | var testRoommatePreferences; 6 | //load controller's module and other necessary modules 7 | beforeEach(module('data'/*,'ui.router'*/)); 8 | 9 | beforeEach(inject(function(_RoommateFactory_, $httpBackend) { 10 | RoommateFactory = _RoommateFactory_; 11 | httpBackend = $httpBackend; 12 | 13 | testUser = { 14 | roommatePreferences: { 15 | gender: 'female', 16 | ageMin: 22, 17 | ageMax: 26 18 | } 19 | }; 20 | })); 21 | 22 | afterEach(function() { 23 | httpBackend.verifyNoOutstandingExpectation(); 24 | httpBackend.verifyNoOutstandingRequest(); 25 | }); 26 | 27 | //tests start here 28 | 29 | it('Should initialize roommatePreferences to the empty default', function(){ 30 | var roommatePreferences = RoommateFactory.all(); 31 | expect(roommatePreferences.gender).toBeNull(); 32 | expect(Object.keys(roommatePreferences).length).toEqual(3); 33 | }); 34 | 35 | describe('initialize', function() { 36 | 37 | it("Should initialize roommatePreferences to the passed in object", function() { 38 | httpBackend.whenPUT('http://localhost:8888/user/1234/roommatePreferences?token=5678') 39 | .respond(testUser) 40 | RoommateFactory.initialize(testUser.roommatePreferences, {fbid: 1234, token: 5678}) 41 | .then(function(prefs){ 42 | expect(prefs.ageMin).toEqual(22); 43 | }) 44 | httpBackend.flush(); 45 | }); 46 | 47 | }); 48 | 49 | describe('all', function() { 50 | 51 | it("Should return the roommatePreferences object", function() { 52 | var roommatePreferences = RoommateFactory.all(); 53 | expect(Object.keys(roommatePreferences).length).toEqual(3); 54 | }); 55 | 56 | }); 57 | 58 | describe('update', function() { 59 | 60 | it("Should update a roommatePreferences property", function() { 61 | RoommateFactory.update('gender','any'); 62 | RoommateFactory.update('ageMin',24); 63 | RoommateFactory.update('ageMax',28); 64 | var roommatePreferences = RoommateFactory.all(); 65 | expect(roommatePreferences.gender).toEqual('any'); 66 | expect(roommatePreferences.ageMin).toEqual(24); 67 | expect(roommatePreferences.ageMax).toEqual(28); 68 | }); 69 | 70 | }); 71 | 72 | describe('getProperty', function() { 73 | 74 | it("Should return the specified property", function() { 75 | httpBackend.whenPUT('http://localhost:8888/user/1234/roommatePreferences?token=5678') 76 | .respond(testUser) 77 | RoommateFactory.initialize(testUser.roommatePreferences, {fbid: 1234, token:5678}) 78 | .then(function(prefs){ 79 | expect(RoommateFactory.getProperty('gender')).toEqual('female'); 80 | expect(RoommateFactory.getProperty('ageMin')).toEqual(22); 81 | expect(RoommateFactory.getProperty('ageMax')).toEqual(26); 82 | }) 83 | httpBackend.flush(); 84 | }); 85 | 86 | }); 87 | 88 | }); 89 | -------------------------------------------------------------------------------- /hooks/README.md: -------------------------------------------------------------------------------- 1 | 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 | .range input{ 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 | -------------------------------------------------------------------------------- /tests/data/ProfileFactory.tests.js: -------------------------------------------------------------------------------- 1 | describe('Factory: Profile', function() { 2 | // var scope, $login, controller; 3 | var scope, ProfileFactory, httpBackend; 4 | 5 | var testProfile; 6 | //load controller's module and other necessary modules 7 | beforeEach(module('data'/*,'ui.router'*/)); 8 | 9 | beforeEach(inject(function($httpBackend, _ProfileFactory_) { 10 | ProfileFactory = _ProfileFactory_; 11 | httpBackend = $httpBackend; 12 | testUser = { 13 | profile: { 14 | gender: 'female', 15 | age: 25, 16 | keywords: ['Running','Coffee','Student','Vegetarian'] 17 | } 18 | }; 19 | })); 20 | 21 | afterEach(function() { 22 | httpBackend.verifyNoOutstandingExpectation(); 23 | httpBackend.verifyNoOutstandingRequest(); 24 | }); 25 | 26 | //tests start here 27 | describe('ProfileFactory', function() { 28 | 29 | it('Should initialize profile to empty default', function(){ 30 | var profile = ProfileFactory.all(); 31 | expect(profile.gender).toBeNull(); 32 | expect(profile.keywords.length).toEqual(5); 33 | }); 34 | 35 | describe('initialize', function() { 36 | 37 | it("Should initialize profile to the passed in object", function() { 38 | httpBackend.whenPUT('http://localhost:8888/user/1234/profile?token=5678') 39 | .respond(testUser) 40 | ProfileFactory.initialize(testUser.profile, {fbid: 1234, token: 5678}) 41 | .then(function(newProfile) { 42 | console.log("new location ", newProfile) 43 | expect(newProfile.age).toEqual(25); 44 | }); 45 | httpBackend.flush(); 46 | }); 47 | 48 | }); 49 | 50 | describe('all', function() { 51 | 52 | it("Should return the profile object", function() { 53 | httpBackend.whenPUT('http://localhost:8888/user/1234/profile?token=5678') 54 | .respond(testUser) 55 | ProfileFactory.initialize(testUser.profile, {fbid: 1234, token: 5678}) 56 | .then(function(newProfile) { 57 | expect(ProfileFactory.all().gender).toEqual('female'); 58 | }); 59 | httpBackend.flush(); 60 | }); 61 | 62 | }); 63 | 64 | describe('update', function() { 65 | 66 | it("Should update a profile property", function() { 67 | ProfileFactory.update('age',35); 68 | ProfileFactory.update('keywords',['Jogging','tea','teacher']); 69 | var newPlace = { 70 | peopleCount: 4, 71 | genders: 'women', 72 | rent: 1200, 73 | zipCode: 12345 74 | }; 75 | ProfileFactory.update('myPlace',newPlace); 76 | var profile = ProfileFactory.all(); 77 | expect(profile.age).toEqual(35); 78 | expect(profile.keywords[1]).toEqual('tea'); 79 | expect(profile.myPlace.peopleCount).toEqual(4); 80 | expect(profile.myPlace.zipCode).toEqual(12345); 81 | }); 82 | 83 | }); 84 | 85 | describe('getProperty', function() { 86 | 87 | it("Should return the specified property", function() { 88 | httpBackend.whenPUT('http://localhost:8888/user/1234/profile?token=5678') 89 | .respond(testUser) 90 | ProfileFactory.initialize(testUser.profile, {fbid: 1234, token: 5678}) 91 | .then(function(newProfile) { 92 | expect(ProfileFactory.getProperty('age')).toEqual(25); 93 | expect(ProfileFactory.getProperty('gender')).toEqual('female'); 94 | }); 95 | httpBackend.flush(); 96 | }); 97 | 98 | }); 99 | 100 | }); 101 | 102 | }); 103 | -------------------------------------------------------------------------------- /tests/profile/profile-controller.tests.js: -------------------------------------------------------------------------------- 1 | // describe('controllers: profile', function() { 2 | // // var scope, $login, controller; 3 | // var scope, controllerSwipe, CandidatesFactory, MatchesFactory, SkippedFactory, User; 4 | 5 | // //load controller's module and other necessary modules 6 | // //??? Why do we add ui.router here? 7 | // beforeEach(module('profile.controllers' , 'data','ui.router')); 8 | 9 | // beforeEach(inject(function($rootScope, $controller,$injector,User) { 10 | // scope = $rootScope.$new(); 11 | // controller = $controller('ProfileCtrl', { $scope: scope}); 12 | // CandidatesFactory = $injector.get('CandidatesFactory'); 13 | // MatchesFactory = $injector.get('MatchesFactory'); 14 | // SkippedFactory = $injector.get('SkippedFactory'); 15 | // })); 16 | 17 | // afterEach(function(){ 18 | // CandidatesFactory.initialize([]); 19 | // MatchesFactory.initialize([]); 20 | // SkippedFactory.initialize([]); 21 | // }); 22 | 23 | // describe('ProfileController', function() { 24 | 25 | // it('Should initialize scope.candidates to contain all currently loaded candidates', function() { 26 | // expect(scope.candidates.length).toEqual(CandidatesFactory.all().length); 27 | // }); 28 | 29 | // it('Should initialize scope.currentCandidate to be the first candidate in the list of loaded candidates', function() { 30 | // expect(scope.currentCandidate).toEqual(CandidatesFactory.getFirst()); 31 | // }); 32 | 33 | // it('should have a candidateSwipe function', function(){ 34 | // expect(scope.candidateSwipe).toBeDefined; 35 | // }); 36 | 37 | // //How to test $stateParams 38 | 39 | // describe('scope.candidateSwipe', function() { 40 | 41 | // it('Should set currentCandidate.match to true and add the current candidate to MatchesFactory if like is selected', function() { 42 | // expect(MatchesFactory.all().length).toEqual(0); 43 | // expect(scope.currentCandidate.match).not.toBeDefined; 44 | // scope.candidateSwipe(true); 45 | // expect(MatchesFactory.all().length).toEqual(1); 46 | // expect(MatchesFactory.get(scope.currentCandidate.id).id).toEqual(CandidatesFactory.getFirst().id); 47 | // }); 48 | 49 | // it('Should add the current candidate to SkippedFactory if skip is selected', function() { 50 | // expect(SkippedFactory.all().length).toEqual(0); 51 | // expect(scope.currentCandidate.match).not.toBeDefined; 52 | // scope.candidateSwipe(false); 53 | // expect(SkippedFactory.all().length).toEqual(1); 54 | // expect(SkippedFactory.getFirst().id).toEqual(CandidatesFactory.getFirst().id); 55 | // }); 56 | 57 | // it('Should remove candidates from CandidatesFactory and update scope.currentCandidate', function(){ 58 | // var currentCandidateID = CandidatesFactory.getFirst().id; 59 | // expect(scope.currentCandidate.id).toEqual(currentCandidateID); 60 | // scope.candidateSwipe(true); 61 | // expect(CandidatesFactory.getFirst().id).not.toEqual(currentCandidateID); 62 | // expect(scope.currentCandidate.id).toEqual(CandidatesFactory.getFirst().id); 63 | // currentCandidateID = CandidatesFactory.getFirst().id; 64 | // scope.candidateSwipe(false); 65 | // console.log(scope.currentCandidate,CandidatesFactory.getFirst()); 66 | // expect(CandidatesFactory.getFirst().id).not.toEqual(currentCandidateID); 67 | // expect(scope.currentCandidate.id).toEqual(CandidatesFactory.getFirst().id); 68 | 69 | // }); 70 | 71 | // //??? How to test $state.go('tab.swipe') 72 | 73 | // }); 74 | 75 | 76 | // }); 77 | 78 | // }); 79 | -------------------------------------------------------------------------------- /www/lib/ionic/scss/_grid.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Grid 3 | * -------------------------------------------------- 4 | * Using flexbox for the grid, inspired by Philip Walton: 5 | * http://philipwalton.github.io/solved-by-flexbox/demos/grids/ 6 | * By default each .col within a .row will evenly take up 7 | * available width, and the height of each .col with take 8 | * up the height of the tallest .col in the same .row. 9 | */ 10 | 11 | .row { 12 | @include display-flex(); 13 | padding: ($grid-padding-width / 2); 14 | width: 100%; 15 | } 16 | 17 | .row-wrap { 18 | @include flex-wrap(wrap); 19 | } 20 | 21 | .row-no-padding { 22 | padding: 0; 23 | 24 | > .col { 25 | padding: 0; 26 | } 27 | } 28 | 29 | .row + .row { 30 | margin-top: ($grid-padding-width / 2) * -1; 31 | padding-top: 0; 32 | } 33 | 34 | .col { 35 | @include flex(1); 36 | display: block; 37 | padding: ($grid-padding-width / 2); 38 | width: 100%; 39 | } 40 | 41 | 42 | /* Vertically Align Columns */ 43 | /* .row-* vertically aligns every .col in the .row */ 44 | .row-top { 45 | @include align-items(flex-start); 46 | } 47 | .row-bottom { 48 | @include align-items(flex-end); 49 | } 50 | .row-center { 51 | @include align-items(center); 52 | } 53 | .row-stretch { 54 | @include align-items(stretch); 55 | } 56 | .row-baseline { 57 | @include align-items(baseline); 58 | } 59 | 60 | /* .col-* vertically aligns an individual .col */ 61 | .col-top { 62 | @include align-self(flex-start); 63 | } 64 | .col-bottom { 65 | @include align-self(flex-end); 66 | } 67 | .col-center { 68 | @include align-self(center); 69 | } 70 | 71 | /* Column Offsets */ 72 | .col-offset-10 { 73 | margin-left: 10%; 74 | } 75 | .col-offset-20 { 76 | margin-left: 20%; 77 | } 78 | .col-offset-25 { 79 | margin-left: 25%; 80 | } 81 | .col-offset-33, .col-offset-34 { 82 | margin-left: 33.3333%; 83 | } 84 | .col-offset-50 { 85 | margin-left: 50%; 86 | } 87 | .col-offset-66, .col-offset-67 { 88 | margin-left: 66.6666%; 89 | } 90 | .col-offset-75 { 91 | margin-left: 75%; 92 | } 93 | .col-offset-80 { 94 | margin-left: 80%; 95 | } 96 | .col-offset-90 { 97 | margin-left: 90%; 98 | } 99 | 100 | 101 | /* Explicit Column Percent Sizes */ 102 | /* By default each grid column will evenly distribute */ 103 | /* across the grid. However, you can specify individual */ 104 | /* columns to take up a certain size of the available area */ 105 | .col-10 { 106 | @include flex(0, 0, 10%); 107 | max-width: 10%; 108 | } 109 | .col-20 { 110 | @include flex(0, 0, 20%); 111 | max-width: 20%; 112 | } 113 | .col-25 { 114 | @include flex(0, 0, 25%); 115 | max-width: 25%; 116 | } 117 | .col-33, .col-34 { 118 | @include flex(0, 0, 33.3333%); 119 | max-width: 33.3333%; 120 | } 121 | .col-50 { 122 | @include flex(0, 0, 50%); 123 | max-width: 50%; 124 | } 125 | .col-66, .col-67 { 126 | @include flex(0, 0, 66.6666%); 127 | max-width: 66.6666%; 128 | } 129 | .col-75 { 130 | @include flex(0, 0, 75%); 131 | max-width: 75%; 132 | } 133 | .col-80 { 134 | @include flex(0, 0, 80%); 135 | max-width: 80%; 136 | } 137 | .col-90 { 138 | @include flex(0, 0, 90%); 139 | max-width: 90%; 140 | } 141 | 142 | 143 | /* Responsive Grid Classes */ 144 | /* Adding a class of responsive-X to a row */ 145 | /* will trigger the flex-direction to */ 146 | /* change to column and add some margin */ 147 | /* to any columns in the row for clearity */ 148 | 149 | @include responsive-grid-break('.responsive-sm', $grid-responsive-sm-break); 150 | @include responsive-grid-break('.responsive-md', $grid-responsive-md-break); 151 | @include responsive-grid-break('.responsive-lg', $grid-responsive-lg-break); 152 | -------------------------------------------------------------------------------- /www/lib/ionic/scss/_type.scss: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Typography 4 | * -------------------------------------------------- 5 | */ 6 | 7 | 8 | // Body text 9 | // ------------------------- 10 | 11 | p { 12 | margin: 0 0 ($line-height-computed / 2); 13 | } 14 | 15 | 16 | // Emphasis & misc 17 | // ------------------------- 18 | 19 | small { font-size: 85%; } 20 | cite { font-style: normal; } 21 | 22 | 23 | // Alignment 24 | // ------------------------- 25 | 26 | .text-left { text-align: left; } 27 | .text-right { text-align: right; } 28 | .text-center { text-align: center; } 29 | 30 | 31 | // Headings 32 | // ------------------------- 33 | 34 | h1, h2, h3, h4, h5, h6, 35 | .h1, .h2, .h3, .h4, .h5, .h6 { 36 | color: $base-color; 37 | font-weight: $headings-font-weight; 38 | font-family: $headings-font-family; 39 | line-height: $headings-line-height; 40 | 41 | small { 42 | font-weight: normal; 43 | line-height: 1; 44 | } 45 | } 46 | 47 | h1, .h1, 48 | h2, .h2, 49 | h3, .h3 { 50 | margin-top: $line-height-computed; 51 | margin-bottom: ($line-height-computed / 2); 52 | 53 | &:first-child { 54 | margin-top: 0; 55 | } 56 | 57 | + h1, + .h1, 58 | + h2, + .h2, 59 | + h3, + .h3 { 60 | margin-top: ($line-height-computed / 2); 61 | } 62 | } 63 | 64 | h4, .h4, 65 | h5, .h5, 66 | h6, .h6 { 67 | margin-top: ($line-height-computed / 2); 68 | margin-bottom: ($line-height-computed / 2); 69 | } 70 | 71 | h1, .h1 { font-size: floor($font-size-base * 2.60); } // ~36px 72 | h2, .h2 { font-size: floor($font-size-base * 2.15); } // ~30px 73 | h3, .h3 { font-size: ceil($font-size-base * 1.70); } // ~24px 74 | h4, .h4 { font-size: ceil($font-size-base * 1.25); } // ~18px 75 | h5, .h5 { font-size: $font-size-base; } 76 | h6, .h6 { font-size: ceil($font-size-base * 0.85); } // ~12px 77 | 78 | h1 small, .h1 small { font-size: ceil($font-size-base * 1.70); } // ~24px 79 | h2 small, .h2 small { font-size: ceil($font-size-base * 1.25); } // ~18px 80 | h3 small, .h3 small, 81 | h4 small, .h4 small { font-size: $font-size-base; } 82 | 83 | 84 | // Description Lists 85 | // ------------------------- 86 | 87 | dl { 88 | margin-bottom: $line-height-computed; 89 | } 90 | dt, 91 | dd { 92 | line-height: $line-height-base; 93 | } 94 | dt { 95 | font-weight: bold; 96 | } 97 | 98 | 99 | // Blockquotes 100 | // ------------------------- 101 | 102 | blockquote { 103 | margin: 0 0 $line-height-computed; 104 | padding: ($line-height-computed / 2) $line-height-computed; 105 | border-left: 5px solid gray; 106 | 107 | p { 108 | font-weight: 300; 109 | font-size: ($font-size-base * 1.25); 110 | line-height: 1.25; 111 | } 112 | 113 | p:last-child { 114 | margin-bottom: 0; 115 | } 116 | 117 | small { 118 | display: block; 119 | line-height: $line-height-base; 120 | &:before { 121 | content: '\2014 \00A0';// EM DASH, NBSP; 122 | } 123 | } 124 | } 125 | 126 | 127 | // Quotes 128 | // ------------------------- 129 | 130 | q:before, 131 | q:after, 132 | blockquote:before, 133 | blockquote:after { 134 | content: ""; 135 | } 136 | 137 | 138 | // Addresses 139 | // ------------------------- 140 | 141 | address { 142 | display: block; 143 | margin-bottom: $line-height-computed; 144 | font-style: normal; 145 | line-height: $line-height-base; 146 | } 147 | 148 | 149 | // Links 150 | // ------------------------- 151 | 152 | a.subdued { 153 | padding-right: 10px; 154 | color: #888; 155 | text-decoration: none; 156 | 157 | &:hover { 158 | text-decoration: none; 159 | } 160 | &:last-child { 161 | padding-right: 0; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /www/lib/ionic/scss/_action-sheet.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Action Sheets 3 | * -------------------------------------------------- 4 | */ 5 | 6 | .action-sheet-backdrop { 7 | @include transition(background-color 150ms 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.4); 18 | } 19 | } 20 | 21 | .action-sheet-wrapper { 22 | @include translate3d(0, 100%, 0); 23 | @include transition(all cubic-bezier(.36, .66, .04, 1) 500ms); 24 | position: absolute; 25 | bottom: 0; 26 | left: 0; 27 | right: 0; 28 | width: 100%; 29 | max-width: 500px; 30 | margin: auto; 31 | } 32 | 33 | .action-sheet-up { 34 | @include translate3d(0, 0, 0); 35 | } 36 | 37 | .action-sheet { 38 | margin-left: $sheet-margin; 39 | margin-right: $sheet-margin; 40 | width: auto; 41 | z-index: $z-index-action-sheet; 42 | overflow: hidden; 43 | 44 | .button { 45 | display: block; 46 | padding: 1px; 47 | width: 100%; 48 | border-radius: 0; 49 | border-color: $sheet-options-border-color; 50 | background-color: transparent; 51 | 52 | color: $sheet-options-text-color; 53 | font-size: 21px; 54 | 55 | &:hover { 56 | color: $sheet-options-text-color; 57 | } 58 | &.destructive { 59 | color: #ff3b30; 60 | &:hover { 61 | color: #ff3b30; 62 | } 63 | } 64 | } 65 | 66 | .button.active, .button.activated { 67 | box-shadow: none; 68 | border-color: $sheet-options-border-color; 69 | color: $sheet-options-text-color; 70 | background: $sheet-options-bg-active-color; 71 | } 72 | } 73 | 74 | .action-sheet-has-icons .icon { 75 | position: absolute; 76 | left: 16px; 77 | } 78 | 79 | .action-sheet-title { 80 | padding: $sheet-margin * 2; 81 | color: #8f8f8f; 82 | text-align: center; 83 | font-size: 13px; 84 | } 85 | 86 | .action-sheet-group { 87 | margin-bottom: $sheet-margin; 88 | border-radius: $sheet-border-radius; 89 | background-color: #fff; 90 | overflow: hidden; 91 | 92 | .button { 93 | border-width: 1px 0px 0px 0px; 94 | } 95 | .button:first-child:last-child { 96 | border-width: 0; 97 | } 98 | } 99 | 100 | .action-sheet-options { 101 | background: $sheet-options-bg-color; 102 | } 103 | 104 | .action-sheet-cancel { 105 | .button { 106 | font-weight: 500; 107 | } 108 | } 109 | 110 | .action-sheet-open { 111 | pointer-events: none; 112 | 113 | &.modal-open .modal { 114 | pointer-events: none; 115 | } 116 | 117 | .action-sheet-backdrop { 118 | pointer-events: auto; 119 | } 120 | } 121 | 122 | 123 | .platform-android { 124 | 125 | .action-sheet-backdrop.active { 126 | background-color: rgba(0,0,0,0.2); 127 | } 128 | 129 | .action-sheet { 130 | margin: 0; 131 | 132 | .action-sheet-title, 133 | .button { 134 | text-align: left; 135 | border-color: transparent; 136 | font-size: 16px; 137 | color: inherit; 138 | } 139 | 140 | .action-sheet-title { 141 | font-size: 14px; 142 | padding: 16px; 143 | color: #666; 144 | } 145 | 146 | .button.active, 147 | .button.activated { 148 | background: #e8e8e8; 149 | } 150 | } 151 | 152 | .action-sheet-group { 153 | margin: 0; 154 | border-radius: 0; 155 | background-color: #fafafa; 156 | } 157 | 158 | .action-sheet-cancel { 159 | display: none; 160 | } 161 | 162 | .action-sheet-has-icons { 163 | 164 | .button { 165 | padding-left: 56px; 166 | } 167 | 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /www/css/style.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | div .profile-content { 4 | text-align: center; 5 | } 6 | 7 | .profile-image { 8 | max-width: 100%; 9 | height: auto; 10 | } 11 | 12 | .match{ 13 | background-color:#387EF5; 14 | } 15 | 16 | .no-match{ 17 | background-color:#EF473A; 18 | } 19 | 20 | #h2 { 21 | font-size: 24px; 22 | font-weight: bold; 23 | } 24 | 25 | #h4 { 26 | font-size: 18px; 27 | font-weight: bold; 28 | color:#ffffff; 29 | } 30 | 31 | #listIcons{ 32 | font-size: 36px; 33 | font-weight: bold; 34 | color:#000000; 35 | } 36 | 37 | .profile-icons { 38 | text-align:center; 39 | } 40 | 41 | .user-profile-icons { 42 | text-align:center; 43 | float:left; 44 | overflow: visible; 45 | white-space: normal; 46 | margin-top:10px; 47 | } 48 | 49 | .swipe-page { 50 | 51 | } 52 | 53 | .swipe-page .current-candidate { 54 | -webkit-transform: scale(1); 55 | -ms-transform: scale(1); 56 | transform: scale(1); 57 | opacity: 1; 58 | 59 | -webkit-transition:all cubic-bezier(0.420, 0.000, 1.000, 1.000) 0.25s; 60 | transition:all cubic-bezier(0.420, 0.000, 1.000, 1.000) 0.25s; 61 | } 62 | 63 | .swipe-page .current-candidate .item-image { 64 | max-height:300px; 65 | overflow:hidden; 66 | } 67 | 68 | /*.item { 69 | overflow:visible; 70 | word-wrap:break-word; 71 | }*/ 72 | 73 | @media screen and (max-height: 600px) { 74 | .swipe-page .current-candidate .item-image { 75 | max-height:200px; 76 | overflow:hidden; 77 | } 78 | } 79 | 80 | .swipe-page .current-candidate.favorited.ng-hide { 81 | -webkit-transform: translateX(1000px) rotate(50deg); 82 | -ms-transform: translateX(1000px) rotate(50deg); 83 | transform: translateX(1000px) rotate(50deg); 84 | opacity:0; 85 | } 86 | 87 | .swipe-page .current-candidate.skipped.ng-hide { 88 | -webkit-transform: translateX(-1000px) rotate(-50deg); 89 | -ms-transform: translateX(-1000px) rotate(-50deg); 90 | transform: translateX(-1000px) rotate(-50deg); 91 | opacity:0; 92 | } 93 | 94 | .swipe-page .current-candidate.ng-hide-add { 95 | -webkit-transition:all cubic-bezier(0.420, 0.000, 1.000, 1.000) 0.3s; 96 | transition:all cubic-bezier(0.420, 0.000, 1.000, 1.000) 0.3s; 97 | display:block!important; 98 | } 99 | 100 | .swipe-page .current-candidate.ng-hide-remove { 101 | -webkit-transform: scale(0.7) translateX(0px) rotate(0deg); 102 | -ms-transform: scale(0.7) translateX(0px) rotate(0deg); 103 | transform: scale(0.7) translateX(0px) rotate(0deg); 104 | opacity: 0; 105 | } 106 | 107 | 108 | .swipe-page .img-lookahead img { 109 | height:1px; 110 | width:1px; 111 | opacity:0.01; 112 | } 113 | 114 | 115 | .button-medium { 116 | padding: 4px 6px 4px; 117 | margin-left:23px; 118 | margin-right:23px; 119 | min-width: 100px; 120 | min-height: 43px; 121 | font-size: 18px; 122 | line-height: 42px; 123 | } 124 | 125 | /*.account-buttons { 126 | text-align: center; 127 | }*/ 128 | 129 | #map { 130 | width: 100%; 131 | height: 100%; 132 | } 133 | 134 | .scroll { 135 | height: 75%; 136 | } 137 | 138 | #panel { 139 | position: absolute; 140 | top: 5px; 141 | left: 57%; 142 | margin-left: -180px; 143 | border-radius: 10px; 144 | z-index: 5; 145 | background-color: black; 146 | padding: 7px; 147 | } 148 | 149 | #ionicon-button { 150 | top:-1px; 151 | float:left; 152 | } 153 | 154 | #ionicon-input { 155 | width:100%; 156 | line-height:12px; 157 | padding:0px; 158 | float:left; 159 | font-size:12px; 160 | } 161 | 162 | #ionicon-text { 163 | overflow: visible; 164 | white-space: normal; 165 | margin-top:10px; 166 | } 167 | 168 | .no-more-candidates { 169 | text-align: center; 170 | } 171 | 172 | ion-footer-bar { 173 | margin-bottom: 10px 174 | } 175 | -------------------------------------------------------------------------------- /_PRESS-RELEASE.md: -------------------------------------------------------------------------------- 1 | # Project Name # 2 | 3 | 18 | 19 | ## Heading ## 20 | > Name the product in a way the reader (i.e. your target customers) will understand. 21 | 22 | ## Sub-Heading ## 23 | > Describe who the market for the product is and what benefit they get. One sentence only underneath the title. 24 | 25 | ## Summary ## 26 | > Give a summary of the product and the benefit. Assume the reader will not read anything else so make this paragraph good. 27 | 28 | ## Problem ## 29 | > Describe the problem your product solves. 30 | 31 | ## Solution ## 32 | > Describe how your product elegantly solves the problem. 33 | 34 | ## Quote from You ## 35 | > A quote from a spokesperson in your company. 36 | 37 | ## How to Get Started ## 38 | > Describe how easy it is to get started. 39 | 40 | ## Customer Quote ## 41 | > Provide a quote from a hypothetical customer that describes how they experienced the benefit. 42 | 43 | ## Closing and Call to Action ## 44 | > Wrap it up and give pointers where the reader should go next. 45 | -------------------------------------------------------------------------------- /www/lib/ionic/scss/_popover.scss: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Popovers 4 | * -------------------------------------------------- 5 | * Popovers are independent views which float over content 6 | */ 7 | 8 | .popover-backdrop { 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | z-index: $z-index-popover; 13 | width: 100%; 14 | height: 100%; 15 | background-color: $popover-backdrop-bg-inactive; 16 | 17 | &.active { 18 | background-color: $popover-backdrop-bg-active; 19 | } 20 | } 21 | 22 | .popover { 23 | position: absolute; 24 | top: 25%; 25 | left: 50%; 26 | z-index: $z-index-popover; 27 | display: block; 28 | margin-top: 12px; 29 | margin-left: -$popover-width / 2; 30 | height: $popover-height; 31 | width: $popover-width; 32 | background-color: $popover-bg-color; 33 | box-shadow: $popover-box-shadow; 34 | opacity: 0; 35 | 36 | .item:first-child { 37 | border-top: 0; 38 | } 39 | 40 | .item:last-child { 41 | border-bottom: 0; 42 | } 43 | 44 | &.popover-bottom { 45 | margin-top: -12px; 46 | } 47 | } 48 | 49 | 50 | // Set popover border-radius 51 | .popover, 52 | .popover .bar-header { 53 | border-radius: $popover-border-radius; 54 | } 55 | .popover .scroll-content { 56 | z-index: 1; 57 | margin: 2px 0; 58 | } 59 | .popover .bar-header { 60 | border-bottom-right-radius: 0; 61 | border-bottom-left-radius: 0; 62 | } 63 | .popover .has-header { 64 | border-top-right-radius: 0; 65 | border-top-left-radius: 0; 66 | } 67 | .popover-arrow { 68 | display: none; 69 | } 70 | 71 | 72 | // iOS Popover 73 | .platform-ios { 74 | 75 | .popover { 76 | box-shadow: $popover-box-shadow-ios; 77 | border-radius: $popover-border-radius-ios; 78 | } 79 | .popover .bar-header { 80 | @include border-top-radius($popover-border-radius-ios); 81 | } 82 | .popover .scroll-content { 83 | margin: 8px 0; 84 | border-radius: $popover-border-radius-ios; 85 | } 86 | .popover .scroll-content.has-header { 87 | margin-top: 0; 88 | } 89 | .popover-arrow { 90 | position: absolute; 91 | display: block; 92 | top: -17px; 93 | width: 30px; 94 | height: 19px; 95 | overflow: hidden; 96 | 97 | &:after { 98 | position: absolute; 99 | top: 12px; 100 | left: 5px; 101 | width: 20px; 102 | height: 20px; 103 | background-color: $popover-bg-color; 104 | border-radius: 3px; 105 | content: ''; 106 | @include rotate(-45deg); 107 | } 108 | } 109 | .popover-bottom .popover-arrow { 110 | top: auto; 111 | bottom: -10px; 112 | &:after { 113 | top: -6px; 114 | } 115 | } 116 | } 117 | 118 | 119 | // Android Popover 120 | .platform-android { 121 | 122 | .popover { 123 | margin-top: -32px; 124 | background-color: $popover-bg-color-android; 125 | box-shadow: $popover-box-shadow-android; 126 | 127 | .item { 128 | border-color: $popover-bg-color-android; 129 | background-color: $popover-bg-color-android; 130 | color: #4d4d4d; 131 | } 132 | &.popover-bottom { 133 | margin-top: 32px; 134 | } 135 | } 136 | 137 | .popover-backdrop, 138 | .popover-backdrop.active { 139 | background-color: transparent; 140 | } 141 | } 142 | 143 | 144 | // disable clicks on all but the popover 145 | .popover-open { 146 | pointer-events: none; 147 | 148 | .popover, 149 | .popover-backdrop { 150 | pointer-events: auto; 151 | } 152 | // prevent clicks on popover when loading overlay is active though 153 | &.loading-active { 154 | .popover, 155 | .popover-backdrop { 156 | pointer-events: none; 157 | } 158 | } 159 | } 160 | 161 | 162 | // wider popover on larger viewports 163 | @media (min-width: $popover-large-break-point) { 164 | .popover { 165 | width: $popover-large-width; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /www/account/place/place.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

Welcome, {{username}}

6 |
7 | 8 | 9 | I already have a place, just need a roommate 10 | I have nothing! 11 | 12 | 13 | 14 | 15 |
16 |

My Place

17 | 18 |

Rent: {{location.myPlace.rent | currency}}

19 |
20 | 21 | 22 |
23 |
24 | 25 |
26 |

Rooms Available

27 | 30 |
31 |
32 |

Room Type

33 | 37 |
38 |
39 |

Current Occupants

40 | 55 |
56 | 57 |
58 |

The Place I want

59 | 60 |

Desired Rent: {{location.desiredPlace.rent | currency}}

61 |
62 | 63 | 64 |
65 |
66 |
67 | 68 | 71 | 72 |
73 | 74 | 75 | 78 | 79 | 80 |
81 |
82 | -------------------------------------------------------------------------------- /tests/data/MatchesFactory.tests.js: -------------------------------------------------------------------------------- 1 | describe('Factory: MatchesFactory', function() { 2 | // var scope, $login, controller; 3 | var scope, MatchesFactory, httpBackend; 4 | 5 | var testMatches; 6 | //load controller's module and other necessary modules 7 | beforeEach(module('data'/*,'ui.router'*/)); 8 | 9 | beforeEach(inject(function($httpBackend, _MatchesFactory_) { 10 | httpBackend = $httpBackend; 11 | MatchesFactory = _MatchesFactory_; 12 | testMatches = [ 13 | {fbid:123,name:"Test Name"}, 14 | {fbid:456,name:"Hello World"}, 15 | {fbid:789,name:"Another Test"} 16 | ]; 17 | })); 18 | 19 | 20 | //tests start here 21 | 22 | describe('initialize: ', function() { 23 | 24 | it("Should initialize matches to the passed in array", function() { 25 | expect(MatchesFactory.all().length).toEqual(0); 26 | httpBackend.whenGET("http://localhost:8888/user/1234/matches") 27 | .respond(testMatches) 28 | MatchesFactory.initialize({fbid: 1234, matched: [123, 456, 789]}) 29 | .then(function(matches) { 30 | expect(MatchesFactory.all().length).toEqual(3); 31 | }) 32 | }); 33 | 34 | }); 35 | 36 | describe('all: ', function() { 37 | 38 | it("Should return an empty array before being initialized", function() { 39 | expect(MatchesFactory.all().length).toEqual(0); 40 | }); 41 | 42 | it("Should return all added objects array after initialization and adding", function() { 43 | httpBackend.whenGET("http://localhost:8888/user/1234/matches") 44 | .respond(testMatches) 45 | MatchesFactory.initialize({fbid: 1234, matched: [123, 456, 789]}) 46 | .then(function(matches){ 47 | MatchesFactory.add({id:0,name:"Zero",likedCurrentUser:true}); 48 | expect(MatchesFactory.all().length).toEqual(4); 49 | }); 50 | }); 51 | 52 | }); 53 | 54 | describe('remove: ', function() { 55 | 56 | it("Should remove a match from MatchesFactory", function() { 57 | httpBackend.whenGET("http://localhost:8888/user/1234/matches") 58 | .respond(testMatches) 59 | MatchesFactory.initialize({fbid: 1234, matched: [123, 456, 789]}) 60 | .then(function(matches) { 61 | expect(MatchesFactory.all().length).toEqual(3); 62 | MatchesFactory.remove(testMatches[1]); 63 | expect(MatchesFactory.all().length).toEqual(2); 64 | MatchesFactory.remove(testMatches[1]); 65 | MatchesFactory.remove(testMatches[0]); 66 | expect(MatchesFactory.all().length).toEqual(0); 67 | }) 68 | }); 69 | 70 | }); 71 | 72 | describe('add: ', function() { 73 | 74 | it("Should add a match to MatchesFactory", function() { 75 | httpBackend.whenGET("http://localhost:8888/user/1234/matches") 76 | .respond(testMatches) 77 | MatchesFactory.initialize({fbid: 1234, matched: [123, 456, 789]}) 78 | .then(function(matches) { 79 | MatchesFactory.add({id:0,name:"Zero",likedCurrentUser:true}); 80 | expect(MatchesFactory.all().length).toEqual(4); 81 | MatchesFactory.add({id:1,name:"One",likedCurrentUser:true}); 82 | MatchesFactory.add({id:2,name:"Two",likedCurrentUser:true}); 83 | expect(MatchesFactory.all().length).toEqual(6); 84 | }); 85 | }); 86 | 87 | }); 88 | 89 | describe('get: ', function() { 90 | 91 | it("Should return a match with the specified id from MatchesFactory", function() { 92 | httpBackend.whenGET("http://localhost:8888/user/1234/matches") 93 | .respond(testMatches) 94 | MatchesFactory.initialize({fbid: 1234, matched: [123, 456, 789]}) 95 | .then(function(matches) { 96 | var getName = MatchesFactory.get(456).name; 97 | expect(getName).toEqual("Hello World"); 98 | }); 99 | }); 100 | 101 | }); 102 | 103 | }); 104 | -------------------------------------------------------------------------------- /tests/data/PlaceFactory.tests.js: -------------------------------------------------------------------------------- 1 | describe('Factory: PlaceFactory', function() { 2 | // var scope, $login, controller; 3 | var scope, PlaceFactory, httpBackend; 4 | 5 | var testPlace; 6 | //load controller's module and other necessary modules 7 | beforeEach(module('data'/*,'ui.router'*/)); 8 | 9 | beforeEach(inject(function(_PlaceFactory_, $httpBackend) { 10 | PlaceFactory = _PlaceFactory_; 11 | httpBackend = $httpBackend; 12 | testUser = { 13 | location: { 14 | host: true, 15 | myPlace: { 16 | rent: 1000, 17 | zipCode: 77777, 18 | genders: 'any', 19 | openRooms: 2, 20 | roomType: 'awesome', 21 | occupants: 1, 22 | city: null, 23 | state: null 24 | }, 25 | desiredPlace:{ 26 | rent: 1000, 27 | zipCode: 77776, 28 | radius: 3, 29 | roomType: 'awesome', 30 | latitude: null, 31 | longitude: null, 32 | city: null, 33 | state: null 34 | } 35 | } 36 | }; 37 | })); 38 | 39 | afterEach(function() { 40 | httpBackend.verifyNoOutstandingExpectation(); 41 | httpBackend.verifyNoOutstandingRequest(); 42 | }); 43 | 44 | //tests start here 45 | 46 | it('Should initialize place to the empty default', function(){ 47 | var place = PlaceFactory.all(); 48 | expect(place.myPlace.rent).toBeNull(); 49 | expect(place.myPlace.genders).toBeNull(); 50 | expect(Object.keys(place.desiredPlace).length).toEqual(8); 51 | }); 52 | 53 | describe('initialize', function() { 54 | 55 | it("Should initialize place to the passed in object", function() { 56 | httpBackend.whenPUT('http://localhost:8888/user/1234/location?token=5678') 57 | .respond(testUser) 58 | PlaceFactory.initialize(testUser.location, {fbid: 1234, token: 5678}) 59 | .then(function(newLocation) { 60 | console.log("new location ", newLocation) 61 | expect(newLocation.myPlace.rent).toEqual(1000); 62 | }); 63 | httpBackend.flush(); 64 | }); 65 | 66 | }); 67 | 68 | describe('all', function() { 69 | 70 | it("Should return the place object", function() { 71 | httpBackend.whenPUT('http://localhost:8888/user/1234/location?token=5678') 72 | .respond(testUser) 73 | PlaceFactory.initialize(testUser.location, {fbid: 1234,token:5678}) 74 | .then(function(newLocation) { 75 | console.log("all ", PlaceFactory.all()) 76 | expect(PlaceFactory.all().myPlace.rent).toEqual(1000); 77 | }); 78 | httpBackend.flush(); 79 | }); 80 | 81 | }); 82 | /////////////////////////Not Using these right now//////////////////// 83 | describe('update', function() { 84 | 85 | it("Should update a place property", function() { 86 | PlaceFactory.update('host',false); 87 | var myPlace = { 88 | rent: 2000, 89 | zipCode: 88888, 90 | genders: 'unspecified', 91 | openRooms: 1, 92 | roomType: 'nice', 93 | occupants: 0 94 | }; 95 | var desiredPlace = { 96 | rent: 2000, 97 | zipCode: 99999, 98 | radius: 5, 99 | roomType: 'nice', 100 | }; 101 | PlaceFactory.update('myPlace',myPlace); 102 | PlaceFactory.update('desiredPlace',desiredPlace); 103 | var place = PlaceFactory.all(); 104 | expect(place.host).toEqual(false); 105 | expect(place.myPlace.zipCode).toEqual(88888); 106 | expect(place.myPlace.roomType).toEqual('nice'); 107 | expect(place.desiredPlace.zipCode).toEqual(99999); 108 | expect(place.desiredPlace.roomType).toEqual('nice'); 109 | }); 110 | 111 | }); 112 | 113 | describe('getProperty', function() { 114 | 115 | it("Should return the specified property", function() { 116 | httpBackend.whenPUT('http://localhost:8888/user/1234/location?token=5678') 117 | .respond(testUser) 118 | PlaceFactory.initialize(testUser.location, {fbid: 1234, token: 5678}) 119 | .then(function(newLocation) { 120 | expect(PlaceFactory.getProperty('host')).toEqual(true); 121 | var myPlace = PlaceFactory.getProperty('myPlace'); 122 | expect(myPlace.genders).toEqual('any'); 123 | expect(myPlace.zipCode).toEqual(77777); 124 | }); 125 | httpBackend.flush(); 126 | }); 127 | 128 | }); 129 | /////////////////////////////////////////////////////////////// 130 | 131 | 132 | }); 133 | -------------------------------------------------------------------------------- /www/lib/ionic/scss/_transitions.scss: -------------------------------------------------------------------------------- 1 | 2 | // iOS View Transitions 3 | // ------------------------------- 4 | 5 | $ios-transition-duration: 500ms !default; 6 | $ios-transition-timing-function: cubic-bezier(.36, .66, .04, 1) !default; 7 | $ios-transition-container-bg-color: #000 !default; 8 | 9 | 10 | [nav-view-transition="ios"] { 11 | 12 | [nav-view="entering"], 13 | [nav-view="leaving"] { 14 | @include transition-duration( $ios-transition-duration ); 15 | @include transition-timing-function( $ios-transition-timing-function ); 16 | -webkit-transition-property: opacity, -webkit-transform, box-shadow; 17 | transition-property: opacity, transform, box-shadow; 18 | } 19 | 20 | &[nav-view-direction="forward"], 21 | &[nav-view-direction="back"] { 22 | background-color: $ios-transition-container-bg-color; 23 | } 24 | 25 | [nav-view="active"], 26 | &[nav-view-direction="forward"] [nav-view="entering"], 27 | &[nav-view-direction="back"] [nav-view="leaving"] { 28 | z-index: $z-index-view-above; 29 | } 30 | 31 | &[nav-view-direction="back"] [nav-view="entering"], 32 | &[nav-view-direction="forward"] [nav-view="leaving"] { 33 | z-index: $z-index-view-below; 34 | } 35 | 36 | } 37 | 38 | 39 | 40 | // iOS Nav Bar Transitions 41 | // ------------------------------- 42 | 43 | [nav-bar-transition="ios"] { 44 | 45 | .title, 46 | .buttons, 47 | .back-text { 48 | @include transition-duration( $ios-transition-duration ); 49 | @include transition-timing-function( $ios-transition-timing-function ); 50 | -webkit-transition-property: opacity, -webkit-transform; 51 | transition-property: opacity, transform; 52 | } 53 | 54 | [nav-bar="active"], 55 | [nav-bar="entering"] { 56 | z-index: $z-index-bar-above; 57 | 58 | .bar { 59 | background: transparent; 60 | } 61 | } 62 | 63 | [nav-bar="cached"] { 64 | display: block; 65 | 66 | .header-item { 67 | display: none; 68 | } 69 | } 70 | 71 | } 72 | 73 | 74 | 75 | // Android View Transitions 76 | // ------------------------------- 77 | 78 | $android-transition-duration: 200ms !default; 79 | $android-transition-timing-function: cubic-bezier(0.4, 0.6, 0.2, 1) !default; 80 | 81 | 82 | [nav-view-transition="android"] { 83 | 84 | [nav-view="entering"], 85 | [nav-view="leaving"] { 86 | @include transition-duration( $android-transition-duration ); 87 | @include transition-timing-function( $android-transition-timing-function ); 88 | -webkit-transition-property: -webkit-transform; 89 | transition-property: transform; 90 | } 91 | 92 | [nav-view="active"], 93 | &[nav-view-direction="forward"] [nav-view="entering"], 94 | &[nav-view-direction="back"] [nav-view="leaving"] { 95 | z-index: $z-index-view-above; 96 | } 97 | 98 | &[nav-view-direction="back"] [nav-view="entering"], 99 | &[nav-view-direction="forward"] [nav-view="leaving"] { 100 | z-index: $z-index-view-below; 101 | } 102 | 103 | } 104 | 105 | 106 | 107 | // Android Nav Bar Transitions 108 | // ------------------------------- 109 | 110 | [nav-bar-transition="android"] { 111 | 112 | .title, 113 | .buttons { 114 | @include transition-duration( $android-transition-duration ); 115 | @include transition-timing-function( $android-transition-timing-function ); 116 | -webkit-transition-property: opacity; 117 | transition-property: opacity; 118 | } 119 | 120 | [nav-bar="active"], 121 | [nav-bar="entering"] { 122 | z-index: $z-index-bar-above; 123 | 124 | .bar { 125 | background: transparent; 126 | } 127 | } 128 | 129 | [nav-bar="cached"] { 130 | display: block; 131 | 132 | .header-item { 133 | display: none; 134 | } 135 | } 136 | 137 | } 138 | 139 | 140 | 141 | // Nav Swipe 142 | // ------------------------------- 143 | 144 | [nav-swipe="fast"] { 145 | [nav-view], 146 | .title, 147 | .buttons, 148 | .back-text { 149 | @include transition-duration(50ms); 150 | @include transition-timing-function(linear); 151 | } 152 | } 153 | 154 | [nav-swipe="slow"] { 155 | [nav-view], 156 | .title, 157 | .buttons, 158 | .back-text { 159 | @include transition-duration(160ms); 160 | @include transition-timing-function(linear); 161 | } 162 | } 163 | 164 | 165 | 166 | // Transition Settings 167 | // ------------------------------- 168 | 169 | [nav-view="cached"], 170 | [nav-bar="cached"] { 171 | display: none; 172 | } 173 | 174 | [nav-view="stage"] { 175 | opacity: 0; 176 | @include transition-duration( 0 ); 177 | } 178 | 179 | [nav-bar="stage"] { 180 | .title, 181 | .buttons, 182 | .back-text { 183 | position: absolute; 184 | opacity: 0; 185 | @include transition-duration(0s); 186 | } 187 | } 188 | 189 | -------------------------------------------------------------------------------- /config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | tfr 4 | 5 | An Ionic Framework and Cordova project. 6 | 7 | 8 | Ionic Framework Team 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /server/users/userController.js: -------------------------------------------------------------------------------- 1 | var Users = require('./userModel.js'); 2 | var Q = require('q'); 3 | var jwt = require('jwt-simple'); 4 | var secret = require('./secret.js'); 5 | 6 | 7 | var findUsers = Q.nbind(Users.find, Users); 8 | var findUser = Q.nbind(Users.findOne, Users); 9 | var findOrCreate = Q.nbind(Users.findOneAndUpdate, Users); 10 | var findAndUpdate = Q.nbind(Users.findOneAndUpdate, Users); 11 | var removeUser = Q.nbind(Users.remove, Users); 12 | 13 | module.exports = { 14 | 15 | auth: function(req,res){ 16 | var token = req.body.token; 17 | var decoded = jwt.decode(token, secret); 18 | var decodedId = decoded.d.uid.match(/\d+/)[0]; 19 | var tokenTail = token.match(/(\.[^\.]+)/g).slice(1); 20 | if(req.params.id === decodedId){ 21 | findOrCreate( 22 | {fbid: req.params.id}, 23 | {$setOnInsert: { 24 | fbid: req.params.id, 25 | token: tokenTail 26 | } 27 | }, 28 | {upsert: true, new: true} 29 | ) 30 | .then(function(user) { 31 | console.log('AUTHUSER',user); 32 | res.send(user); 33 | }); 34 | } 35 | }, 36 | 37 | getCandidates: function(req, res) { 38 | console.log('req body in getCandidates ', req.query) 39 | console.log('req params ', req.params) 40 | var latitude = req.query.lat; 41 | var longitude = req.query.longt; 42 | var radius = req.query.radi*1.6/6370; 43 | console.log('req.query.token',req.query.token); 44 | findUser({_id: req.params.id,token: req.query.token}) 45 | .then(function(user) { 46 | console.log('getCandidates: found user- ', user); 47 | if (user.matched) { 48 | var matches = Object.keys(user.matched); 49 | } else { 50 | var matches = []; 51 | } 52 | var likeAndMatchedAndUser = user.liked.concat(matches); 53 | console.log('like and match ', likeAndMatchedAndUser) 54 | likeAndMatchedAndUser.push(req.params.id) 55 | findUsers({ 56 | loc: {$nearSphere: [latitude, longitude], $maxDistance: radius}, 57 | _id: {$nin: likeAndMatchedAndUser} 58 | }) 59 | .then(function(data){ 60 | data.forEach(function(user) { 61 | console.log('getCandidates: possible candidates\' ids ', user.id); 62 | }) 63 | res.send(data); 64 | }) 65 | }) 66 | }, 67 | 68 | addOrFindCurrentUser: function(req, res) { 69 | console.log('req.body!!!',req.body.name); 70 | if(req.body.location){ 71 | var latitude = req.body.location.desiredPlace.latitude; 72 | var longitude = req.body.location.desiredPlace.longitude; 73 | } else { 74 | var latitude = 0; 75 | var longitude = 0; 76 | } 77 | findAndUpdate( 78 | {fbid: req.params.id,token: req.body.token}, 79 | {$set: { 80 | name: req.body.name, 81 | } 82 | }, 83 | {upsert: false, new: true} 84 | ) 85 | .then(function(user) { 86 | res.send(user); 87 | }); 88 | }, 89 | 90 | updateProfile: function(req, res) { 91 | console.log('params in updateProperty ', req.params); 92 | findOrCreate( 93 | {_id: req.params.id,token: req.query.token}, 94 | {$set: {profile: req.body.profile}}, 95 | {upsert: true, new: true} 96 | ) 97 | .then(function(user){ 98 | res.send(user); 99 | }); 100 | }, 101 | 102 | updateLocation: function(req, res) { 103 | var latitude = req.body.location.desiredPlace.latitude; 104 | var longitude = req.body.location.desiredPlace.longitude; 105 | console.log('updateLocation!!!',req.query); 106 | findOrCreate( 107 | {_id: req.params.id,token: req.query.token}, 108 | {$set: {loc: [latitude, longitude], location: req.body.location}}, 109 | {upsert: true, new: true} 110 | ) 111 | .then(function(user){ 112 | res.send(user); 113 | }); 114 | }, 115 | 116 | updateRoommatePreferences: function(req, res) { 117 | findOrCreate( 118 | {_id: req.params.id,token: req.query.token}, 119 | {$set: {roommatePreferences: req.body.roommatePreferences}}, 120 | {upsert: true, new: true} 121 | ) 122 | .then(function(user){ 123 | res.send(user); 124 | }); 125 | }, 126 | 127 | getMatches: function(req, res) { 128 | findUser({_id: req.params.id,token: req.query.token}) 129 | .then(function(user){ 130 | var matches = Object.keys(user.matched); 131 | findUsers({_id: {$in: matches}}) 132 | .then(function(matches) { 133 | res.send(matches); 134 | }) 135 | }) 136 | }, 137 | 138 | updateUserMatches: function(req, res) { 139 | console.log('/user: updateMatches request body ', req.body); 140 | findAndUpdate({_id: req.params.id,token: req.query.token}, 141 | {$set: {matched: req.body.matchesIds, liked: req.body.likedIds}}, 142 | {new: true} 143 | ) 144 | .then(function(user) { 145 | res.send(user); 146 | }) 147 | }, 148 | 149 | deleteAccount: function(req, res) { 150 | console.log("in delete request ", req.params) 151 | findUser({fbid: req.params.id,token: req.query.token}) 152 | .then(function(user) { 153 | removeUser({_id: user.id}) 154 | .then(function(data) { 155 | res.send("User deleted") 156 | }) 157 | }) 158 | } 159 | 160 | }; 161 | -------------------------------------------------------------------------------- /www/lib/ionic/scss/_checkbox.scss: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Checkbox 4 | * -------------------------------------------------- 5 | */ 6 | 7 | .checkbox { 8 | // set the color defaults 9 | @include checkbox-style($checkbox-off-border-default, $checkbox-on-bg-default, $checkbox-on-border-default); 10 | 11 | position: relative; 12 | display: inline-block; 13 | padding: ($checkbox-height / 4) ($checkbox-width / 4); 14 | cursor: pointer; 15 | } 16 | .checkbox-light { 17 | @include checkbox-style($checkbox-off-border-light, $checkbox-on-bg-light, $checkbox-off-border-light); 18 | } 19 | .checkbox-stable { 20 | @include checkbox-style($checkbox-off-border-stable, $checkbox-on-bg-stable, $checkbox-off-border-stable); 21 | } 22 | .checkbox-positive { 23 | @include checkbox-style($checkbox-off-border-positive, $checkbox-on-bg-positive, $checkbox-off-border-positive); 24 | } 25 | .checkbox-calm { 26 | @include checkbox-style($checkbox-off-border-calm, $checkbox-on-bg-calm, $checkbox-off-border-calm); 27 | } 28 | .checkbox-assertive { 29 | @include checkbox-style($checkbox-off-border-assertive, $checkbox-on-bg-assertive, $checkbox-off-border-assertive); 30 | } 31 | .checkbox-balanced { 32 | @include checkbox-style($checkbox-off-border-balanced, $checkbox-on-bg-balanced, $checkbox-off-border-balanced); 33 | } 34 | .checkbox-energized{ 35 | @include checkbox-style($checkbox-off-border-energized, $checkbox-on-bg-energized, $checkbox-off-border-energized); 36 | } 37 | .checkbox-royal { 38 | @include checkbox-style($checkbox-off-border-royal, $checkbox-on-bg-royal, $checkbox-off-border-royal); 39 | } 40 | .checkbox-dark { 41 | @include checkbox-style($checkbox-off-border-dark, $checkbox-on-bg-dark, $checkbox-off-border-dark); 42 | } 43 | 44 | .checkbox input:disabled:before, 45 | .checkbox input:disabled + .checkbox-icon:before { 46 | border-color: $checkbox-off-border-light; 47 | } 48 | 49 | .checkbox input:disabled:checked:before, 50 | .checkbox input:disabled:checked + .checkbox-icon:before { 51 | background: $checkbox-on-bg-light; 52 | } 53 | 54 | 55 | .checkbox.checkbox-input-hidden input { 56 | display: none !important; 57 | } 58 | 59 | .checkbox input, 60 | .checkbox-icon { 61 | position: relative; 62 | width: $checkbox-width; 63 | height: $checkbox-height; 64 | display: block; 65 | border: 0; 66 | background: transparent; 67 | cursor: pointer; 68 | -webkit-appearance: none; 69 | 70 | &:before { 71 | // what the checkbox looks like when its not checked 72 | display: table; 73 | width: 100%; 74 | height: 100%; 75 | border-width: $checkbox-border-width; 76 | border-style: solid; 77 | border-radius: $checkbox-border-radius; 78 | background: $checkbox-off-bg-color; 79 | content: ' '; 80 | @include transition(background-color 20ms ease-in-out); 81 | } 82 | } 83 | 84 | .checkbox input:checked:before, 85 | input:checked + .checkbox-icon:before { 86 | border-width: $checkbox-border-width + 1; 87 | } 88 | 89 | // the checkmark within the box 90 | .checkbox input:after, 91 | .checkbox-icon:after { 92 | @include transition(opacity .05s ease-in-out); 93 | @include rotate(-45deg); 94 | position: absolute; 95 | top: 33%; 96 | left: 25%; 97 | display: table; 98 | width: ($checkbox-width / 2); 99 | height: ($checkbox-width / 4) - 1; 100 | border: $checkbox-check-width solid $checkbox-check-color; 101 | border-top: 0; 102 | border-right: 0; 103 | content: ' '; 104 | opacity: 0; 105 | } 106 | 107 | .platform-android .checkbox-platform input:before, 108 | .platform-android .checkbox-platform .checkbox-icon:before, 109 | .checkbox-square input:before, 110 | .checkbox-square .checkbox-icon:before { 111 | border-radius: 2px; 112 | width: 72%; 113 | height: 72%; 114 | margin-top: 14%; 115 | margin-left: 14%; 116 | border-width: 2px; 117 | } 118 | 119 | .platform-android .checkbox-platform input:after, 120 | .platform-android .checkbox-platform .checkbox-icon:after, 121 | .checkbox-square input:after, 122 | .checkbox-square .checkbox-icon:after { 123 | border-width: 2px; 124 | top: 19%; 125 | left: 25%; 126 | width: ($checkbox-width / 2) - 1; 127 | height: 7px; 128 | } 129 | 130 | .grade-c .checkbox input:after, 131 | .grade-c .checkbox-icon:after { 132 | @include rotate(0); 133 | top: 3px; 134 | left: 4px; 135 | border: none; 136 | color: $checkbox-check-color; 137 | content: '\2713'; 138 | font-weight: bold; 139 | font-size: 20px; 140 | } 141 | 142 | // what the checkmark looks like when its checked 143 | .checkbox input:checked:after, 144 | input:checked + .checkbox-icon:after { 145 | opacity: 1; 146 | } 147 | 148 | // make sure item content have enough padding on left to fit the checkbox 149 | .item-checkbox { 150 | padding-left: ($item-padding * 2) + $checkbox-width; 151 | 152 | &.active { 153 | box-shadow: none; 154 | } 155 | } 156 | 157 | // position the checkbox to the left within an item 158 | .item-checkbox .checkbox { 159 | position: absolute; 160 | top: 50%; 161 | right: $item-padding / 2; 162 | left: $item-padding / 2; 163 | z-index: $z-index-item-checkbox; 164 | margin-top: (($checkbox-height + ($checkbox-height / 2)) / 2) * -1; 165 | } 166 | 167 | 168 | .item-checkbox.item-checkbox-right { 169 | padding-right: ($item-padding * 2) + $checkbox-width; 170 | padding-left: $item-padding; 171 | } 172 | 173 | .item-checkbox-right .checkbox input, 174 | .item-checkbox-right .checkbox-icon { 175 | float: right; 176 | } 177 | -------------------------------------------------------------------------------- /www/lib/ionic/scss/_toggle.scss: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Toggle 4 | * -------------------------------------------------- 5 | */ 6 | 7 | .item-toggle { 8 | pointer-events: none; 9 | } 10 | 11 | .toggle { 12 | // set the color defaults 13 | @include toggle-style($toggle-on-default-border, $toggle-on-default-bg); 14 | 15 | position: relative; 16 | display: inline-block; 17 | pointer-events: auto; 18 | margin: -$toggle-hit-area-expansion; 19 | padding: $toggle-hit-area-expansion; 20 | 21 | &.dragging { 22 | .handle { 23 | background-color: $toggle-handle-dragging-bg-color !important; 24 | } 25 | } 26 | 27 | } 28 | 29 | .toggle { 30 | &.toggle-light { 31 | @include toggle-style($toggle-on-light-border, $toggle-on-light-bg); 32 | } 33 | &.toggle-stable { 34 | @include toggle-style($toggle-on-stable-border, $toggle-on-stable-bg); 35 | } 36 | &.toggle-positive { 37 | @include toggle-style($toggle-on-positive-border, $toggle-on-positive-bg); 38 | } 39 | &.toggle-calm { 40 | @include toggle-style($toggle-on-calm-border, $toggle-on-calm-bg); 41 | } 42 | &.toggle-assertive { 43 | @include toggle-style($toggle-on-assertive-border, $toggle-on-assertive-bg); 44 | } 45 | &.toggle-balanced { 46 | @include toggle-style($toggle-on-balanced-border, $toggle-on-balanced-bg); 47 | } 48 | &.toggle-energized { 49 | @include toggle-style($toggle-on-energized-border, $toggle-on-energized-bg); 50 | } 51 | &.toggle-royal { 52 | @include toggle-style($toggle-on-royal-border, $toggle-on-royal-bg); 53 | } 54 | &.toggle-dark { 55 | @include toggle-style($toggle-on-dark-border, $toggle-on-dark-bg); 56 | } 57 | } 58 | 59 | .toggle input { 60 | // hide the actual input checkbox 61 | display: none; 62 | } 63 | 64 | /* the track appearance when the toggle is "off" */ 65 | .toggle .track { 66 | @include transition-timing-function(ease-in-out); 67 | @include transition-duration($toggle-transition-duration); 68 | @include transition-property((background-color, border)); 69 | 70 | display: inline-block; 71 | box-sizing: border-box; 72 | width: $toggle-width; 73 | height: $toggle-height; 74 | border: solid $toggle-border-width $toggle-off-border-color; 75 | border-radius: $toggle-border-radius; 76 | background-color: $toggle-off-bg-color; 77 | content: ' '; 78 | cursor: pointer; 79 | pointer-events: none; 80 | } 81 | 82 | /* Fix to avoid background color bleeding */ 83 | /* (occured on (at least) Android 4.2, Asus MeMO Pad HD7 ME173X) */ 84 | .platform-android4_2 .toggle .track { 85 | -webkit-background-clip: padding-box; 86 | } 87 | 88 | /* the handle (circle) thats inside the toggle's track area */ 89 | /* also the handle's appearance when it is "off" */ 90 | .toggle .handle { 91 | @include transition($toggle-transition-duration cubic-bezier(0, 1.1, 1, 1.1)); 92 | @include transition-property((background-color, transform)); 93 | position: absolute; 94 | display: block; 95 | width: $toggle-handle-width; 96 | height: $toggle-handle-height; 97 | border-radius: $toggle-handle-radius; 98 | background-color: $toggle-handle-off-bg-color; 99 | top: $toggle-border-width + $toggle-hit-area-expansion; 100 | left: $toggle-border-width + $toggle-hit-area-expansion; 101 | box-shadow: 0 2px 7px rgba(0,0,0,.35), 0 1px 1px rgba(0,0,0,.15); 102 | 103 | &:before { 104 | // used to create a larger (but hidden) hit area to slide the handle 105 | position: absolute; 106 | top: -4px; 107 | left: ( ($toggle-handle-width / 2) * -1) - 8; 108 | padding: ($toggle-handle-height / 2) + 5 ($toggle-handle-width + 7); 109 | content: " "; 110 | } 111 | } 112 | 113 | .toggle input:checked + .track .handle { 114 | // the handle when the toggle is "on" 115 | @include translate3d($toggle-width - $toggle-handle-width - ($toggle-border-width * 2), 0, 0); 116 | background-color: $toggle-handle-on-bg-color; 117 | } 118 | 119 | .item-toggle.active { 120 | box-shadow: none; 121 | } 122 | 123 | .item-toggle, 124 | .item-toggle.item-complex .item-content { 125 | // make sure list item content have enough padding on right to fit the toggle 126 | padding-right: ($item-padding * 3) + $toggle-width; 127 | } 128 | 129 | .item-toggle.item-complex { 130 | padding-right: 0; 131 | } 132 | 133 | .item-toggle .toggle { 134 | // position the toggle to the right within a list item 135 | position: absolute; 136 | top: ($item-padding / 2) + 2; 137 | right: $item-padding; 138 | z-index: $z-index-item-toggle; 139 | } 140 | 141 | .toggle input:disabled + .track { 142 | opacity: .6; 143 | } 144 | 145 | .toggle-small { 146 | 147 | .track { 148 | border: 0; 149 | width: 34px; 150 | height: 15px; 151 | background: #9e9e9e; 152 | } 153 | input:checked + .track { 154 | background: rgba(0,150,137,.5); 155 | } 156 | .handle { 157 | top: 2px; 158 | left: 4px; 159 | width: 21px; 160 | height: 21px; 161 | box-shadow: 0 2px 5px rgba(0,0,0,.25); 162 | } 163 | input:checked + .track .handle { 164 | @include translate3d(16px, 0, 0); 165 | background: rgb(0,150,137); 166 | } 167 | &.item-toggle .toggle { 168 | top: 19px; 169 | } 170 | 171 | .toggle-light { 172 | @include toggle-small-style($toggle-on-light-bg); 173 | } 174 | .toggle-stable { 175 | @include toggle-small-style($toggle-on-stable-bg); 176 | } 177 | .toggle-positive { 178 | @include toggle-small-style($toggle-on-positive-bg); 179 | } 180 | .toggle-calm { 181 | @include toggle-small-style($toggle-on-calm-bg); 182 | } 183 | .toggle-assertive { 184 | @include toggle-small-style($toggle-on-assertive-bg); 185 | } 186 | .toggle-balanced { 187 | @include toggle-small-style($toggle-on-balanced-bg); 188 | } 189 | .toggle-energized { 190 | @include toggle-small-style($toggle-on-energized-bg); 191 | } 192 | .toggle-royal { 193 | @include toggle-small-style($toggle-on-royal-bg); 194 | } 195 | .toggle-dark { 196 | @include toggle-small-style($toggle-on-dark-bg); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /www/swipe/td-cards-helpers/ionic.tdcards.min.js: -------------------------------------------------------------------------------- 1 | !function(t){for(var i=document.createElement("div"),n=["webkitTransformOrigin","transform-origin","-webkit-transform-origin","webkit-transform-origin","-moz-transform-origin","moz-transform-origin","MozTransformOrigin","mozTransformOrigin"],e="webkitTransformOrigin",r=0;r0?this.parentWidth/2+this.width:-(this.parentWidth+this.width) 9 | var o=e/Math.tan(r),s=(this.rotationAngle,.3-Math.min(Math.max(Math.abs(i.gesture.velocityX)/10,.05),.2)) 10 | t.requestAnimationFrame(function(){n.el.style.transform=n.el.style.webkitTransform="translate3d("+e+"px, "+o+"px,0) rotate("+n.rotationAngle+"rad)",n.el.style.transition=n.el.style.webkitTransition="all "+s+"s ease-in-out"}),setTimeout(function(){n.onDestroy&&n.onDestroy()},1e3*s)},bindEvents:function(){var i=this 11 | t.onGesture("dragstart",function(n){t.requestAnimationFrame(function(){i._doDragStart(n)})},this.el),t.onGesture("drag",function(n){t.requestAnimationFrame(function(){i._doDrag(n)}),n.gesture.srcEvent.preventDefault()},this.el),t.onGesture("dragend",function(n){t.requestAnimationFrame(function(){i._doDragEnd(n)})},this.el)},_transformOriginLeft:function(){this.el.style[e]="left center",this.rotationDirection=1},_transformOriginRight:function(){this.el.style[e]="right center",this.rotationDirection=-1},_doDragStart:function(t){t.preventDefault() 12 | var i=this.el.offsetWidth,n=window.innerWidth/2+this.rotationDirection*(i/2),e=Math.abs(n-t.gesture.touches[0].pageX) 13 | this.touchDistance=10*e},_doDrag:function(t){t.preventDefault() 14 | var i=t.gesture.deltaX/-1e3 15 | this.rotationAngle=Math.atan(i),this.x=this.startX+.8*t.gesture.deltaX,this.y=this.startY+.8*t.gesture.deltaY,this.el.style.transform=this.el.style.webkitTransform="translate3d("+this.x+"px, "+this.y+"px, 0) rotate("+(this.rotationAngle||0)+"rad)",this.thresholdAmount=this.x/(this.parentWidth/2) 16 | var n=this 17 | setTimeout(function(){n.onPartialSwipe(n.thresholdAmount)})},_doDragEnd:function(t){this.transitionOut(t)}}) 18 | angular.module("ionic.contrib.ui.tinderCards",["ionic"]).directive("tdCard",["$timeout",function(t){var i=function(t){t=Math.min(1,3*t) 19 | var i=.11,n=.67,e=.41,r=.99 20 | return Math.pow(1-t,3)*i+3*Math.pow(1-t,2)*t*n+3*(1-t)*t*t*e+Math.pow(t,3)*r} 21 | return{restrict:"E",template:'
',require:"^tdCards",transclude:!0,scope:{onSwipeLeft:"&",onSwipeRight:"&",onTransitionLeft:"&",onTransitionRight:"&",onTransitionOut:"&",onPartialSwipe:"&",onSnapBack:"&",onDestroy:"&"},compile:function(n,e){return function(n,e,r,o){var s=e[0],l=s.querySelector(".no-text"),h=s.querySelector(".yes-text") 22 | s.style.transform=s.style.webkitTransform="translate3d(0px, 0px, 0px)" 23 | var c=new a({el:s,leftText:l,rightText:h,onPartialSwipe:function(e){o.partial(e) 24 | var r=this 25 | t(function(){0>e?(r.leftText&&(r.leftText.style.opacity=i(-e)),r.rightText&&(r.rightText.style.opacity=0)):(r.leftText&&(r.leftText.style.opacity=0),r.rightText&&(r.rightText.style.opacity=i(e))),n.onPartialSwipe({amt:e})})},onSwipeRight:function(){t(function(){n.onSwipeRight()})},onSwipeLeft:function(){t(function(){n.onSwipeLeft()})},onTransitionRight:function(){t(function(){n.onTransitionRight()})},onTransitionLeft:function(){t(function(){n.onTransitionLeft()})},onTransitionOut:function(i){0>i?c.onTransitionLeft():c.onTransitionRight(),t(function(){n.onTransitionOut({amt:i})})},onDestroy:function(){t(function(){n.onDestroy()})},onSnapBack:function(i,e,r){{var o=s.querySelector(".yes-text"),a=s.querySelector(".no-text") 26 | collide.animation({duration:500,percent:0,reverse:!1}).easing({type:"spring",frequency:15,friction:250,initialForce:!1}).on("step",function(t){s.style.transform=s.style.webkitTransform="translate3d("+(i-i*t)+"px, "+(e-e*t)+"px, 0) rotate("+(r-r*t)+"rad)",a&&(a.style.opacity=0),o&&(o.style.opacity=0)}).start()}t(function(){n.onSnapBack()})}}) 27 | n.$parent.swipeCard=c}}}}]).directive("tdCards",["$rootScope","$timeout",function(t,i){return{restrict:"E",template:'
',transclude:!0,scope:{},controller:["$scope","$element",function(t,n){var e,r,o,s,a,l,h,c=function(){for(a=n[0].querySelectorAll("td-card"),h=0;h2&&e[1],s=e.length>3&&e[2],o&&u(o,t,4),s&&u(s,t,8)}}]}}]).factory("TDCardDelegate",["$rootScope",function(t){return{popCard:function(i,n){t.$emit("tdCard.pop",n)},getSwipeableCard:function(t){return t.swipeCard}}}])}(window.ionic) 31 | -------------------------------------------------------------------------------- /www/profiles/profile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 |
7 | 8 | 9 |
10 |
11 | 12 | 13 | 14 |
  • 15 |
    16 | 17 |
    18 |
  • 19 | 20 | 21 | 22 | 23 |
    25 |
    26 | Ideal Rent:
    {{profile.location.desiredPlace.rent | currency}} 27 |
    28 |
    29 | Rent:
    {{profile.location.myPlace.rent | currency}} 30 |
    31 | 32 | 33 |
  • 34 |
    35 | Location:
    {{profile.location.desiredPlace.city}}, {{profile.location.desiredPlace.state}} 36 |
    37 |
    38 | Location:
    {{profile.location.myPlace.city}}, {{profile.location.myPlace.state}} 39 |
    40 |
  • 41 | 42 |
  • 43 |
    44 | Choice Icons:
    45 |
    46 |
    47 |
  • 48 | 49 |
  • 50 |
    51 | Preferred Roommate Gender:
    {{profile.roommatePreferences.gender}} 52 |
    53 |
  • 54 | 55 |
  • 56 |
    57 | A few words of description:
    {{keywords}} 58 |
    59 |
  • 60 |
    61 | 62 | 63 | 64 | 65 |
    66 |
  • 67 |
    68 | Ideal Rent:
    {{profile.location.desiredPlace.rent | currency}} 69 |
    70 |
  • 71 | 72 |
  • 73 |
    74 | Location:
    {{profile.location.desiredPlace.city}}, {{profile.location.desiredPlace.state}} 75 |
    76 |
  • 77 | 78 | 79 | 80 |
  • 81 |
    82 | Choice Icons:
    83 |
    84 |
    85 |
  • 86 | 87 |
  • 88 |
    89 | Preferred Roommate Gender:
    {{profile.roommatePreferences.gender}} 90 |
    91 |
  • 92 | 93 |
  • 94 |
    95 |
    96 |
  • 97 |
    98 | 99 | 100 | 101 | 102 |
    103 |

    I have a place, I just need a Roommate 104 |
  • 105 |
    106 | Rent:
    {{profile.location.myPlace.rent | currency}} 107 |
    108 |
  • 109 | 110 |
  • 111 |
    112 | Location:
    {{profile.location.myPlace.city}}, {{profile.location.myPlace.state}} 113 |
    114 |
  • 115 | 116 |
  • 117 |
    118 | Choice Ionicons:
    119 |
    120 |
    121 |
  • 122 | 123 |
  • 124 |
    125 | Preferred Roommate Gender:
    {{profile.roommatePreferences.gender}} 126 |
    127 |
  • 128 | 129 |
  • 130 |
    131 | A few words of description:
    {{keyword}}
    132 |
    133 |
  • 134 |

    135 | 136 | 137 |
  • 138 |
    139 | 140 |
    141 |
  • 142 | 143 |
    144 |
    145 |
    -------------------------------------------------------------------------------- /www/lib/ionic/scss/_util.scss: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Utility Classes 4 | * -------------------------------------------------- 5 | */ 6 | 7 | .hide { 8 | display: none; 9 | } 10 | .opacity-hide { 11 | opacity: 0; 12 | } 13 | .grade-b .opacity-hide, 14 | .grade-c .opacity-hide { 15 | opacity: 1; 16 | display: none; 17 | } 18 | .show { 19 | display: block; 20 | } 21 | .opacity-show { 22 | opacity: 1; 23 | } 24 | .invisible { 25 | visibility: hidden; 26 | } 27 | 28 | .keyboard-open .hide-on-keyboard-open { 29 | display: none; 30 | } 31 | 32 | .keyboard-open .tabs.hide-on-keyboard-open + .pane .has-tabs, 33 | .keyboard-open .bar-footer.hide-on-keyboard-open + .pane .has-footer { 34 | bottom: 0; 35 | } 36 | 37 | .inline { 38 | display: inline-block; 39 | } 40 | 41 | .disable-pointer-events { 42 | pointer-events: none; 43 | } 44 | 45 | .enable-pointer-events { 46 | pointer-events: auto; 47 | } 48 | 49 | .disable-user-behavior { 50 | // used to prevent the browser from doing its native behavior. this doesnt 51 | // prevent the scrolling, but cancels the contextmenu, tap highlighting, etc 52 | 53 | @include user-select(none); 54 | @include touch-callout(none); 55 | @include tap-highlight-transparent(); 56 | 57 | -webkit-user-drag: none; 58 | 59 | -ms-touch-action: none; 60 | -ms-content-zooming: none; 61 | } 62 | 63 | // Fill the screen to block clicks (a better pointer-events: none) for the body 64 | // to avoid full-page reflows and paints which can cause flickers 65 | .click-block { 66 | position: absolute; 67 | top: 0; 68 | right: 0; 69 | bottom: 0; 70 | left: 0; 71 | opacity: 0; 72 | z-index: $z-index-click-block; 73 | @include translate3d(0, 0, 0); 74 | overflow: hidden; 75 | } 76 | .click-block-hide { 77 | @include translate3d(-9999px, 0, 0); 78 | } 79 | 80 | .no-resize { 81 | resize: none; 82 | } 83 | 84 | .block { 85 | display: block; 86 | clear: both; 87 | &:after { 88 | display: block; 89 | visibility: hidden; 90 | clear: both; 91 | height: 0; 92 | content: "."; 93 | } 94 | } 95 | 96 | .full-image { 97 | width: 100%; 98 | } 99 | 100 | .clearfix { 101 | *zoom: 1; 102 | &:before, 103 | &:after { 104 | display: table; 105 | content: ""; 106 | // Fixes Opera/contenteditable bug: 107 | // http://nicolasgallagher.com/micro-clearfix-hack/#comment-36952 108 | line-height: 0; 109 | } 110 | &:after { 111 | clear: both; 112 | } 113 | } 114 | 115 | /** 116 | * Content Padding 117 | * -------------------------------------------------- 118 | */ 119 | 120 | .padding { 121 | padding: $content-padding; 122 | } 123 | 124 | .padding-top, 125 | .padding-vertical { 126 | padding-top: $content-padding; 127 | } 128 | 129 | .padding-right, 130 | .padding-horizontal { 131 | padding-right: $content-padding; 132 | } 133 | 134 | .padding-bottom, 135 | .padding-vertical { 136 | padding-bottom: $content-padding; 137 | } 138 | 139 | .padding-left, 140 | .padding-horizontal { 141 | padding-left: $content-padding; 142 | } 143 | 144 | 145 | /** 146 | * Scrollable iFrames 147 | * -------------------------------------------------- 148 | */ 149 | 150 | .iframe-wrapper { 151 | position: fixed; 152 | -webkit-overflow-scrolling: touch; 153 | overflow: scroll; 154 | 155 | iframe { 156 | height: 100%; 157 | width: 100%; 158 | } 159 | } 160 | 161 | 162 | /** 163 | * Rounded 164 | * -------------------------------------------------- 165 | */ 166 | 167 | .rounded { 168 | border-radius: $border-radius-base; 169 | } 170 | 171 | 172 | /** 173 | * Utility Colors 174 | * -------------------------------------------------- 175 | * Utility colors are added to help set a naming convention. You'll 176 | * notice we purposely do not use words like "red" or "blue", but 177 | * instead have colors which represent an emotion or generic theme. 178 | */ 179 | 180 | .light, a.light { 181 | color: $light; 182 | } 183 | .light-bg { 184 | background-color: $light; 185 | } 186 | .light-border { 187 | border-color: $button-light-border; 188 | } 189 | 190 | .stable, a.stable { 191 | color: $stable; 192 | } 193 | .stable-bg { 194 | background-color: $stable; 195 | } 196 | .stable-border { 197 | border-color: $button-stable-border; 198 | } 199 | 200 | .positive, a.positive { 201 | color: $positive; 202 | } 203 | .positive-bg { 204 | background-color: $positive; 205 | } 206 | .positive-border { 207 | border-color: $button-positive-border; 208 | } 209 | 210 | .calm, a.calm { 211 | color: $calm; 212 | } 213 | .calm-bg { 214 | background-color: $calm; 215 | } 216 | .calm-border { 217 | border-color: $button-calm-border; 218 | } 219 | 220 | .assertive, a.assertive { 221 | color: $assertive; 222 | } 223 | .assertive-bg { 224 | background-color: $assertive; 225 | } 226 | .assertive-border { 227 | border-color: $button-assertive-border; 228 | } 229 | 230 | .balanced, a.balanced { 231 | color: $balanced; 232 | } 233 | .balanced-bg { 234 | background-color: $balanced; 235 | } 236 | .balanced-border { 237 | border-color: $button-balanced-border; 238 | } 239 | 240 | .energized, a.energized { 241 | color: $energized; 242 | } 243 | .energized-bg { 244 | background-color: $energized; 245 | } 246 | .energized-border { 247 | border-color: $button-energized-border; 248 | } 249 | 250 | .royal, a.royal { 251 | color: $royal; 252 | } 253 | .royal-bg { 254 | background-color: $royal; 255 | } 256 | .royal-border { 257 | border-color: $button-royal-border; 258 | } 259 | 260 | .dark, a.dark { 261 | color: $dark; 262 | } 263 | .dark-bg { 264 | background-color: $dark; 265 | } 266 | .dark-border { 267 | border-color: $button-dark-border; 268 | } 269 | 270 | [collection-repeat] { 271 | /* Position is set by transforms */ 272 | left: 0 !important; 273 | top: 0 !important; 274 | position: absolute !important; 275 | z-index: 1; 276 | } 277 | .collection-repeat-container { 278 | position: relative; 279 | z-index: 1; //make sure it's above the after-container 280 | } 281 | .collection-repeat-after-container { 282 | z-index: 0; 283 | display: block; 284 | 285 | /* when scrolling horizontally, make sure the after container doesn't take up 100% width */ 286 | &.horizontal { 287 | display: inline-block; 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /www/app.js: -------------------------------------------------------------------------------- 1 | // Ionic Starter App 2 | 3 | // angular.module is a global place for creating, registering and retrieving Angular modules 4 | // 'starter' is the name of this angular module example (also set in a attribute in index.html) 5 | // the 2nd parameter is an array of 'requires' 6 | // 'starter.services' is found in services.js 7 | // 'starter.controllers' is found in controllers.js 8 | angular.module('starter', [ 9 | 'ionic', 10 | 'ngResource', 11 | 'firebase', 12 | 'login.controllers', 13 | 'login.services', 14 | 'matches.controllers', 15 | 'profile.controllers', 16 | 'profile.directives', 17 | 'userProfile.controllers', 18 | 'preferences.controllers', 19 | 'people.controllers', 20 | 'swipe.controllers', 21 | 'data', 22 | 'map.controllers', 23 | 'map.services', 24 | 'account.controllers', 25 | 'chats.services', 26 | 'chats.controllers', 27 | 'userProfile.services', 28 | 'ionic.contrib.ui.tinderCards' 29 | ]) 30 | 31 | .filter('range', function() { 32 | return function(input, start, end) { 33 | start = parseInt(start); 34 | end = parseInt(end); 35 | var direction = (start <= end) ? 1 : -1; 36 | while (start != end) { 37 | input.push(start); 38 | start += direction; 39 | } 40 | return input; 41 | }; 42 | }) 43 | 44 | .run(function($ionicPlatform, $rootScope, $state, userSession, CandidatesFactory, MatchesFactory) { 45 | $ionicPlatform.ready(function() { 46 | // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard 47 | // for form inputs) 48 | if (window.cordova && window.cordova.plugins.Keyboard) { 49 | cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true); 50 | } 51 | if (window.StatusBar) { 52 | // org.apache.cordova.statusbar required 53 | StatusBar.styleDefault(); 54 | } 55 | }); 56 | 57 | $rootScope.$on('userRetrieved', function(event, data){ 58 | CandidatesFactory.initialize(data.data); 59 | MatchesFactory.initialize(data.data); 60 | }); 61 | 62 | }) 63 | 64 | .config(function($stateProvider, $urlRouterProvider, $httpProvider) { 65 | // Ionic uses AngularUI Router which uses the concept of states 66 | // Learn more here: https://github.com/angular-ui/ui-router 67 | // Set up the various states which the app can be in. 68 | // Each state's controller can be found in controllers.js 69 | $stateProvider 70 | // setup an abstract state for the tabs directive 71 | .state('tab', { 72 | url: '/tab', 73 | abstract: true, 74 | templateUrl: 'tabs.html', 75 | // Sets the User object to the user data firebase returns from fb 76 | // To access this data, simply inject User into the controller 77 | resolve: { 78 | User: function(userSession, LoginFact){ 79 | console.log('userSession: ', userSession); 80 | var user = userSession.user; 81 | 82 | return LoginFact.saveUser(user) 83 | .then(function(data){ 84 | console.log('DATA for resolved USER', data); 85 | return data.data; 86 | }) 87 | } 88 | } 89 | }) 90 | // Each tab has its own nav history stack: 91 | .state('tab.swipe', { 92 | url: '/swipe', 93 | views: { 94 | 'tab-swipe': { 95 | templateUrl: 'swipe/swipe.html', 96 | controller: 'SwipeController' 97 | } 98 | } 99 | }) 100 | 101 | .state('tab.swipe-detail', { 102 | url: '/:type/profiles/:id', 103 | views: { 104 | 'tab-swipe': { 105 | templateUrl: 'profiles/profile.html', 106 | controller: 'ProfileCtrl' 107 | } 108 | } 109 | }) 110 | 111 | .state('tab.account', { 112 | url: '/account', 113 | views: { 114 | 'tab-account': { 115 | templateUrl: 'account/account.html', 116 | controller: 'AccountCtrl' 117 | } 118 | } 119 | }) 120 | 121 | .state('tab.account-profile', { 122 | url: '/account/profile', 123 | views: { 124 | 'tab-account': { 125 | templateUrl: 'profiles/profile.html', 126 | controller: 'ProfileCtrl' 127 | } 128 | } 129 | }) 130 | 131 | .state('tab.account-place', { 132 | url: '/account/place', 133 | views: { 134 | 'tab-account': { 135 | templateUrl: 'account/place/place.html', 136 | controller: 'PlaceCtrl' 137 | } 138 | } 139 | }) 140 | 141 | .state('tab.account-people', { 142 | url: '/account/people', 143 | views: { 144 | 'tab-account': { 145 | templateUrl: 'account/people/people.html', 146 | controller: 'PeopleCtrl' 147 | } 148 | } 149 | }) 150 | 151 | .state('tab.account-editProfile', { 152 | url: '/account/profile/edit', 153 | views: { 154 | 'tab-account': { 155 | templateUrl: 'account/userProfile/userProfile.html', 156 | controller: 'UserProfileCtrl' 157 | } 158 | } 159 | }) 160 | 161 | .state('tab.account-map', { 162 | url: '/account/map', 163 | views: { 164 | 'tab-account': { 165 | templateUrl: 'map/map.html', 166 | controller: 'MapCtrl' 167 | } 168 | } 169 | }) 170 | 171 | // all matches state view 172 | .state('tab.matches', { 173 | url: '/matches', 174 | views: { 175 | 'tab-matches': { 176 | templateUrl: 'matches/matches.html', 177 | controller: 'MatchesCtrl' 178 | } 179 | } 180 | }) 181 | 182 | .state('tab.matches-detail', { 183 | url: '/matches/profiles/:type/:id', 184 | views: { 185 | 'tab-matches': { 186 | templateUrl: 'profiles/profile.html', 187 | controller: 'ProfileCtrl' 188 | } 189 | } 190 | }) 191 | 192 | .state('tab.matches-chat', { 193 | url: '/matches/chat/:matchId', 194 | views: { 195 | 'tab-matches': { 196 | templateUrl: 'chats/chats.html', 197 | controller: 'ChatsCtrl' 198 | } 199 | } 200 | }) 201 | 202 | .state('login', { 203 | url: '/login', 204 | templateUrl: 'login/login.html', 205 | controller: 'LoginCtrl' 206 | }) 207 | 208 | // if none of the above states are matched, redirect to the login tab 209 | $urlRouterProvider.otherwise('/login'); 210 | 211 | $httpProvider.defaults.useXDomain = true; 212 | delete $httpProvider.defaults.headers.common['X-Requested-With']; 213 | 214 | }); 215 | -------------------------------------------------------------------------------- /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 | -ms-content-zooming: none; 45 | } 46 | 47 | body.grade-b, 48 | body.grade-c { 49 | // disable optimizeLegibility for low end devices 50 | text-rendering: auto; 51 | } 52 | 53 | .content { 54 | // used for content areas not using the content directive 55 | position: relative; 56 | } 57 | 58 | .scroll-content { 59 | position: absolute; 60 | top: 0; 61 | right: 0; 62 | bottom: 0; 63 | left: 0; 64 | overflow: hidden; 65 | 66 | // Hide the top border if any 67 | margin-top: -1px; 68 | 69 | // Prevents any distortion of lines 70 | padding-top: 1px; 71 | margin-bottom: -1px; 72 | 73 | width: auto; 74 | height: auto; 75 | } 76 | 77 | .scroll-content-false, 78 | .menu .scroll-content.scroll-content-false{ 79 | z-index: $z-index-scroll-content-false; 80 | } 81 | 82 | .scroll-view { 83 | position: relative; 84 | display: block; 85 | overflow: hidden; 86 | 87 | // Hide the top border if any 88 | margin-top: -1px; 89 | } 90 | 91 | /** 92 | * Scroll is the scroll view component available for complex and custom 93 | * scroll view functionality. 94 | */ 95 | .scroll { 96 | @include user-select(none); 97 | @include touch-callout(none); 98 | @include text-size-adjust(none); 99 | @include transform-origin(left, top); 100 | } 101 | 102 | // Scroll bar styles 103 | .scroll-bar { 104 | position: absolute; 105 | z-index: $z-index-scroll-bar; 106 | } 107 | // hide the scroll-bar during animations 108 | .ng-animate .scroll-bar { 109 | visibility: hidden; 110 | } 111 | .scroll-bar-h { 112 | right: 2px; 113 | bottom: 3px; 114 | left: 2px; 115 | height: 3px; 116 | 117 | .scroll-bar-indicator { 118 | height: 100%; 119 | } 120 | } 121 | 122 | .scroll-bar-v { 123 | top: 2px; 124 | right: 3px; 125 | bottom: 2px; 126 | width: 3px; 127 | 128 | .scroll-bar-indicator { 129 | width: 100%; 130 | } 131 | } 132 | .scroll-bar-indicator { 133 | position: absolute; 134 | border-radius: 4px; 135 | background: rgba(0,0,0,0.3); 136 | opacity: 1; 137 | @include transition(opacity .3s linear); 138 | 139 | &.scroll-bar-fade-out { 140 | opacity: 0; 141 | } 142 | } 143 | .platform-android .scroll-bar-indicator { 144 | // android doesn't have rounded ends on scrollbar 145 | border-radius: 0; 146 | } 147 | .grade-b .scroll-bar-indicator, 148 | .grade-c .scroll-bar-indicator { 149 | // disable rgba background and border radius for low end devices 150 | background: #aaa; 151 | 152 | &.scroll-bar-fade-out { 153 | @include transition(none); 154 | } 155 | } 156 | 157 | ion-infinite-scroll { 158 | height: 60px; 159 | width: 100%; 160 | display: block; 161 | 162 | @include display-flex(); 163 | @include flex-direction(row); 164 | @include justify-content(center); 165 | @include align-items(center); 166 | 167 | .icon { 168 | color: #666666; 169 | font-size: 30px; 170 | color: $scroll-refresh-icon-color; 171 | } 172 | .icon:before, 173 | .spinner{ 174 | -webkit-transform: translate3d(0,0,0); 175 | transform: translate3d(0,0,0); 176 | } 177 | &:not(.active){ 178 | .spinner, 179 | .icon:before{ 180 | -webkit-transform: translate3d(-1000px,0,0); 181 | transform: translate3d(-1000px,0,0); 182 | } 183 | } 184 | } 185 | 186 | .overflow-scroll { 187 | overflow-x: hidden; 188 | overflow-y: scroll; 189 | -webkit-overflow-scrolling: touch; 190 | top: 0; 191 | right: 0; 192 | bottom: 0; 193 | left: 0; 194 | position: absolute; 195 | 196 | .scroll { 197 | position: static; 198 | height: 100%; 199 | -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 200 | } 201 | } 202 | 203 | 204 | // Pad top/bottom of content so it doesn't hide behind .bar-title and .bar-tab. 205 | // Note: For these to work, content must come after both bars in the markup 206 | /* If you change these, change platform.scss as well */ 207 | .has-header { 208 | top: $bar-height; 209 | } 210 | // Force no header 211 | .no-header { 212 | top: 0; 213 | } 214 | 215 | .has-subheader { 216 | top: $bar-height + $bar-subheader-height; 217 | } 218 | .has-tabs-top { 219 | top: $bar-height + $tabs-height; 220 | } 221 | .has-header.has-subheader.has-tabs-top { 222 | top: $bar-height + $bar-subheader-height + $tabs-height; 223 | } 224 | 225 | .has-footer { 226 | bottom: $bar-footer-height; 227 | } 228 | .has-subfooter { 229 | bottom: $bar-footer-height + $bar-subfooter-height; 230 | } 231 | 232 | .has-tabs, 233 | .bar-footer.has-tabs { 234 | bottom: $tabs-height; 235 | &.pane{ 236 | bottom: $tabs-height; 237 | height:auto; 238 | } 239 | } 240 | 241 | .has-footer.has-tabs { 242 | bottom: $tabs-height + $bar-footer-height; 243 | } 244 | 245 | // A full screen section with a solid background 246 | .pane { 247 | @include translate3d(0,0,0); 248 | @include transition-duration(0); 249 | z-index: $z-index-pane; 250 | } 251 | .view { 252 | z-index: $z-index-view; 253 | } 254 | .pane, 255 | .view { 256 | position: absolute; 257 | top: 0; 258 | right: 0; 259 | bottom: 0; 260 | left: 0; 261 | width: 100%; 262 | height: 100%; 263 | background-color: $base-background-color; 264 | overflow: hidden; 265 | } 266 | .view-container { 267 | position: absolute; 268 | display: block; 269 | width: 100%; 270 | height: 100%; 271 | } 272 | -------------------------------------------------------------------------------- /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, 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 | display: table; 114 | padding: 7px 10px 7px 0px; 115 | max-width: 200px; 116 | width: 35%; 117 | color: $input-label-color; 118 | font-size: 16px; 119 | } 120 | 121 | .placeholder-icon { 122 | color: #aaa; 123 | &:first-child { 124 | padding-right: 6px; 125 | } 126 | &:last-child { 127 | padding-left: 6px; 128 | } 129 | } 130 | 131 | .item-stacked-label { 132 | display: block; 133 | background-color: transparent; 134 | box-shadow: none; 135 | 136 | .input-label, .icon { 137 | display: inline-block; 138 | padding: 4px 0 0 0px; 139 | vertical-align: middle; 140 | } 141 | } 142 | 143 | .item-stacked-label input, 144 | .item-stacked-label textarea { 145 | @include border-radius(2px); 146 | padding: 4px 8px 3px 0; 147 | border: none; 148 | background-color: $input-bg; 149 | } 150 | .item-stacked-label input { 151 | overflow: hidden; 152 | height: $line-height-computed + $font-size-base + 12px; 153 | } 154 | 155 | .item-floating-label { 156 | display: block; 157 | background-color: transparent; 158 | box-shadow: none; 159 | 160 | .input-label { 161 | position: relative; 162 | padding: 5px 0 0 0; 163 | opacity: 0; 164 | top: 10px; 165 | @include transition(opacity .15s ease-in, top .2s linear); 166 | 167 | &.has-input { 168 | opacity: 1; 169 | top: 0; 170 | @include transition(opacity .15s ease-in, top .2s linear); 171 | } 172 | } 173 | } 174 | 175 | 176 | // Form Controls 177 | // ------------------------------- 178 | 179 | // Shared size and type resets 180 | textarea, 181 | input[type="text"], 182 | input[type="password"], 183 | input[type="datetime"], 184 | input[type="datetime-local"], 185 | input[type="date"], 186 | input[type="month"], 187 | input[type="time"], 188 | input[type="week"], 189 | input[type="number"], 190 | input[type="email"], 191 | input[type="url"], 192 | input[type="search"], 193 | input[type="tel"], 194 | input[type="color"] { 195 | display: block; 196 | padding-top: 2px; 197 | padding-left: 0; 198 | height: $line-height-computed + $font-size-base; 199 | color: $input-color; 200 | vertical-align: middle; 201 | font-size: $font-size-base; 202 | line-height: $font-size-base + 2; 203 | } 204 | 205 | .platform-ios, 206 | .platform-android { 207 | input[type="datetime-local"], 208 | input[type="date"], 209 | input[type="month"], 210 | input[type="time"], 211 | input[type="week"] { 212 | padding-top: 8px; 213 | } 214 | } 215 | 216 | .item-input { 217 | input, 218 | textarea { 219 | width: 100%; 220 | } 221 | } 222 | 223 | textarea { 224 | padding-left: 0; 225 | @include placeholder($input-color-placeholder, -3px); 226 | } 227 | 228 | // Reset height since textareas have rows 229 | textarea { 230 | height: auto; 231 | } 232 | 233 | // Everything else 234 | textarea, 235 | input[type="text"], 236 | input[type="password"], 237 | input[type="datetime"], 238 | input[type="datetime-local"], 239 | input[type="date"], 240 | input[type="month"], 241 | input[type="time"], 242 | input[type="week"], 243 | input[type="number"], 244 | input[type="email"], 245 | input[type="url"], 246 | input[type="search"], 247 | input[type="tel"], 248 | input[type="color"] { 249 | border: 0; 250 | } 251 | 252 | // Position radios and checkboxes better 253 | input[type="radio"], 254 | input[type="checkbox"] { 255 | margin: 0; 256 | line-height: normal; 257 | } 258 | 259 | // Reset width of input images, buttons, radios, checkboxes 260 | .item-input { 261 | input[type="file"], 262 | input[type="image"], 263 | input[type="submit"], 264 | input[type="reset"], 265 | input[type="button"], 266 | input[type="radio"], 267 | input[type="checkbox"] { 268 | width: auto; // Override of generic input selector 269 | } 270 | } 271 | 272 | // Set the height of file to match text inputs 273 | input[type="file"] { 274 | line-height: $input-height-base; 275 | } 276 | 277 | // Text input classes to hide text caret during scroll 278 | .previous-input-focus, 279 | .cloned-text-input + input, 280 | .cloned-text-input + textarea { 281 | position: absolute !important; 282 | left: -9999px; 283 | width: 200px; 284 | } 285 | 286 | 287 | // Placeholder 288 | // ------------------------------- 289 | input, 290 | textarea { 291 | @include placeholder(); 292 | } 293 | 294 | 295 | // DISABLED STATE 296 | // ------------------------------- 297 | 298 | // Disabled and read-only inputs 299 | input[disabled], 300 | select[disabled], 301 | textarea[disabled], 302 | input[readonly]:not(.cloned-text-input), 303 | textarea[readonly]:not(.cloned-text-input), 304 | select[readonly] { 305 | background-color: $input-bg-disabled; 306 | cursor: not-allowed; 307 | } 308 | // Explicitly reset the colors here 309 | input[type="radio"][disabled], 310 | input[type="checkbox"][disabled], 311 | input[type="radio"][readonly], 312 | input[type="checkbox"][readonly] { 313 | background-color: transparent; 314 | } 315 | --------------------------------------------------------------------------------