Commit 1653dec57735009838ca570c792d6fa1384e47fc

Authored by Andrew Buss
1 parent edf69c1074

Happy cleanup and refactor of card deck

Showing 8 changed files with 62 additions and 96 deletions Inline Diff

angular.module('flashy', [ 1 1 angular.module('flashy', [
'flashy.LoginController', 2 2 'flashy.LoginController',
'flashy.RootController', 3 3 'flashy.RootController',
'flashy.FeedController', 4 4 'flashy.FeedController',
'flashy.DeckController', 5 5 'flashy.DeckController',
'flashy.ClassAddController', 6 6 'flashy.ClassAddController',
'flashy.ClassDropController', 7 7 'flashy.ClassDropController',
'flashy.RequestResetController', 8 8 'flashy.RequestResetController',
'flashy.StudyController', 9 9 'flashy.StudyController',
'flashy.UserService', 10 10 'flashy.UserService',
'flashy.FlashcardDirective', 11 11 'flashy.FlashcardDirective',
//'flashy.SelectDirective', 12
// DOESNT WORK RN 13
'flashy.ResetPasswordController', 14 12 'flashy.ResetPasswordController',
'flashy.VerifyEmailController', 15 13 'flashy.VerifyEmailController',
'flashy.CardListController', 16 14 'flashy.CardListController',
'flashy.HelpController', 17 15 'flashy.HelpController',
'flashy.SettingsController', 18 16 'flashy.SettingsController',
'ngCookies']). 19 17 'ngCookies']).
config(function($stateProvider, $urlRouterProvider, $resourceProvider, $httpProvider, $locationProvider) { 20 18 config(function($stateProvider, $urlRouterProvider, $resourceProvider, $httpProvider, $locationProvider) {
'use strict'; 21 19 'use strict';
$httpProvider.defaults.withCredentials = true; 22 20 $httpProvider.defaults.withCredentials = true;
$httpProvider.defaults.xsrfCookieName = 'csrftoken'; 23 21 $httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'; 24 22 $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
$resourceProvider.defaults.stripTrailingSlashes = false; 25 23 $resourceProvider.defaults.stripTrailingSlashes = false;
var arrayMethods = Object.getOwnPropertyNames(Array.prototype); 26 24 var arrayMethods = Object.getOwnPropertyNames(Array.prototype);
arrayMethods.forEach(attachArrayMethodsToNodeList); 27 25 arrayMethods.forEach(attachArrayMethodsToNodeList);
function attachArrayMethodsToNodeList(methodName) { 28 26 function attachArrayMethodsToNodeList(methodName) {
if (methodName !== 'length') { 29 27 if (methodName !== 'length') {
NodeList.prototype[methodName] = Array.prototype[methodName]; 30 28 NodeList.prototype[methodName] = Array.prototype[methodName];
} 31 29 }
} 32 30 }
33 31
$httpProvider.interceptors.push(function($q, $rootScope) { 34 32 $httpProvider.interceptors.push(function($q, $rootScope) {
return { 35 33 return {
'responseError': function(rejection) { // need a better redirect 36 34 'responseError': function(rejection) { // need a better redirect
if (rejection.status >= 500) { 37 35 if (rejection.status >= 500) {
console.log('got error'); 38 36 console.log('got error');
console.log(rejection); 39 37 console.log(rejection);
$rootScope.$broadcast('server_error', rejection); 40 38 $rootScope.$broadcast('server_error', rejection);
} 41 39 }
if (rejection.status == 403) { 42 40 if (rejection.status == 403) {
console.log(rejection); 43 41 console.log(rejection);
if (rejection.data && rejection.data.detail == 'Please verify your email before continuing') { 44 42 if (rejection.data && rejection.data.detail == 'Please verify your email before continuing') {
UserService.showLockedMessage(); 45 43 UserService.showLockedMessage();
UserService.logout(); 46 44 UserService.logout();
} 47 45 }
} 48 46 }
return $q.reject(rejection); 49 47 return $q.reject(rejection);
} 50 48 }
}; 51 49 };
}); 52 50 });
$locationProvider.html5Mode(true); 53 51 $locationProvider.html5Mode(true);
$urlRouterProvider.otherwise('/404'); 54 52 $urlRouterProvider.otherwise('/404');
var auth_resolve = { 55 53 var auth_resolve = {
authorize: function($q, $rootScope, $state, $stateParams, UserService) { 56 54 authorize: function($q, $rootScope, $state, $stateParams, UserService) {
57 55
console.log('do we need to authorize a user for', $rootScope.nextState.name); 58 56 console.log('do we need to authorize a user for', $rootScope.nextState.name);
if (UserService.noAuthRequired($rootScope.nextState)) { 59 57 if (UserService.noAuthRequired($rootScope.nextState)) {
console.log('no auth required for', $rootScope.nextState.name); 60 58 console.log('no auth required for', $rootScope.nextState.name);
return UserService.getUserData(); 61 59 return UserService.getUserData();
} 62 60 }
console.log('resolving user before continuing to ' + $rootScope.nextState.name); 63 61 console.log('resolving user before continuing to ' + $rootScope.nextState.name);
var redirectAsNeeded = function() { 64 62 var redirectAsNeeded = function() {
if (!UserService.isLoggedIn()) { 65 63 if (!UserService.isLoggedIn()) {
console.log(UserService.getUserData()); 66 64 console.log(UserService.getUserData());
console.log('making the user log in'); 67 65 console.log('making the user log in');
$state.go('login'); 68 66 $state.go('login');
} 69 67 }
if (!UserService.authorizedFor($rootScope.nextState, $rootScope.nextStateParams)) { 70 68 if (!UserService.authorizedFor($rootScope.nextState, $rootScope.nextStateParams)) {
console.log('user not authorized for ' + $rootScope.nextState.name); 71 69 console.log('user not authorized for ' + $rootScope.nextState.name);
$state.go('addclass'); 72 70 $state.go('addclass');
} 73 71 }
}; 74 72 };
if (UserService.isResolved()) return redirectAsNeeded(); 75 73 if (UserService.isResolved()) return redirectAsNeeded();
return UserService.getUserData().then(redirectAsNeeded); 76 74 return UserService.getUserData().then(redirectAsNeeded);
} 77 75 }
}; 78 76 };
$stateProvider. 79 77 $stateProvider.
state('login', { 80 78 state('login', {
resolve: auth_resolve, 81 79 resolve: auth_resolve,
url: '/login', 82 80 url: '/login',
templateUrl: 'templates/login.html', 83 81 templateUrl: 'templates/login.html',
controller: 'LoginController' 84 82 controller: 'LoginController'
}). 85 83 }).
state('root', { 86 84 state('root', {
resolve: auth_resolve, 87 85 resolve: auth_resolve,
url: '', 88 86 url: '',
controller: 'RootController' 89 87 controller: 'RootController'
}). 90 88 }).
state('feed', { 91 89 state('feed', {
resolve: auth_resolve, 92 90 resolve: auth_resolve,
url: '/feed/{sectionId}', 93 91 url: '/feed/{sectionId}',
templateUrl: 'templates/feed.html', 94 92 templateUrl: 'templates/feed.html',
controller: 'FeedController' 95 93 controller: 'FeedController'
}). 96 94 }).
state('cardlist', { 97 95 state('cardlist', {
resolve: auth_resolve, 98 96 resolve: auth_resolve,
url: '/cards/{sectionId}', 99 97 url: '/cards/{sectionId}',
templateUrl: 'templates/cardlist.html', 100 98 templateUrl: 'templates/cardlist.html',
controller: 'CardListController' 101 99 controller: 'CardListController'
}). 102 100 }).
state('addclass', { 103 101 state('addclass', {
resolve: auth_resolve, 104 102 resolve: auth_resolve,
url: '/addclass', 105 103 url: '/addclass',
templateUrl: 'templates/addclass.html', 106 104 templateUrl: 'templates/addclass.html',
controller: 'ClassAddController' 107 105 controller: 'ClassAddController'
}). 108 106 }).
state('dropclass', { 109 107 state('dropclass', {
resolve: auth_resolve, 110 108 resolve: auth_resolve,
url: '/settings/dropclass', 111 109 url: '/settings/dropclass',
templateUrl: 'templates/dropclass.html', 112 110 templateUrl: 'templates/dropclass.html',
controller: 'ClassDropController' 113 111 controller: 'ClassDropController'
}). 114 112 }).
state('deck', { 115 113 state('deck', {
resolve: auth_resolve, 116 114 resolve: auth_resolve,
url: '/deck/{sectionId}', 117 115 url: '/deck/{sectionId}',
templateUrl: 'templates/deck.html', 118 116 templateUrl: 'templates/deck.html',
controller: 'DeckController' 119 117 controller: 'DeckController'
}). 120 118 }).
state('study', { 121 119 state('study', {
resolve: auth_resolve, 122 120 resolve: auth_resolve,
url: '/study', 123 121 url: '/study',
templateUrl: 'templates/study.html', 124 122 templateUrl: 'templates/study.html',
controller: 'StudyController' 125 123 controller: 'StudyController'
}). 126 124 }).
state('flashcard', { 127 125 state('flashcard', {
resolve: auth_resolve, 128 126 resolve: auth_resolve,
url: '/flashcard', 129 127 url: '/flashcard',
templateUrl: 'templates/flashcard.html', 130 128 templateUrl: 'templates/flashcard.html',
controller: 'FlashcardController' 131 129 controller: 'FlashcardController'
}). 132 130 }).
state('settings', { 133 131 state('settings', {
resolve: auth_resolve, 134 132 resolve: auth_resolve,
url: '/settings', 135 133 url: '/settings',
templateUrl: 'templates/settings.html', 136 134 templateUrl: 'templates/settings.html',
controller: 'SettingsController' 137 135 controller: 'SettingsController'
}). 138 136 }).
state('requestpasswordreset', { 139 137 state('requestpasswordreset', {
url: '/requestpasswordreset', 140 138 url: '/requestpasswordreset',
templateUrl: 'templates/requestpasswordreset.html', 141 139 templateUrl: 'templates/requestpasswordreset.html',
controller: 'RequestResetController' 142 140 controller: 'RequestResetController'
}). 143 141 }).
state('resetpassword', { 144 142 state('resetpassword', {
url: '/resetpassword/{uid}/{token}', 145 143 url: '/resetpassword/{uid}/{token}',
templateUrl: 'templates/resetpassword.html', 146 144 templateUrl: 'templates/resetpassword.html',
controller: 'ResetPasswordController' 147 145 controller: 'ResetPasswordController'
}). 148 146 }).
state('verifyemail', { 149 147 state('verifyemail', {
url: '/verifyemail/{key}', 150 148 url: '/verifyemail/{key}',
templateUrl: 'templates/verifyemail.html', 151 149 templateUrl: 'templates/verifyemail.html',
controller: 'VerifyEmailController' 152 150 controller: 'VerifyEmailController'
}). 153 151 }).
state('404', { 154 152 state('404', {
url: '/404', 155 153 url: '/404',
template: "<h1>This page doesn't exist!</h1>" 156 154 template: "<h1>This page doesn't exist!</h1>"
}). 157 155 }).
state('help', { 158 156 state('help', {
resolve: auth_resolve, 159 157 resolve: auth_resolve,
url: '/help', 160 158 url: '/help',
templateUrl: 'templates/help.html', 161 159 templateUrl: 'templates/help.html',
controller: 'HelpController' 162 160 controller: 'HelpController'
}); 163 161 });
}). 164 162 }).
run(function($rootScope, $state, $stateParams, $location, UserService) { 165 163 run(function($rootScope, $state, $stateParams, $location, UserService) {
$rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error) { 166 164 $rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error) {
console.log('failed to change state: ' + error); 167 165 console.log('failed to change state: ' + error);
$state.go('login'); 168 166 $state.go('login');
}); 169 167 });
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) { 170 168 $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
$rootScope.nextState = toState; 171 169 $rootScope.nextState = toState;
$rootScope.nextStateParams = toParams; 172 170 $rootScope.nextStateParams = toParams;
console.log('changing state to', toState); 173 171 console.log('changing state to', toState);
if (['feed', 'deck', 'cardlist'].indexOf(toState.name) >= 0) { 174 172 if (['feed', 'deck', 'cardlist'].indexOf(toState.name) >= 0) {
localStorage.setItem('last_state', toState.name); 175 173 localStorage.setItem('last_state', toState.name);
localStorage.setItem('last_state_params', JSON.stringify(toParams)); 176 174 localStorage.setItem('last_state_params', JSON.stringify(toParams));
} 177 175 }
<!DOCTYPE html> 1 1 <!DOCTYPE html>
<html ng-app="flashy"> 2 2 <html ng-app="flashy">
<base href="/app/"> 3 3 <base href="/app/">
<head> 4 4 <head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/> 5 5 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<link rel="stylesheet" 6 6 <link rel="stylesheet"
href="https://ajax.googleapis.com/ajax/libs/angular_material/0.9.0/angular-material.min.css"> 7 7 href="https://ajax.googleapis.com/ajax/libs/angular_material/0.9.0/angular-material.min.css">
<link rel="shortcut icon" href="flashy.ico"> 8 8 <link rel="shortcut icon" href="flashy.ico">
9 9
<link rel="stylesheet" href="styles/flashier.css"/> 10 10 <link rel="stylesheet" href="styles/flashier.css"/>
<link rel="stylesheet" href="styles/flashy.css"/> 11 11 <link rel="stylesheet" href="styles/flashy.css"/>
<link rel="manifest" href="manifest.json"> 12 12 <link rel="manifest" href="manifest.json">
<link 13 13 <link
href='https://fonts.googleapis.com/css?family=Satisfy|Titillium+Web:400,200,200italic,300,600,700,900,700italic,600italic,400italic,300italic' 14 14 href='https://fonts.googleapis.com/css?family=Satisfy|Titillium+Web:400,200,200italic,300,600,700,900,700italic,600italic,400italic,300italic'
rel='stylesheet' type='text/css'> 15 15 rel='stylesheet' type='text/css'>
<title>Flashy</title> 16 16 <title>Flashy</title>
</head> 17 17 </head>
<body ng-controller="RootController"> 18 18 <body ng-controller="RootController">
<header> 19 19 <header>
<nav> 20 20 <nav>
<div class="nav-wrapper"> 21 21 <div class="nav-wrapper">
<a ng-show="UserService.isLoggedIn()" href="#" data-activates="mobile-demo" 22 22 <a ng-show="UserService.isLoggedIn()" href="#" data-activates="mobile-demo"
class="left button-collapse hide-on-med-and-up"><i 23 23 class="left button-collapse hide-on-med-and-up"><i
class="mdi-navigation-menu"></i></a> 24 24 class="mdi-navigation-menu"></i></a>
<!-- User's classes dropdown --> 25 25 <!-- User's classes dropdown -->
<ul id="classDropdown" class="dropdown-content"> 26 26 <ul id="classDropdown" class="dropdown-content">
<li ui-sref-active="active" ng-repeat="section in UserService.getUserData().sections"> 27 27 <li ui-sref-active="active" ng-repeat="section in UserService.getUserData().sections">
<a ui-sref="feed({sectionId:section.id})">{{section.short_name}}</a> 28 28 <a ui-sref="feed({sectionId:section.id})">{{section.short_name}}</a>
</li> 29 29 </li>
<li class="divider"></li> 30 30 <li class="divider"></li>
<li><a ui-sref="addclass">Add Class</a></li> 31 31 <li><a ui-sref="addclass">Add Class</a></li>
</ul> 32 32 </ul>
<ul ng-show="UserService.isLoggedIn()" class="left hide-on-small-and-down"> 33 33 <ul ng-show="UserService.isLoggedIn()" class="left hide-on-small-and-down">
<li><a style="font-size:20px; font-weight:700" class="dropdown-button ng-cloak hide-on-small-and-down" 34 34 <li><a style="font-size:20px; font-weight:700" class="dropdown-button ng-cloak hide-on-small-and-down"
href="#!" 35 35 href="#!"
data-activates="classDropdown">{{currentSection.id?currentSection.short_name:"Classes"}}<i 36 36 data-activates="classDropdown">{{currentSection.id?currentSection.short_name:"Classes"}}<i
class="mdi-navigation-arrow-drop-down right"></i></a></li> 37 37 class="mdi-navigation-arrow-drop-down right"></i></a></li>
<li ng-show="currentSection.id" ui-sref-active="active"><a ui-sref="feed({sectionId:currentSection.id})" 38 38 <li ng-show="currentSection.id" ui-sref-active="active"><a ui-sref="feed({sectionId:currentSection.id})"
class="tooltipped" 39 39 class="tooltipped"
data-position="bottom" 40 40 data-position="bottom"
data-delay="50" data-tooltip="Feed"><i 41 41 data-delay="50" data-tooltip="Feed"><i
class="mdi-action-view-module"></i></a></li> 42 42 class="mdi-action-view-module"></i></a></li>
<li ng-show="currentSection.id" ui-sref-active="active"><a ui-sref="deck({sectionId:currentSection.id})" 43 43 <li ng-show="currentSection.id" ui-sref-active="active"><a ui-sref="deck({sectionId:currentSection.id})"
class="tooltipped" 44 44 class="tooltipped"
data-position="bottom" 45 45 data-position="bottom"
data-delay="50" data-tooltip="Deck"><i 46 46 data-delay="50" data-tooltip="Deck"><i
class="mdi-action-view-carousel"></i></a></li> 47 47 class="mdi-action-view-carousel"></i></a></li>
<li ng-show="currentSection.id" ui-sref-active="active"><a ui-sref="cardlist({sectionId:currentSection.id})" 48 48 <li ng-show="currentSection.id" ui-sref-active="active"><a ui-sref="cardlist({sectionId:currentSection.id})"
class="tooltipped" 49 49 class="tooltipped"
data-position="bottom" 50 50 data-position="bottom"
data-delay="50" data-tooltip="Card List"><i 51 51 data-delay="50" data-tooltip="Card List"><i
class="mdi-action-view-list"></i></a></li> 52 52 class="mdi-action-view-list"></i></a></li>
</ul> 53 53 </ul>
<a href="#" class="brand-logo center">Flashy</a> 54 54 <a href="#" class="brand-logo center">Flashy</a>
55 55
<ul ng-show="UserService.isLoggedIn()" ng-cloak id="nav-mobile" class="right hide-on-small-and-down"> 56 56 <ul ng-show="UserService.isLoggedIn()" ng-cloak id="nav-mobile" class="right hide-on-small-and-down">
57 57
<li ui-sref-active="active"><a ui-sref="study" class="tooltipped" data-position="bottom" data-delay="50" 58 58 <li ui-sref-active="active"><a ui-sref="study" class="tooltipped" data-position="bottom" data-delay="50"
data-tooltip="Study"> 59 59 data-tooltip="Study">
<i class="tiny mdi-action-pageview"></i></a></li> 60 60 <i class="tiny mdi-action-pageview"></i></a></li>
61 61
<!-- Settings Dropdown --> 62 62 <!-- Settings Dropdown -->
<ul id="settingsDropdown" class="dropdown-content"> 63 63 <ul id="settingsDropdown" class="dropdown-content">
64 64
65 65
</ul> 66 66 </ul>
67 67
<li ui-sref-active="active"><a ui-sref="help"><i class="tiny mdi-action-help tooltipped" 68 68 <li ui-sref-active="active"><a ui-sref="help"><i class="tiny mdi-action-help tooltipped"
data-position="bottom" 69 69 data-position="bottom"
data-delay="50" data-tooltip="Help"></i></a></li> 70 70 data-delay="50" data-tooltip="Help"></i></a></li>
<li ui-sref-active="active"><a ui-sref="settings"><i data-position="bottom" data-delay="50" 71 71 <li ui-sref-active="active"><a ui-sref="settings"><i data-position="bottom" data-delay="50"
data-tooltip="Settings" 72 72 data-tooltip="Settings"
class="mdi-action-settings tooltipped"></i></a></li> 73 73 class="mdi-action-settings tooltipped"></i></a></li>
<li><a ng-click="logout()" ui-sref="login"><i data-position="bottom" data-delay="50" data-tooltip="Logout" 74 74 <li><a ng-click="logout()" ui-sref="login"><i data-position="bottom" data-delay="50" data-tooltip="Logout"
class="mdi-content-forward tooltipped"></i></a></li> 75 75 class="mdi-content-forward tooltipped"></i></a></li>
76 76
77 77
</ul> 78 78 </ul>
79 79
<!-- Slide-in side-nav for small screens --> 80 80 <!-- Slide-in side-nav for small screens -->
<ul ng-show="UserService.isLoggedIn()" class="side-nav" id="mobile-demo"> 81 81 <ul ng-show="UserService.isLoggedIn()" class="side-nav" id="mobile-demo">
<span ng-show="currentSection.id"> 82 82 <span ng-show="currentSection.id">
<li ui-sref-active="active"><a ui-sref="feed({sectionId:currentSection.id})" class="tooltipped"> 83 83 <li ui-sref-active="active"><a ui-sref="feed({sectionId:currentSection.id})" class="tooltipped">
<i class="mdi-action-view-module left"></i> 84 84 <i class="mdi-action-view-module left"></i>
Feed</a> 85 85 Feed</a>
</li> 86 86 </li>
<li ui-sref-active="active"><a ui-sref="deck({sectionId:currentSection.id})" class="tooltipped"> 87 87 <li ui-sref-active="active"><a ui-sref="deck({sectionId:currentSection.id})" class="tooltipped">
<i class="mdi-action-view-carousel left"> </i> 88 88 <i class="mdi-action-view-carousel left"> </i>
Deck 89 89 Deck
</a> 90 90 </a>
</li> 91 91 </li>
<li ui-sref-active="active"><a ui-sref="cardlist({sectionId:currentSection.id})" class="tooltipped"> 92 92 <li ui-sref-active="active"><a ui-sref="cardlist({sectionId:currentSection.id})" class="tooltipped">
<i class="mdi-action-view-list left"></i> 93 93 <i class="mdi-action-view-list left"></i>
Card List 94 94 Card List
</a> 95 95 </a>
</li> 96 96 </li>
<hr> 97 97 <hr>
</span> 98 98 </span>
<!-- Collapsible menu for all the User's classes --> 99 99 <!-- Collapsible menu for all the User's classes -->
<ul class="collapsible" data-collapsible="accordion"> 100 100 <ul class="collapsible" data-collapsible="accordion">
<li class="bold"> 101 101 <li class="bold">
<a class="collapsible-header black-text"> 102 102 <a class="collapsible-header black-text">
Classes 103 103 Classes
<i class="mdi-navigation-arrow-drop-down right"></i> 104 104 <i class="mdi-navigation-arrow-drop-down right"></i>
</a> 105 105 </a>
</li> 106 106 </li>
<div class="collapsible-body" style="display: block"> 107 107 <div class="collapsible-body" style="display: block">
<ul> 108 108 <ul>
<li ui-sref-active="active" ng-repeat="section in UserService.getUserData().sections"> 109 109 <li ui-sref-active="active" ng-repeat="section in UserService.getUserData().sections">
<a class="class bold" ui-sref="feed({sectionId:section.id})">{{section.short_name}}</a> 110 110 <a class="class bold" ui-sref="feed({sectionId:section.id})">{{section.short_name}}</a>
</li> 111 111 </li>
<hr> 112 112 <hr>
<li><a ui-sref="addclass"><i class="tiny mdi-content-add">Add Class</i></a></li> 113 113 <li><a ui-sref="addclass"><i class="tiny mdi-content-add">Add Class</i></a></li>
</ul> 114 114 </ul>
</div> 115 115 </div>
</ul> 116 116 </ul>
<li><a ui-sref="study">Study</a></li> 117 117 <li><a ui-sref="study">Study</a></li>
<li><a ui-sref="settings">Settings</a></li> 118 118 <li><a ui-sref="settings">Settings</a></li>
<li><a ng-click="logout()">Logout</a></li> 119 119 <li><a ng-click="logout()">Logout</a></li>
</ul> 120 120 </ul>
</div> 121 121 </div>
</nav> 122 122 </nav>
123 123
</header> 124 124 </header>
125 125
126 126
<!-- Menu Bar --> 127 127 <!-- Menu Bar -->
<main ui-view></main> 128 128 <main ui-view></main>
129 129
130 130
<!--<footer class="page-footer">--> 131 131 <!--<footer class="page-footer">-->
<!--<div class="footer-copyright">--> 132 132 <!--<div class="footer-copyright">-->
<!--<div class="container">--> 133 133 <!--<div class="container">-->
<!--&copy; 2015 Team Swag--> 134 134 <!--&copy; 2015 Team Swag-->
<!--<a class="grey-text text-lighten-4 right" id="contact" href="mailto:halp@flashy.cards">Concerns? Contact us by--> 135 135 <!--<a class="grey-text text-lighten-4 right" id="contact" href="mailto:halp@flashy.cards">Concerns? Contact us by-->
<!--email!</a>--> 136 136 <!--email!</a>-->
<!--</div>--> 137 137 <!--</div>-->
138 138
<!--</div>--> 139 139 <!--</div>-->
<!--</footer>--> 140 140 <!--</footer>-->
141 141
</body> 142 142 </body>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script> 143 143 <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.14/angular-ui-router.js"></script> 144 144 <script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.14/angular-ui-router.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-cookies.js"></script> 145 145 <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-cookies.js"></script>
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script> 146 146 <script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
<script type="text/javascript" src="scripts/materialize.js"></script> 147 147 <script type="text/javascript" src="scripts/materialize.js"></script>
<script type="text/javascript" src="scripts/jquery.collapsible.js"></script> 148 148 <script type="text/javascript" src="scripts/jquery.collapsible.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angular_material/0.9.0/angular-material.min.js"></script> 149 149 <script src="https://ajax.googleapis.com/ajax/libs/angular_material/0.9.0/angular-material.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-animate.min.js"></script> 150 150 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-aria.min.js"></script> 151 151 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-aria.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-resource.min.js"></script> 152 152 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-resource.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-sanitize.js"></script> 153 153 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-sanitize.js"></script>
<script src="https://cdn.rawgit.com/gdi2290/angular-websocket/v1.0.9/angular-websocket.min.js"></script> 154 154 <script src="https://cdn.rawgit.com/gdi2290/angular-websocket/v1.0.9/angular-websocket.min.js"></script>
<script src="https://cdn.rawgit.com/akatov/angular-contenteditable/master/angular-contenteditable.js"></script> 155 155 <script src="https://cdn.rawgit.com/akatov/angular-contenteditable/master/angular-contenteditable.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.4/angular-filter.js"></script> 156 156 <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.4/angular-filter.js"></script>
157 157
158 158
<script src="config.js"></script> 159 159 <script src="config.js"></script>
160 160
<!-- Controllers --> 161 161 <!-- Controllers -->
<script src="scripts/FeedController.js"></script> 162 162 <script src="scripts/FeedController.js"></script>
<script src="scripts/RootController.js"></script> 163 163 <script src="scripts/RootController.js"></script>
scripts/CardGridController.js View file @ 1653dec
angular.module('flashy.CardGridController', ['ui.router', 'ngAnimate', 'ngWebSocket']).controller = 1 1 angular.module('flashy.CardGridController', ['ui.router', 'ngAnimate', 'ngWebSocket']).controller =
function($scope, $rootScope, $state, $http, $window, $timeout, $stateParams, $websocket, UserService) { 2 2 function($scope, $rootScope, $state, $http, $window, $timeout, $stateParams, $websocket, UserService) {
console.log(arguments); 3
console.log($stateParams); 4
console.log(UserService); 5
$scope.cards = false; 6 3 $scope.cards = false;
$scope.cardCols = []; // organized data 7 4 $scope.cardCols = []; // organized data
$scope.numCols = 0; 8 5 $scope.numCols = 0;
$scope.cardTable = {}; // look up table of cards, {'colNum':col, 'obj':card} 9 6 $scope.cardTable = {}; // look up table of cards, {'colNum':col, 'obj':card}
$scope.sectionId = parseInt($stateParams.sectionId); 10 7 $scope.sectionId = parseInt($stateParams.sectionId);
$scope.section = $rootScope.SectionResource.get({sectionId: $scope.sectionId}); 11 8 $scope.section = $rootScope.SectionResource.get({sectionId: $scope.sectionId});
$rootScope.currentSection = $scope.section; 12 9 $rootScope.currentSection = $scope.section;
console.log('hello from cardgridcontroller'); 13
14 10
if (!UserService.isInSection($scope.sectionId)) { 15 11 if (!UserService.isInSection($scope.sectionId)) {
console.log('user is not enrolled in ' + $scope.sectionId); 16 12 console.log('user is not enrolled in ' + $scope.sectionId);
$state.go('addclass'); 17 13 $state.go('addclass');
} 18 14 }
19 15
$scope.refreshColumnWidth = function() { 20 16 $scope.refreshColumnWidth = function() {
console.log('refreshing column width'); 21
avail = $window.innerWidth - 17; 22 17 avail = $window.innerWidth - 17;
width = Math.floor(avail / Math.floor(avail / 250)); 23 18 width = Math.floor(avail / Math.floor(avail / 250));
$('.cardColumn').css({ 24 19 $('.cardColumn').css({
width: width + 'px', 25 20 width: width + 'px',
'font-size': 100 * width / 250 + '%' 26 21 'font-size': 100 * width / 250 + '%'
}); 27 22 });
$('.cardColumn .card.flashy').css({ 28 23 $('.cardColumn .card.flashy').css({
width: width - 12 + 'px', 29 24 width: width - 12 + 'px',
height: (width * 3 / 5) + 'px' 30 25 height: (width * 3 / 5) + 'px'
}); 31 26 });
}; 32 27 };
33 28
29 $scope.pullCard = function(id) {
30 if ($scope.deck_cards[id]) return console.log('Not pulling', id, "because it's already in deck");
31 $http.post('/api/flashcards/' + id + '/pull/').
32 success(function(data) {
33 console.log('pulled flashcard #' + id);
34 }).
35 error(function(data) {
36 console.log('failed to pull flashcard #' + id);
37 });
38 };
39 $scope.unpullCard = function(id) {
40 if (!$scope.deck_cards[id]) return console.log('Not unpulling', id, "because it's not in deck");
41 $http.post('/api/flashcards/' + id + '/unpull/').
42 success(function(data) {
43 console.log('unpulled flashcard #' + id);
44 }).
45 error(function(data) {
46 console.log('failed to unpull flashcard #' + id);
47 });
48 };
$scope.refreshLayout = function() { 34 49 $scope.refreshLayout = function() {
numCols = Math.max(1, Math.floor(($window.innerWidth - 17) / 250)); 35 50 numCols = Math.max(1, Math.floor(($window.innerWidth - 17) / 250));
36 51
// check if we actually need to refresh the whole layout 37 52 // check if we actually need to refresh the whole layout
if (numCols == $scope.numCols) return $scope.refreshColumnWidth(); 38 53 if (numCols == $scope.numCols) return $scope.refreshColumnWidth();
$scope.numCols = numCols; 39 54 $scope.numCols = numCols;
console.log('refreshing layout for ' + $scope.numCols + ' columns'); 40 55 console.log('refreshing layout for ' + $scope.numCols + ' columns');
$scope.cardCols = []; 41 56 $scope.cardCols = [];
var cols = []; 42 57 var cols = [];
for (i = 0; i < $scope.numCols; i++) cols.push([]); 43 58 for (i = 0; i < $scope.numCols; i++) cols.push([]);
$scope.cards.forEach(function(card, i) { 44 59 $scope.cards.forEach(function(card, i) {
cols[i % $scope.numCols].push(card); 45 60 cols[i % $scope.numCols].push(card);
$scope.cardTable[card.id] = {'colNum': (i % $scope.numCols), 'obj': card}; 46 61 $scope.cardTable[card.id] = {'colNum': (i % $scope.numCols), 'obj': card};
62 card.isInDeck();
}); 47 63 });
// wait until the next digest cycle to update cardCols 48 64 // wait until the next digest cycle to update cardCols
49 65 console.log(cols);
$timeout(function() { 50 66 $timeout(function() {
$scope.cardCols = cols; 51 67 $scope.cardCols = cols;
$timeout($scope.refreshColumnWidth); 52 68 $timeout($scope.refreshColumnWidth);
}); 53 69 });
54 70
}; 55 71 };
56 72
angular.element($window).bind('resize', $scope.refreshLayout); 57 73 angular.element($window).bind('resize', $scope.refreshLayout);
58 74
$scope.ws_host = window.location.origin.replace('http', 'ws'); 59 75 $scope.ws_host = window.location.origin.replace('http', 'ws');
$scope.deck_ws = $websocket($scope.ws_host + '/ws/deck/' + $scope.sectionId + '?subscribe-user'); 60 76 $scope.deck_ws = $websocket($scope.ws_host + '/ws/deck/' + $scope.sectionId + '?subscribe-user');
$scope.deck_ws.onOpen(function() { 61 77 $scope.deck_ws.onOpen(function() {
console.log('deck ws open'); 62 78 console.log('deck ws open');
}); 63 79 });
64 80
$scope.deck_ws.onMessage(function(message) { 65 81 $scope.deck_ws.onMessage(function(message) {
console.log('message', message); 66 82 data = JSON.parse(message.data);
if (message.event_type == 'pull_card') { 67 83 console.log('message', data);
$scope.deck_cards.push(card); 68 84 if (data.event_type == 'pull_card') {
85 card = data.flashcard;
86 console.log('pulling', card);
87 $scope.deck_cards[card.id] = card;
} 69 88 }
89 if (data.event_type == 'unpull_card') {
90 card = data.flashcard;
91 $scope.deck_cards[card.id] = undefined;
92 }
}); 70 93 });
71 94
$scope.cardInDeck = function(id) { 72 95 $scope.cardInDeck = function(id) {
console.log('checking whether', id, 'in deck'); 73 96 return $scope.deck_cards[id];
for (card in $scope.deck_cards) if (card.id == id) return true; 74
return false; 75
}; 76 97 };
$scope.add = function(card) { 77 98 $scope.add = function(card) {
var colNum = 0; 78 99 var colNum = 0;
var lowestCol = $scope.cardCols[0]; 79 100 var lowestCol = $scope.cardCols[0];
var lowestColNum = 0; 80 101 var lowestColNum = 0;
while (colNum < $scope.numCols) { 81 102 while (colNum < $scope.numCols) {
if ($scope.cardCols[colNum].length == 0) { 82 103 if ($scope.cardCols[colNum].length == 0) {
lowestCol = $scope.cardCols[colNum]; 83 104 lowestCol = $scope.cardCols[colNum];
break; 84 105 break;
} else if ($scope.cardCols[colNum].length < lowestCol.length) { 85 106 } else if ($scope.cardCols[colNum].length < lowestCol.length) {
lowestCol = $scope.cardCols[colNum]; 86 107 lowestCol = $scope.cardCols[colNum];
lowestColNum = colNum; 87 108 lowestColNum = colNum;
lowestColLen = $scope.cardCols[colNum].length; 88 109 lowestColLen = $scope.cardCols[colNum].length;
} 89 110 }
colNum++; 90 111 colNum++;
} 91 112 }
console.log(card); 92 113 console.log(card);
$scope.cards.push(data); 93 114 $scope.cards.push(data);
lowestCol.unshift(card); 94 115 lowestCol.unshift(card);
$scope.cardTable[card.id] = {'colNum': lowestColNum, 'obj': card}; 95 116 $scope.cardTable[card.id] = {'colNum': lowestColNum, 'obj': card};
$timeout($scope.refreshColumnWidth); 96 117 $timeout($scope.refreshColumnWidth);
}; 97 118 };
98 119
$http.get('/api/sections/' + $scope.sectionId + '/deck/'). 99 120 $http.get('/api/sections/' + $scope.sectionId + '/deck/').
success(function(data) { 100 121 success(function(data) {
$scope.deck_cards = data; 101 122 $scope.deck_cards = [];
123 for (i in data) $scope.deck_cards[data[i].id] = data[i];
console.log("got user's deck"); 102 124 console.log("got user's deck");
console.log(data); 103
}). 104 125 }).
error(function(err) { 105 126 error(function(err) {
console.log('get deck failed'); 106 127 console.log('get deck failed');
}); 107 128 });
108 129
scripts/FeedController.js View file @ 1653dec
angular.module('flashy.FeedController', ['ui.router', 'ngAnimate', 'ngWebSocket', 'contenteditable']). 1 1 angular.module('flashy.FeedController', ['ui.router', 'ngAnimate', 'ngWebSocket', 'contenteditable']).
2 2
controller('FeedController', function($scope, $rootScope, $state, $http, $window, $timeout, $stateParams, $websocket, UserService) { 3 3 controller('FeedController', function($scope, $rootScope, $state, $http, $window, $timeout, $stateParams, $websocket, UserService) {
angular.module('flashy.CardGridController').controller.apply(this, arguments); 4 4 angular.module('flashy.CardGridController').controller.apply(this, arguments);
console.log('Hello from feed'); 5
6
$scope.refreshCards = function() { 7 5 $scope.refreshCards = function() {
$http.get('/api/sections/' + $scope.sectionId + '/feed/'). 8 6 $http.get('/api/sections/' + $scope.sectionId + '/feed/').
success(function(data) { 9 7 success(function(data) {
console.log(data); 10 8 console.log(data);
$scope.cards = data; 11 9 $scope.cards = [];
10 for (var i in data) {
11 console.log(data[i].id);
12 data[i].isInDeck = function(id) {
13 return $scope.cardInDeck(id);
14 }.bind(this, data[i].id);
15 data[i].unpull = function(id) {
16 $scope.unpullCard(id);
17 //$scope.deck_cards[id] = undefined;
18 }.bind(this, data[i].id);
19 data[i].pull = function(id, card) {
20 $scope.pullCard(id);
21 //$scope.deck_cards[id] = card;
22 }.bind(this, data[i].id, data[i]);
23 $scope.cards.push(data[i]);
24 }
$scope.refreshLayout(); 12 25 $scope.refreshLayout();
console.log('success in refresh cards...'); 13 26 console.log('success in refresh cards...');
}). 14 27 }).
error(function(err) { 15 28 error(function(err) {
console.log('refresh fail'); 16 29 console.log('refresh fail');
console.log(err); 17 30 console.log(err);
}); 18 31 });
}; 19 32 };
20 33
$scope.sortAdd = function(card, array) { 21 34 $scope.sortAdd = function(card, array) {
console.log('sort add'); 22 35 console.log('sort add');
array.forEach(function(ele, i, ary) { 23 36 array.forEach(function(ele, i, ary) {
if (ele.score <= card.score) { 24 37 if (ele.score <= card.score) {
ary.splice(i, 0, card); 25 38 ary.splice(i, 0, card);
return; 26 39 return;
} 27 40 }
}); 28 41 });
}; 29 42 };
30 43
$scope.hide = function(card) { 31 44 $scope.hide = function(card) {
console.log('hiding card'); 32
var found = -1; 33 45 var found = -1;
col = $scope.cardTable[card.id].colNum; 34 46 col = $scope.cardTable[card.id].colNum;
found = $scope.cardCols[col].indexOf(card); 35 47 found = $scope.cardCols[col].indexOf(card);
if (found != -1) { 36 48 if (found != -1) {
$scope.cardCols[col].splice(found, 1); 37 49 $scope.cardCols[col].splice(found, 1);
console.log('card hidden'); 38 50 console.log('card hidden');
return col; 39 51 return col;
} 40 52 }
console.log('Error finding card to hide:'); 41 53 console.log('Error finding card to hide:');
console.log(card); 42 54 console.log(card);
return -1; 43 55 return -1;
}; 44 56 };
45 57
$scope.update = function(id, new_score) { 46 58 $scope.update = function(id, new_score) {
card = $scope.cardTable[id].obj; 47 59 card = $scope.cardTable[id].obj;
if (Math.abs(new_score - card.score) < .0001) { 48 60 if (Math.abs(new_score - card.score) < .0001) {
console.log('score same, no update required'); 49 61 console.log('score same, no update required');
return; 50 62 return;
} 51 63 }
console.log('updating'); 52 64 console.log('updating');
console.log(card); 53 65 console.log(card);
var column = $scope.cardCols[$scope.cardTable[id].colNum]; 54 66 var column = $scope.cardCols[$scope.cardTable[id].colNum];
var found = column.indexOf(card); 55 67 var found = column.indexOf(card);
var i = 0; 56 68 var i = 0;
for (; i < column.length; i++) 57 69 for (; i < column.length; i++)
if (column[i].score <= new_score) break; 58 70 if (column[i].score <= new_score) break;
card.score = new_score; 59 71 card.score = new_score;
if ($scope.$$phase) { // most of the time it is "$digest" 60 72 if ($scope.$$phase) { // most of the time it is "$digest"
column.splice(i, 0, column.splice(found, 1)[0]); 61 73 column.splice(i, 0, column.splice(found, 1)[0]);
} else { 62 74 } else {
$scope.$apply(column.splice(i, 0, column.splice(found, 1)[0])); 63 75 $scope.$apply(column.splice(i, 0, column.splice(found, 1)[0]));
} 64 76 }
}; 65 77 };
66 78
$scope.feed_ws = $websocket($scope.ws_host + '/ws/feed/' + $scope.sectionId + '?subscribe-broadcast'); 67 79 $scope.feed_ws = $websocket($scope.ws_host + '/ws/feed/' + $scope.sectionId + '?subscribe-broadcast');
$scope.feed_ws.onOpen(function() { 68 80 $scope.feed_ws.onOpen(function() {
console.log('deck ws open'); 69 81 console.log('feed ws open');
}); 70 82 });
71 83
$scope.feed_ws.onMessage(function(e) { 72 84 $scope.feed_ws.onMessage(function(e) {
data = JSON.parse(e.data); 73 85 data = JSON.parse(e.data);
console.log('got websocket message ' + e.data); 74 86 console.log('got websocket message ' + e.data);
console.log(data); 75 87 console.log(data);
if (data.event_type == 'new_card') { 76 88 if (data.event_type == 'new_card') {
$scope.add(data.flashcard); 77 89 $scope.add(data.flashcard);
} else if (data.event_type == 'score_change') { 78 90 } else if (data.event_type == 'score_change') {
$scope.update(data.flashcard_id, data.new_score); 79 91 $scope.update(data.flashcard_id, data.new_score);
} 80 92 }
}); 81 93 });
82 94
$scope.pushCard = function() { 83 95 $scope.pushCard = function() {
var myCard = { 84 96 var myCard = {
// we can't trim this string because it'd mess up the blanks. Something to fix. 85 97 // we can't trim this string because it'd mess up the blanks. Something to fix.
'text': $('#new-card-input').text(), 86 98 'text': $('#new-card-input').text(),
'mask': $scope.newCardBlanks, 87 99 'mask': $scope.newCardBlanks,
section: $scope.section.id 88 100 section: $scope.section.id
}; 89 101 };
if (myCard.text == '') { 90 102 if (myCard.text == '') {
console.log('blank flashcard not pushed:' + myCard.text); 91 103 console.log('blank flashcard not pushed:' + myCard.text);
return closeNewCard(); 92 104 return closeNewCard();
} 93 105 }
$http.post('/api/flashcards/', myCard). 94 106 $http.post('/api/flashcards/', myCard).
success(function(data) { 95 107 success(function(data) {
console.log('flashcard pushed: ' + myCard.text); 96 108 console.log('flashcard pushed: ' + myCard.text);
if (!UserService.hasVerifiedEmail()) { 97 109 if (!UserService.hasVerifiedEmail()) {
Materialize.toast("<p>Thanks for contributing! However, others won't see your card until you verify your email address<p>", 4000); 98 110 Materialize.toast("<p>Thanks for contributing! However, others won't see your card until you verify your email address<p>", 4000);
} 99 111 }
}). 100 112 }).
error(function(error) { 101 113 error(function(error) {
console.log('something went wrong pushing a card!'); 102 114 console.log('something went wrong pushing a card!');
}); 103 115 });
return $scope.closeNewCardModal(); 104 116 return $scope.closeNewCardModal();
}; 105 117 };
106 118
/* Key bindings for the whole feed window. Hotkey it up! */ 107 119 /* Key bindings for the whole feed window. Hotkey it up! */
var listenForC = true; 108 120 var listenForC = true;
109 121
// Need to pass these options into openmodal and leanmodal, 110 122 // Need to pass these options into openmodal and leanmodal,
// otherwise the ready handler doesn't get called 111 123 // otherwise the ready handler doesn't get called
112 124
modal_options = { 113 125 modal_options = {
dismissible: true, // Modal can be dismissed by clicking outside of the modal 114 126 dismissible: true, // Modal can be dismissed by clicking outside of the modal
opacity: 0, // Opacity of modal background 115 127 opacity: 0, // Opacity of modal background
in_duration: 300, // Transition in duration 116 128 in_duration: 300, // Transition in duration
out_duration: 200, // Transition out duration 117 129 out_duration: 200, // Transition out duration
ready: function() { 118 130 ready: function() {
$('#new-card-input').focus(); 119 131 $('#new-card-input').focus();
document.execCommand('selectAll', false, null); 120 132 document.execCommand('selectAll', false, null);
} 121 133 }
}; 122 134 };
123 135
$(document).keydown(function(e) { 124 136 $(document).keydown(function(e) {
var keyed = e.which; 125 137 var keyed = e.which;
if (keyed == 67 && listenForC) { // "c" for compose 126 138 if (keyed == 67 && listenForC) { // "c" for compose
$scope.openNewCardModal(); 127 139 $scope.openNewCardModal();
e.preventDefault(); 128 140 e.preventDefault();
return false; 129 141 return false;
} else if (keyed == 27) { // clear on ESC 130 142 } else if (keyed == 27) { // clear on ESC
$scope.closeNewCardModal(); 131 143 $scope.closeNewCardModal();
} 132 144 }
}); 133 145 });
134 146
$scope.openNewCardModal = function() { 135 147 $scope.openNewCardModal = function() {
$('#newCard').openModal(modal_options); 136 148 $('#newCard').openModal(modal_options);
listenForC = false; 137 149 listenForC = false;
$('#new-card-input').html('Write a flashcard!'); 138 150 $('#new-card-input').html('Write a flashcard!');
}; 139 151 };
140 152
$scope.closeNewCardModal = function() { 141 153 $scope.closeNewCardModal = function() {
listenForC = true; 142 154 listenForC = true;
$('#new-card-input').html('').blur(); 143 155 $('#new-card-input').html('').blur();
$('#newCard').closeModal(modal_options); 144 156 $('#newCard').closeModal(modal_options);
}; 145 157 };
146 158
$('.tooltipped').tooltip({delay: 50}); 147 159 $('.tooltipped').tooltip({delay: 50});
// the "href" attribute of .modal-trigger must specify the modal ID that wants to be triggered 148 160 // the "href" attribute of .modal-trigger must specify the modal ID that wants to be triggered
$('.modal-trigger').leanModal(modal_options); 149 161 $('.modal-trigger').leanModal(modal_options);
$('#new-card-input').on('keydown', function(e) { 150 162 $('#new-card-input').on('keydown', function(e) {
if (e.which == 13) { 151 163 if (e.which == 13) {
e.preventDefault(); 152 164 e.preventDefault();
if ($scope.submit_enabled) { 153 165 if ($scope.submit_enabled) {
$scope.pushCard(); 154 166 $scope.pushCard();
listenForC = true; 155 167 listenForC = true;
} 156 168 }
return false; 157 169 return false;
} else { 158 170 } else {
159 171
} 160 172 }
}); 161 173 });
$('button#blank-selected').click(function() { 162 174 $('button#blank-selected').click(function() {
console.log(window.getSelection()); 163 175 console.log(window.getSelection());
document.execCommand('bold'); 164 176 document.execCommand('bold');
}); 165 177 });
$scope.refreshCards(); 166 178 $scope.refreshCards();
$scope.newCardBlanks = []; 167 179 $scope.newCardBlanks = [];
$scope.refreshNewCardInput = function() { 168 180 $scope.refreshNewCardInput = function() {
$scope.newCardText = $('#new-card-input').text(); 169 181 $scope.newCardText = $('#new-card-input').text();
$scope.submit_enabled = $scope.newCardText.length >= 5 && $scope.newCardText.length <= 160; 170 182 $scope.submit_enabled = $scope.newCardText.length >= 5 && $scope.newCardText.length <= 160;
var i = 0; 171 183 var i = 0;
$scope.newCardBlanks = []; 172 184 $scope.newCardBlanks = [];
$('#new-card-input')[0].childNodes.forEach(function(node) { 173 185 $('#new-card-input')[0].childNodes.forEach(function(node) {
if (typeof node.data == 'undefined') { 174 186 if (typeof node.data == 'undefined') {
console.log('undefined node'); 175 187 console.log('undefined node');
} 176 188 }
node = $(node)[0]; 177 189 node = $(node)[0];
if (node.tagName == 'B') { 178 190 if (node.tagName == 'B') {
var text = $(node).text(); 179 191 var text = $(node).text();
var leftspaces = 0, rightspaces = 0; 180 192 var leftspaces = 0, rightspaces = 0;
// awful way to find the first non-space character from the left or the right. thanks.js 181 193 // awful way to find the first non-space character from the left or the right. thanks.js
while (text[leftspaces] == ' ' || text[leftspaces] == '\xa0') leftspaces++; 182 194 while (text[leftspaces] == ' ' || text[leftspaces] == '\xa0') leftspaces++;
while (text[text.length - 1 - rightspaces] == ' ' || text[text.length - 1 - rightspaces] == '\xa0') rightspaces++; 183 195 while (text[text.length - 1 - rightspaces] == ' ' || text[text.length - 1 - rightspaces] == '\xa0') rightspaces++;
console.log(leftspaces, text.length); 184 196 console.log(leftspaces, text.length);
if (leftspaces != text.length) $scope.newCardBlanks.push([i + leftspaces, i + text.length - rightspaces]); 185 197 if (leftspaces != text.length) $scope.newCardBlanks.push([i + leftspaces, i + text.length - rightspaces]);
i += text.length; 186 198 i += text.length;
} else if (!node.data) { 187 199 } else if (!node.data) {
console.log('weird node', node); 188 200 console.log('weird node', node);
i += $(node).text().length; 189 201 i += $(node).text().length;
} else { 190 202 } else {
i += node.data.length; 191 203 i += node.data.length;
} 192 204 }
}); 193 205 });
$scope.newCardBlanks.sort(function(a, b) { 194 206 $scope.newCardBlanks.sort(function(a, b) {
return a[0] - b[0]; 195 207 return a[0] - b[0];
}); 196 208 });
i = 0; 197 209 i = 0;
newtext = ''; 198 210 newtext = '';
console.log($scope.newCardBlanks); 199 211 console.log($scope.newCardBlanks);
$scope.newCardBlanks.forEach(function(blank) { 200 212 $scope.newCardBlanks.forEach(function(blank) {
newtext += $scope.newCardText.slice(i, blank[0]); 201 213 newtext += $scope.newCardText.slice(i, blank[0]);
newtext += '<b>' + $scope.newCardText.slice(blank[0], blank[1]) + '</b>'; 202 214 newtext += '<b>' + $scope.newCardText.slice(blank[0], blank[1]) + '</b>';
i = blank[1]; 203 215 i = blank[1];
}); 204 216 });
newtext += $scope.newCardText.slice(i); 205 217 newtext += $scope.newCardText.slice(i);
scripts/FlashcardDirective.js View file @ 1653dec
angular.module('flashy.FlashcardDirective', []). 1 1 angular.module('flashy.FlashcardDirective', []).
2 2
directive('flashcard', 3 3 directive('flashcard',
function($http, $state, $window) { 4 4 function($http, $state, $window) {
return { 5 5 return {
templateUrl: '/app/templates/flashcard.html', 6 6 templateUrl: '/app/templates/flashcard.html',
restrict: 'E', 7 7 restrict: 'E',
scope: { 8 8 scope: {
flashcard: '=flashcardObj', // flashcard-obj in parent html 9 9 flashcard: '=flashcardObj', // flashcard-obj in parent html
refresh: '&' // eval refresh in parent html 10 10 refresh: '&' // eval refresh in parent html
}, 11 11 },
link: function(scope, element) { 12 12 link: function(scope, element) {
/* Handles width of the card */ 13 13 /* Handles width of the card */
scope.textPieces = []; 14 14 scope.textPieces = [];
scope.flashcard.mask.sort(function(a, b) { 15 15 scope.flashcard.mask.sort(function(a, b) {
return a[0] - b[0]; 16 16 return a[0] - b[0];
}); 17 17 });
var i = 0; 18 18 var i = 0;
scope.flashcard.mask.forEach(function(blank) { 19 19 scope.flashcard.mask.forEach(function(blank) {
scope.textPieces.push({text: scope.flashcard.text.slice(i, blank[0])}); 20 20 scope.textPieces.push({text: scope.flashcard.text.slice(i, blank[0])});
scope.textPieces.push({text: scope.flashcard.text.slice(blank[0], blank[1]), blank: true}); 21 21 scope.textPieces.push({text: scope.flashcard.text.slice(blank[0], blank[1]), blank: true});
i = blank[1]; 22 22 i = blank[1];
}); 23 23 });
scope.textPieces.push({text: scope.flashcard.text.slice(i)}); 24 24 scope.textPieces.push({text: scope.flashcard.text.slice(i)});
/* Pulls card from feed into deck */ 25
scope.pullCard = function(flashcard) { 26
flashcard.is_in_deck = true; 27
$http.post('/api/flashcards/' + flashcard.id + '/pull/'). 28
success(function(data) { 29
console.log('pulled flashcard #' + flashcard.id); 30
//scope.startShrink = true; 31
//scope.refresh(flashcard); 32
}). 33
error(function(data) { 34
console.log('failed to pull flashcard #' + flashcard.id); 35
}); 36
}; 37
38 25
/* Unpulls card from deck */ 39
scope.unpullCard = function(flashcard) { 40
console.log('unpulling card...'); 41
flashcard.is_in_deck = false; 42
$http.post('/api/flashcards/' + flashcard.id + '/unpull/'). 43
success(function(data) { 44
console.log('card unpull success'); 45
//scope.startShrink = true; 46
//scope.refresh(flashcard); 47
}). 48
error(function(data) { 49
console.log('card unpull FAILURE'); 50
}); 51
}; 52
53
/* Hides card from feed */ 54 26 /* Hides card from feed */
scope.hideCard = function(flashcard) { 55 27 scope.hideCard = function(flashcard) {
if ($state.current.name == 'feed') { 56 28 if ($state.current.name == 'feed') {
$http.post('/api/flashcards/' + flashcard.id + '/hide/'). 57 29 $http.post('/api/flashcards/' + flashcard.id + '/hide/').
success(function(data) { 58 30 success(function(data) {
console.log('card hide success'); 59 31 console.log('card hide success');
scope.startShrink = true; 60 32 scope.startShrink = true;
scope.refresh(flashcard); 61 33 scope.refresh(flashcard);
}). 62 34 }).
error(function(data) { 63 35 error(function(data) {
console.log('card hide FAILURE'); 64 36 console.log('card hide FAILURE');
}); 65 37 });
} 66 38 }
}; 67
scope.isInDeck = function(flashcard) { 68
//$scope. 69
}; 70 39 };
} 71 40 }
scripts/SelectDirective.js View file @ 1653dec
/* Credits to vinciciusmelquiades: 1 File was deleted
https://github.com/viniciusmelquiades/firstExample/blob/master/app/src/directive/directive.js */ 2
(function() { 3
'use strict'; 4
5
angular.module('flashy.SelectDirective', []) 6
.directive('select', materialSelect); 7
console.log('coming inside directive'); 8
materialSelect.$inject = ['$timeout']; 9
10
function materialSelect($timeout) { 11
var directive = { 12
link: link, 13
restrict: 'E', 14
require: '?ngModel' 15
}; 16
17
function link(scope, element, attrs, ngModel) { 18
$timeout(create); 19
20
if (ngModel) { 21
ngModel.$render = create; 22
} 23
24
function create() { 25
element.material_select(); 26
} 27
28
//if using materialize v0.96.0 use this 29
element.one('$destroy', function() { 30
scripts/UserService.js View file @ 1653dec
angular.module('flashy.UserService', ['ui.router']). 1 1 angular.module('flashy.UserService', ['ui.router']).
service('UserService', function($rootScope, $http, $q) { 2 2 service('UserService', function($rootScope, $http, $q) {
var deferred = $q.defer(); 3 3 var deferred = $q.defer();
var _user = false; 4 4 var _user = false;
var login = function(data) { 5 5 var login = function(data) {
if (data.locked) { 6 6 if (data.locked) {
$rootScope.UserService.showLockedMessage(); 7 7 $rootScope.UserService.showLockedMessage();
return deferred.reject('account locked'); 8 8 return deferred.reject('account locked');
} 9 9 }
if (!data.is_confirmed) { 10 10 if (!data.is_confirmed) {
Materialize.toast('Please verify your email address! ' + 11 11 Materialize.toast('Please verify your email address! ' +
'<a class="btn-flat cyan-text" onclick="rootscope.UserService.resendConfirmationEmail()">' + 12 12 '<a class="btn-flat cyan-text" onclick="rootscope.UserService.resendConfirmationEmail()">' +
'Resend Verification Email</a>', 4000); 13 13 'Resend Verification Email</a>', 4000);
} 14 14 }
_user = data; 15 15 _user = data;
_user.sectionIdList = _user.sections.map(function(x) { 16 16 _user.sectionIdList = _user.sections.map(function(x) {
return x.id; 17 17 return x.id;
}); 18 18 });
deferred.resolve(data); 19 19 deferred.resolve(data);
}; 20 20 };
this.login = login; 21 21 this.login = login;
$http.get('/api/me/').success(function(data) { 22 22 $http.get('/api/me/').success(function(data) {
console.log('user is logged in!'); 23 23 console.log('user is logged in!');
login(data); 24 24 login(data);
}).error(function(data) { 25 25 }).error(function(data) {
console.log(data); 26 26 console.log(data);
console.log('not logged in yet: ' + data.detail); 27 27 console.log('not logged in yet: ' + data.detail);
_user = {email: false}; 28 28 _user = {email: false};
deferred.resolve(_user); 29 29 deferred.resolve(_user);
}); 30 30 });
31 31
this.isResolved = function() { 32 32 this.isResolved = function() {
return !!_user; 33 33 return !!_user;
}; 34 34 };
this.getUserData = function() { 35 35 this.getUserData = function() {
if (this.isResolved()) return _user; 36 36 if (this.isResolved()) return _user;
else return deferred.promise; 37 37 else return deferred.promise;
}; 38 38 };
this.hasVerifiedEmail = function() { 39 39 this.hasVerifiedEmail = function() {
return this.isResolved() && _user.is_confirmed; 40 40 return this.isResolved() && _user.is_confirmed;
}; 41 41 };
42
this.logout = function($state) { 42 43 this.logout = function($state) {
$http.post('/api/logout/').success(function() { 43 44 $http.post('/api/logout/').success(function() {
if (!_user.locked)Materialize.toast('Logged out!', 1000); 44 45 if (!_user.locked)Materialize.toast('Logged out!', 1000);
}).error(function() { 45 46 }).error(function() {
console.log('Problem logging out'); 46 47 console.log('Problem logging out');
}); 47 48 });
_user = false; 48 49 _user = false;
deferred.resolve({}); 49 50 deferred.resolve({});
$state.go('login'); 50 51 $state.go('login');
}; 51 52 };
this.addClass = function(section) { 52 53 this.addClass = function(section) {
_user.sections.push(section); 53 54 _user.sections.push(section);
_user.sectionIdList.push(section.id); 54 55 _user.sectionIdList.push(section.id);
}; 55 56 };
this.isLoggedIn = function() { 56 57 this.isLoggedIn = function() {
rv = this.isResolved() && _user.email; 57 58 rv = this.isResolved() && _user.email;
return rv; 58 59 return rv;
}; 59 60 };
this.isInSection = function(sectionId) { 60 61 this.isInSection = function(sectionId) {
return (_user.sectionIdList.indexOf(sectionId) >= 0); 61 62 return (_user.sectionIdList.indexOf(sectionId) >= 0);
}; 62 63 };
this.redirectToDefaultState = function($state) { 63 64 this.redirectToDefaultState = function($state) {
console.log('redirecting user to their default state'); 64 65 console.log('redirecting user to their default state');
66 // if the user isn't logged in, log in!
if (!this.isLoggedIn()) return $state.go('login'); 65 67 if (!this.isLoggedIn()) return $state.go('login');
68 // if the user isn't enrolled in any sections, go to addclass
if (!_user.sections.length) return $state.go('addclass'); 66 69 if (!_user.sections.length) return $state.go('addclass');
last_state = localStorage.getItem('last_state'); 67 70 last_state = localStorage.getItem('last_state');
if (last_state) { 68 71 if (last_state) {
72 // if there was a last state, get the parameters of the state
last_state_params = JSON.parse(localStorage.getItem('last_state_params')); 69 73 last_state_params = JSON.parse(localStorage.getItem('last_state_params'));
if (last_state_params.sectionId && this.authorizedFor(last_state, last_state_params)) { 70 74 if (last_state_params.sectionId && this.authorizedFor(last_state, last_state_params)) {
75 // if we're authorized to access that state with those parameters, go there
return $state.go(last_state, JSON.parse(localStorage.getItem('last_state_params'))); 71 76 return $state.go(last_state, JSON.parse(localStorage.getItem('last_state_params')));
} 72 77 }
} 73 78 }
$state.go('feed', {sectionId: _user.sections[0].id}); 74 79 $state.go('feed', {sectionId: _user.sections[0].id});
}; 75 80 };
this.authorizedFor = function(state, stateParams) { 76 81 this.authorizedFor = function(state, stateParams) {
if (['feed', 'deck', 'cardlist'].indexOf(state.name) >= 0) { 77 82 if (['feed', 'deck', 'cardlist'].indexOf(state.name) >= 0) {
console.log('checking whether', stateParams, 'in', _user.sectionIdList); 78 83 console.log('checking whether', stateParams, 'in', _user.sectionIdList);
if (_user.sectionIdList.indexOf(parseInt(stateParams.sectionId)) < 0) { 79 84 if (_user.sectionIdList.indexOf(parseInt(stateParams.sectionId)) < 0) {
return false; 80 85 return false;
} 81 86 }
} 82 87 }
return true; 83 88 return true;
}; 84 89 };
this.showLockedMessage = function() { 85 90 this.showLockedMessage = function() {
Materialize.toast('You must verify your email address before continuing.' + 86 91 Materialize.toast('You must verify your email address before continuing.' +
'<a class="btn-flat cyan-text" onclick="rootscope.UserService.resendConfirmationEmail()">' + 87 92 '<a class="btn-flat cyan-text" onclick="rootscope.UserService.resendConfirmationEmail()">' +
'Resend Verification Email</a>', 4000); 88 93 'Resend Verification Email</a>', 4000);
}; 89 94 };
this.noAuthRequired = function(state) { 90 95 this.noAuthRequired = function(state) {
if (['verifyemail', 'login'].indexOf(state.name) >= 0) { 91 96 if (['verifyemail', 'login'].indexOf(state.name) >= 0) {
return true; 92 97 return true;
} 93 98 }
return false; 94 99 return false;
}; 95 100 };
this.resendConfirmationEmail = function() { 96 101 this.resendConfirmationEmail = function() {
console.log('Requesting resend of confirmation email'); 97 102 console.log('Requesting resend of confirmation email');
$http.post('/api/resend_confirmation_email/').success(function() { 98 103 $http.post('/api/resend_confirmation_email/').success(function() {
Materialize.toast('Resent confirmation email! Check your spam folder too.', 4000); 99 104 Materialize.toast('Resent confirmation email! Check your spam folder too.', 4000);
}); 100 105 });
}; 101 106 };
templates/flashcard.html View file @ 1653dec
<div class="card flashy smallify cyan-text text-darken-2" ng-init="startShrink = false" 1 1 <div class="card flashy smallify cyan-text text-darken-2" ng-init="startShrink = false"
ng-class="{'shrinky': startShrink}"> 2 2 ng-class="{'shrinky': startShrink}">
<div class="valign-wrapper"> 3 3 <div class="valign-wrapper">
<div class="card-content valign center-align"> 4 4 <div class="card-content valign center-align">
<span ng-repeat="piece in textPieces" 5 5 <span ng-repeat="piece in textPieces"
ng-style="piece.blank ? {'opacity':'0.4', 'border-bottom': '1px solid black'} : {}">{{piece.text}}</span> 6 6 ng-style="piece.blank ? {'opacity':'0.4', 'border-bottom': '1px solid black'} : {}">{{piece.text}}</span>
</div> 7 7 </div>
</div> 8 8 </div>
<div class="card-overlay"> 9 9 <div class="card-overlay">
<div class="top-box no-user-select" ng-hide="flashcard.is_in_deck" 10 10 <div class="top-box no-user-select" ng-hide="flashcard.isInDeck()"
ng-click="pullCard(flashcard)"> 11 11 ng-click="flashcard.pull()">
<div class="center-me"><i class="mdi-content-add-circle-outline medium"></i></div> 12 12 <div class="center-me"><i class="mdi-content-add-circle-outline medium"></i></div>
</div> 13 13 </div>
<div class="top-box no-user-select" ng-show="flashcard.is_in_deck" 14 14 <div class="top-box no-user-select" ng-show="flashcard.isInDeck()"
ng-click="unpullCard(flashcard)"> 15 15 ng-click="flashcard.unpull()">
<div class="center-me"><i class="mdi-content-remove-circle-outline medium"></i></div> 16 16 <div class="center-me"><i class="mdi-content-remove-circle-outline medium"></i></div>
</div> 17 17 </div>
<div class="bottom-box no-user-select"> 18 18 <div class="bottom-box no-user-select">
<div class="left-box"> 19 19 <div class="left-box">
<div class="center-me"><i class="mdi-action-info-outline small"></i></div> 20 20 <div class="center-me"><i class="mdi-action-info-outline small"></i></div>
</div> 21 21 </div>
<div class="right-box" ng-click=" 22 22 <div class="right-box" ng-click="
hideCard(flashcard)"> 23 23 hideCard(flashcard)">
<div class="center-me"><i class="mdi-action-delete small"></i></div> 24 24 <div class="center-me"><i class="mdi-action-delete small"></i></div>
</div> 25 25 </div>
26 26
</div> 27 27 </div>
</div> 28 28 </div>
<div ng-show="cardInDeck(flashcard.id)" class="green-text" style="position:absolute; top:0px;right:0px"> 29 29 <div ng-show="flashcard.isInDeck()" class="green-text" style="position:absolute; top:0px;right:0px">
<div class="center-me"><i class="mdi-action-done small"></i></div> 30 30 <div class="center-me"><i class="mdi-action-done small"></i></div>
</div> 31 31 </div>
<div ng-show="$root.debug_flashcard" style="position:absolute; bottom:0px; right:5px;"> 32 32 <div ng-show="$root.debug_flashcard" style="position:absolute; bottom:0px; right:5px;">
<span class="center-me">score:{{flashcard.score}}</span> 33 33 <span class="center-me">score:{{flashcard.score}}</span>
</div> 34 34 </div>
</div> 35 35 </div>
36 36