Commit 8e3988f5e9d0972b393bab4f9e6c1708015992dc

Authored by Andrew Buss
1 parent 31711803f0
Exists in master

fix bugs about bolds; allow viewing help without being logged in

Showing 6 changed files with 71 additions and 80 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.RequestResetController', 7 7 'flashy.RequestResetController',
'flashy.StudyController', 8 8 'flashy.StudyController',
'flashy.UserService', 9 9 'flashy.UserService',
'flashy.FlashcardDirective', 10 10 'flashy.FlashcardDirective',
'flashy.FlashcardFactory', 11 11 'flashy.FlashcardFactory',
'flashy.ResetPasswordController', 12 12 'flashy.ResetPasswordController',
'flashy.VerifyEmailController', 13 13 'flashy.VerifyEmailController',
'flashy.CardListController', 14 14 'flashy.CardListController',
'flashy.HelpController', 15 15 'flashy.HelpController',
'flashy.SettingsController', 16 16 'flashy.SettingsController',
'ngCookies']). 17 17 'ngCookies']).
config(function($stateProvider, $urlRouterProvider, $resourceProvider, $httpProvider, $locationProvider) { 18 18 config(function($stateProvider, $urlRouterProvider, $resourceProvider, $httpProvider, $locationProvider) {
'use strict'; 19 19 'use strict';
$httpProvider.defaults.withCredentials = true; 20 20 $httpProvider.defaults.withCredentials = true;
$httpProvider.defaults.xsrfCookieName = 'csrftoken'; 21 21 $httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'; 22 22 $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
$resourceProvider.defaults.stripTrailingSlashes = false; 23 23 $resourceProvider.defaults.stripTrailingSlashes = false;
var arrayMethods = Object.getOwnPropertyNames(Array.prototype); 24 24 var arrayMethods = Object.getOwnPropertyNames(Array.prototype);
arrayMethods.forEach(attachArrayMethodsToNodeList); 25 25 arrayMethods.forEach(attachArrayMethodsToNodeList);
function attachArrayMethodsToNodeList(methodName) { 26 26 function attachArrayMethodsToNodeList(methodName) {
if (methodName !== 'length') { 27 27 if (methodName !== 'length') {
NodeList.prototype[methodName] = Array.prototype[methodName]; 28 28 NodeList.prototype[methodName] = Array.prototype[methodName];
} 29 29 }
} 30 30 }
31 31
$httpProvider.interceptors.push(function($q, $rootScope) { 32 32 $httpProvider.interceptors.push(function($q, $rootScope) {
return { 33 33 return {
'responseError': function(rejection) { // need a better redirect 34 34 'responseError': function(rejection) { // need a better redirect
if (rejection.status >= 500) { 35 35 if (rejection.status >= 500) {
console.log('got error'); 36 36 console.log('got error');
console.log(rejection); 37 37 console.log(rejection);
$rootScope.$broadcast('server_error', rejection); 38 38 $rootScope.$broadcast('server_error', rejection);
} 39 39 }
if (rejection.status == 403) { 40 40 if (rejection.status == 403) {
console.log(rejection); 41 41 console.log(rejection);
if (rejection.data && rejection.data.detail == 'Please verify your email before continuing') { 42 42 if (rejection.data && rejection.data.detail == 'Please verify your email before continuing') {
UserService.showLockedMessage(); 43 43 UserService.showLockedMessage();
UserService.logout(); 44 44 UserService.logout();
} 45 45 }
} 46 46 }
if (rejection.status == 429) { 47 47 if (rejection.status == 429) {
console.log(rejection); 48 48 console.log(rejection);
Materialize.toast('Your enthusiasm is appreciated, but we ask that you slow down a little!', 4000); 49 49 Materialize.toast('Your enthusiasm is appreciated, but we ask that you slow down a little!', 4000);
} 50 50 }
return $q.reject(rejection); 51 51 return $q.reject(rejection);
} 52 52 }
}; 53 53 };
}); 54 54 });
$locationProvider.html5Mode(true); 55 55 $locationProvider.html5Mode(true);
$urlRouterProvider.otherwise('/login'); 56 56 $urlRouterProvider.otherwise('/login');
var auth_resolve = { 57 57 var auth_resolve = {
authorize: function($q, $rootScope, $state, $stateParams, UserService) { 58 58 authorize: function($q, $rootScope, $state, $stateParams, UserService) {
console.log('do we need to authorize a user for', $rootScope.nextState.name); 59 59 console.log('do we need to authorize a user for', $rootScope.nextState.name);
if (UserService.noAuthRequired($rootScope.nextState)) { 60 60 if (UserService.noAuthRequired($rootScope.nextState)) {
console.log('no auth required for', $rootScope.nextState.name); 61 61 console.log('no auth required for', $rootScope.nextState.name);
return UserService.getUserData(); 62 62 return UserService.getUserData();
} 63 63 }
console.log('resolving user before continuing to ' + $rootScope.nextState.name); 64 64 console.log('resolving user before continuing to ' + $rootScope.nextState.name);
var redirectAsNeeded = function() { 65 65 var redirectAsNeeded = function() {
if (!UserService.isLoggedIn()) { 66 66 if (!UserService.isLoggedIn()) {
console.log(UserService.getUserData()); 67 67 console.log(UserService.getUserData());
console.log('making the user log in'); 68 68 console.log('making the user log in');
$state.go('login'); 69 69 $state.go('login');
} 70 70 }
if (!UserService.authorizedFor($rootScope.nextState, $rootScope.nextStateParams)) { 71 71 if (!UserService.authorizedFor($rootScope.nextState, $rootScope.nextStateParams)) {
console.log('user not authorized for ' + $rootScope.nextState.name); 72 72 console.log('user not authorized for ' + $rootScope.nextState.name);
$state.go('addclass'); 73 73 $state.go('addclass');
} 74 74 }
}; 75 75 };
if (UserService.isResolved()) return redirectAsNeeded(); 76 76 if (UserService.isResolved()) return redirectAsNeeded();
return UserService.getUserData().then(redirectAsNeeded); 77 77 return UserService.getUserData().then(redirectAsNeeded);
} 78 78 }
}; 79 79 };
$stateProvider. 80 80 $stateProvider.
state('login', { 81 81 state('login', {
resolve: auth_resolve, 82 82 resolve: auth_resolve,
url: '/login', 83 83 url: '/login',
templateUrl: 'templates/login.html', 84 84 templateUrl: 'templates/login.html',
controller: 'LoginController' 85 85 controller: 'LoginController'
}). 86 86 }).
state('root', { 87 87 state('root', {
resolve: auth_resolve, 88 88 resolve: auth_resolve,
url: '', 89 89 url: '',
controller: 'RootController' 90 90 controller: 'RootController'
}). 91 91 }).
state('feed', { 92 92 state('feed', {
resolve: auth_resolve, 93 93 resolve: auth_resolve,
url: '/feed/{sectionId}', 94 94 url: '/feed/{sectionId}',
templateUrl: 'templates/feed.html', 95 95 templateUrl: 'templates/feed.html',
controller: 'FeedController' 96 96 controller: 'FeedController'
}). 97 97 }).
state('cardlist', { 98 98 state('cardlist', {
resolve: auth_resolve, 99 99 resolve: auth_resolve,
url: '/cards/{sectionId}', 100 100 url: '/cards/{sectionId}',
templateUrl: 'templates/cardlist.html', 101 101 templateUrl: 'templates/cardlist.html',
controller: 'CardListController' 102 102 controller: 'CardListController'
}). 103 103 }).
state('addclass', { 104 104 state('addclass', {
resolve: auth_resolve, 105 105 resolve: auth_resolve,
url: '/addclass', 106 106 url: '/addclass',
templateUrl: 'templates/addclass.html', 107 107 templateUrl: 'templates/addclass.html',
controller: 'ClassAddController' 108 108 controller: 'ClassAddController'
}). 109 109 }).
state('deck', { 110 110 state('deck', {
resolve: auth_resolve, 111 111 resolve: auth_resolve,
url: '/deck/{sectionId}', 112 112 url: '/deck/{sectionId}',
templateUrl: 'templates/deck.html', 113 113 templateUrl: 'templates/deck.html',
controller: 'DeckController' 114 114 controller: 'DeckController'
}). 115 115 }).
state('study', { 116 116 state('study', {
resolve: auth_resolve, 117 117 resolve: auth_resolve,
url: '/study', 118 118 url: '/study',
templateUrl: 'templates/study.html', 119 119 templateUrl: 'templates/study.html',
controller: 'StudyController' 120 120 controller: 'StudyController'
}). 121 121 }).
/*state('flashcard', { 122 122 /*state('flashcard', {
resolve: auth_resolve, 123 123 resolve: auth_resolve,
url: '/flashcard', 124 124 url: '/flashcard',
templateUrl: 'templates/flashcard.html', 125 125 templateUrl: 'templates/flashcard.html',
controller: 'FlashcardController' 126 126 controller: 'FlashcardController'
}).*/ 127 127 }).*/
state('settings', { 128 128 state('settings', {
resolve: auth_resolve, 129 129 resolve: auth_resolve,
url: '/settings', 130 130 url: '/settings',
templateUrl: 'templates/settings.html', 131 131 templateUrl: 'templates/settings.html',
controller: 'SettingsController' 132 132 controller: 'SettingsController'
}). 133 133 }).
state('requestpasswordreset', { 134 134 state('requestpasswordreset', {
url: '/requestpasswordreset', 135 135 url: '/requestpasswordreset',
templateUrl: 'templates/requestpasswordreset.html', 136 136 templateUrl: 'templates/requestpasswordreset.html',
controller: 'RequestResetController' 137 137 controller: 'RequestResetController'
}). 138 138 }).
state('resetpassword', { 139 139 state('resetpassword', {
url: '/resetpassword/{uid}/{token}', 140 140 url: '/resetpassword/{uid}/{token}',
templateUrl: 'templates/resetpassword.html', 141 141 templateUrl: 'templates/resetpassword.html',
controller: 'ResetPasswordController' 142 142 controller: 'ResetPasswordController'
}). 143 143 }).
state('verifyemail', { 144 144 state('verifyemail', {
url: '/verifyemail/{key}', 145 145 url: '/verifyemail/{key}',
templateUrl: 'templates/verifyemail.html', 146 146 templateUrl: 'templates/verifyemail.html',
controller: 'VerifyEmailController' 147 147 controller: 'VerifyEmailController'
}). 148 148 }).
state('404', { 149 149 state('404', {
url: '/404', 150 150 url: '/404',
template: "<h1>This page doesn't exist!</h1>" 151 151 template: "<h1>This page doesn't exist!</h1>"
}). 152 152 }).
state('help', { 153 153 state('help', {
resolve: auth_resolve, 154
url: '/help', 155 154 url: '/help',
templateUrl: 'templates/help.html', 156 155 templateUrl: 'templates/help.html',
controller: 'HelpController' 157 156 controller: 'HelpController'
}); 158 157 });
}). 159 158 }).
run(function($rootScope, $state, $stateParams, $location, UserService) { 160 159 run(function($rootScope, $state, $stateParams, $location, UserService) {
$rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error) { 161 160 $rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error) {
console.log('failed to change state: ' + error); 162 161 console.log('failed to change state: ' + error);
$state.go('login'); 163 162 $state.go('login');
}); 164 163 });
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) { 165 164 $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
$rootScope.nextState = toState; 166 165 $rootScope.nextState = toState;
$rootScope.nextStateParams = toParams; 167 166 $rootScope.nextStateParams = toParams;
console.log('changing state to', toState); 168 167 console.log('changing state to', toState);
if (['feed', 'deck', 'cardlist'].indexOf(toState.name) >= 0) { 169 168 if (['feed', 'deck', 'cardlist'].indexOf(toState.name) >= 0) {
localStorage.setItem('last_state', toState.name); 170 169 localStorage.setItem('last_state', toState.name);
localStorage.setItem('last_state_params', JSON.stringify(toParams)); 171 170 localStorage.setItem('last_state_params', JSON.stringify(toParams));
} 172 171 }
}); 173 172 });
<!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-sidenav" 22 22 <a ng-show="UserService.isLoggedIn()" href="#" data-activates="mobile-sidenav"
class="left button-collapse hide-on-med-and-up"><i class="mdi-navigation-menu"></i></a> 23 23 class="left button-collapse hide-on-med-and-up"><i class="mdi-navigation-menu"></i></a>
24 24
<!-- 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="#!" id="class-list" 35 35 href="#!" id="class-list"
data-activates="classDropdown" data-beloworigin="true">{{currentSection.id?currentSection.short_name:"Classes"}}<i 36 36 data-activates="classDropdown" data-beloworigin="true">{{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" id="class-list"><a 43 43 <li ng-show="currentSection.id" ui-sref-active="active" id="class-list"><a
ui-sref="deck({sectionId:currentSection.id})" 44 44 ui-sref="deck({sectionId:currentSection.id})"
class="tooltipped" 45 45 class="tooltipped"
data-position="bottom" 46 46 data-position="bottom"
data-delay="50" data-tooltip="Deck"><i 47 47 data-delay="50" data-tooltip="Deck"><i
class="mdi-action-view-carousel"></i></a></li> 48 48 class="mdi-action-view-carousel"></i></a></li>
<li ng-show="currentSection.id" ui-sref-active="active"><a ui-sref="cardlist({sectionId:currentSection.id})" 49 49 <li ng-show="currentSection.id" ui-sref-active="active"><a ui-sref="cardlist({sectionId:currentSection.id})"
class="tooltipped" 50 50 class="tooltipped"
data-position="bottom" 51 51 data-position="bottom"
data-delay="50" data-tooltip="Card List"><i 52 52 data-delay="50" data-tooltip="Card List"><i
class="mdi-action-view-list"></i></a></li> 53 53 class="mdi-action-view-list"></i></a></li>
</ul> 54 54 </ul>
<a href="#" class="brand-logo center">Flashy</a> 55 55 <a href="#" class="brand-logo center">Flashy</a>
56 56
57 <ul ng-cloak id="nav-mobile" class="right hide-on-small-and-down">
58
59 <li ui-sref-active="active"><a ui-sref="help"><i class="tiny mdi-action-help tooltipped"
60 data-position="bottom"
61 data-delay="50" data-tooltip="Help"></i></a></li>
62 </ul>
<ul ng-show="UserService.isLoggedIn()" ng-cloak id="nav-mobile" class="right hide-on-small-and-down"> 57 63 <ul ng-show="UserService.isLoggedIn()" ng-cloak id="nav-mobile" class="right hide-on-small-and-down">
58 64
<li ui-sref-active="active"><a ui-sref="study" class="tooltipped" data-position="bottom" data-delay="50" 59 65 <li ui-sref-active="active"><a ui-sref="study" class="tooltipped" data-position="bottom" data-delay="50"
data-tooltip="Study"> 60 66 data-tooltip="Study">
<i class="tiny mdi-action-pageview"></i></a></li> 61 67 <i class="tiny mdi-action-pageview"></i></a></li>
62 68
<!-- Settings Dropdown --> 63
<ul id="settingsDropdown" class="dropdown-content"> 64
65 69
66
</ul> 67
68
<li ui-sref-active="active"><a ui-sref="help"><i class="tiny mdi-action-help tooltipped" 69
data-position="bottom" 70
data-delay="50" data-tooltip="Help"></i></a></li> 71
<li ui-sref-active="active"><a ui-sref="settings"><i data-position="bottom" data-delay="50" 72 70 <li ui-sref-active="active"><a ui-sref="settings"><i data-position="bottom" data-delay="50"
data-tooltip="Settings" 73 71 data-tooltip="Settings"
class="mdi-action-settings tooltipped"></i></a></li> 74 72 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" 75 73 <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> 76 74 class="mdi-content-forward tooltipped"></i></a></li>
77 75
</ul> 78 76 </ul>
79 77
<!-- Slide-in side-nav for small screens --> 80 78 <!-- Slide-in side-nav for small screens -->
<ul ng-show="UserService.isLoggedIn()" class="side-nav" id="mobile-sidenav"> 81 79 <ul ng-show="UserService.isLoggedIn()" class="side-nav" id="mobile-sidenav">
<span ng-show="currentSection.id"> 82 80 <span ng-show="currentSection.id">
<li ui-sref-active="active"><a ui-sref="feed({sectionId:currentSection.id})"> 83 81 <li ui-sref-active="active"><a ui-sref="feed({sectionId:currentSection.id})">
<i class="mdi-action-view-module left"></i> 84 82 <i class="mdi-action-view-module left"></i>
Feed</a> 85 83 Feed</a>
</li> 86 84 </li>
<li ui-sref-active="active"><a ui-sref="deck({sectionId:currentSection.id})"> 87 85 <li ui-sref-active="active"><a ui-sref="deck({sectionId:currentSection.id})">
<i class="mdi-action-view-carousel left"> </i> 88 86 <i class="mdi-action-view-carousel left"> </i>
Deck 89 87 Deck
</a> 90 88 </a>
</li> 91 89 </li>
<li ui-sref-active="active"><a ui-sref="cardlist({sectionId:currentSection.id})"> 92 90 <li ui-sref-active="active"><a ui-sref="cardlist({sectionId:currentSection.id})">
<i class="mdi-action-view-list left"></i> 93 91 <i class="mdi-action-view-list left"></i>
Card List 94 92 Card List
</a> 95 93 </a>
</li> 96 94 </li>
<hr> 97 95 <hr>
</span> 98 96 </span>
<!-- Collapsible menu for all the User's classes --> 99 97 <!-- Collapsible menu for all the User's classes -->
<ul class="collapsible" data-collapsible="accordion"> 100 98 <ul class="collapsible" data-collapsible="accordion">
<li class="bold"> 101 99 <li class="bold">
<a class="collapsible-header black-text"> 102 100 <a class="collapsible-header black-text">
Classes 103 101 Classes
<i class="mdi-navigation-arrow-drop-down right"></i> 104 102 <i class="mdi-navigation-arrow-drop-down right"></i>
</a> 105 103 </a>
</li> 106 104 </li>
<div class="collapsible-body" style="display: block"> 107 105 <div class="collapsible-body" style="display: block">
<ul> 108 106 <ul>
<li ui-sref-active="active" ng-repeat="section in UserService.getUserData().sections"> 109 107 <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 108 <a class="class bold" ui-sref="feed({sectionId:section.id})">{{section.short_name}}</a>
</li> 111 109 </li>
<hr> 112 110 <hr>
<li><a ui-sref="addclass"><i class="tiny mdi-content-add">Add Class</i></a></li> 113 111 <li><a ui-sref="addclass"><i class="tiny mdi-content-add">Add Class</i></a></li>
</ul> 114 112 </ul>
</div> 115 113 </div>
</ul> 116 114 </ul>
<li><a ui-sref="study">Study</a></li> 117 115 <li><a ui-sref="study">Study</a></li>
<li><a ui-sref="settings">Settings</a></li> 118 116 <li><a ui-sref="settings">Settings</a></li>
<li><a ui-sref="help">FAQ</a></li> 119 117 <li><a ui-sref="help">FAQ</a></li>
<li><a ng-click="logout()">Logout</a></li> 120 118 <li><a ng-click="logout()">Logout</a></li>
</ul> 121 119 </ul>
</div> 122 120 </div>
</nav> 123 121 </nav>
124 122
</header> 125 123 </header>
126 124
127 125
<!-- Menu Bar --> 128 126 <!-- Menu Bar -->
<main ui-view></main> 129 127 <main ui-view></main>
130 128
</body> 131 129 </body>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script> 132 130 <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> 133 131 <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> 134 132 <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> 135 133 <script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
<script type="text/javascript" src="scripts/materialize.js"></script> 136 134 <script type="text/javascript" src="scripts/materialize.js"></script>
<script type="text/javascript" src="scripts/jquery.collapsible.js"></script> 137 135 <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> 138 136 <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> 139 137 <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> 140 138 <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> 141 139 <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> 142 140 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-sanitize.js"></script>
<script src="static/js/angular-websocket.js"></script> 143 141 <script src="static/js/angular-websocket.js"></script>
<script src="static/js/angular-contenteditable.js"></script> 144 142 <script src="static/js/angular-contenteditable.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.4/angular-filter.js"></script> 145 143 <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.4/angular-filter.js"></script>
146 144
<script src="config.js"></script> 147 145 <script src="config.js"></script>
148 146
<script src="scripts/FlashcardFactory.js"></script> 149 147 <script src="scripts/FlashcardFactory.js"></script>
<script src="scripts/DeckFactory.js"></script> 150 148 <script src="scripts/DeckFactory.js"></script>
151 149
<!-- Controllers --> 152 150 <!-- Controllers -->
<script src="scripts/FeedController.js"></script> 153 151 <script src="scripts/FeedController.js"></script>
<script src="scripts/RootController.js"></script> 154 152 <script src="scripts/RootController.js"></script>
<script src="scripts/SettingsController.js"></script> 155 153 <script src="scripts/SettingsController.js"></script>
scripts/FeedController.js View file @ 8e3988f
angular.module('flashy.FeedController', 1 1 angular.module('flashy.FeedController',
['ui.router', 2 2 ['ui.router',
'ngAnimate', 3 3 'ngAnimate',
'ngWebSocket', 4 4 'ngWebSocket',
'contenteditable', 5 5 'contenteditable',
'flashy.DeckFactory']).controller('FeedController', 6 6 'flashy.DeckFactory']).controller('FeedController',
function($scope, $rootScope, $state, $http, $window, $timeout, $stateParams, $websocket, $interval, UserService, Flashcard, Deck) { 7 7 function($scope, $rootScope, $state, $http, $window, $timeout, $stateParams, $websocket, $interval, UserService, Flashcard, Deck) {
angular.module('flashy.CardGridController').CardGridController.apply(this, arguments); 8 8 angular.module('flashy.CardGridController').CardGridController.apply(this, arguments);
var promise; 9 9 var promise;
sectionId = parseInt($stateParams.sectionId); 10 10 sectionId = parseInt($stateParams.sectionId);
$scope.updateCardScore = function(card, scoreChange) { 11 11 $scope.updateCardScore = function(card, scoreChange) {
console.log('update card score'); 12 12 console.log('update card score');
// if no colNum is attached, then this doesn't exist on the feed yet 13 13 // if no colNum is attached, then this doesn't exist on the feed yet
if (typeof card.colNum === 'undefined' || typeof card.colRank === 'undefined') { 14 14 if (typeof card.colNum === 'undefined' || typeof card.colRank === 'undefined') {
console.log('no col num'); 15 15 console.log('no col num');
return; 16 16 return;
} 17 17 }
/*$scope.cardCols[card.colNum].sort(function(a, b) { 18 18 /*$scope.cardCols[card.colNum].sort(function(a, b) {
return b.score - a.score; 19 19 return b.score - a.score;
}); 20 20 });
console.log($scope.cardCols); 21 21 console.log($scope.cardCols);
$scope.updateColRanks($scope.cardCols[card.colNum]); 22 22 $scope.updateColRanks($scope.cardCols[card.colNum]);
return;*/ 23 23 return;*/
console.log(card.score); 24 24 console.log(card.score);
card.score += scoreChange; 25 25 card.score += scoreChange;
console.log(card.score); 26 26 console.log(card.score);
var col = card.colNum; 27 27 var col = card.colNum;
var rank = card.colRank; 28 28 var rank = card.colRank;
var s = Math.sign(scoreChange); 29 29 var s = Math.sign(scoreChange);
30 30
//rank -= s; 31 31 //rank -= s;
if (rank < 0) return; 32 32 if (rank < 0) return;
if (rank == $scope.cardCols[col].length) return; 33 33 if (rank == $scope.cardCols[col].length) return;
34 34
$scope.affectedCards = []; 35 35 $scope.affectedCards = [];
//console.log('before whiel, rank:' + rank); 36 36 //console.log('before whiel, rank:' + rank);
if (s > 0) { 37 37 if (s > 0) {
rank -= 1; 38 38 rank -= 1;
//console.log('+'); 39 39 //console.log('+');
while ($scope.cardCols[col][rank].score < card.score) { 40 40 while ($scope.cardCols[col][rank].score < card.score) {
//console.log('while'); 41 41 //console.log('while');
$scope.cardCols[col][rank].colRank += 1; 42 42 $scope.cardCols[col][rank].colRank += 1;
$scope.affectedCards.push($scope.cardCols[col][rank]); 43 43 $scope.affectedCards.push($scope.cardCols[col][rank]);
rank -= 1; 44 44 rank -= 1;
if (rank < 0) break; 45 45 if (rank < 0) break;
if (rank == $scope.cardCols[col].length) break; 46 46 if (rank == $scope.cardCols[col].length) break;
} 47 47 }
} else { 48 48 } else {
rank += 1; 49 49 rank += 1;
console.log('-'); 50 50 console.log('-');
while ($scope.cardCols[col][rank].score > card.score) { 51 51 while ($scope.cardCols[col][rank].score > card.score) {
//console.log('while'); 52 52 //console.log('while');
$scope.cardCols[col][rank].colRank -= 1; 53 53 $scope.cardCols[col][rank].colRank -= 1;
$scope.affectedCards.push($scope.cardCols[col][rank]); 54 54 $scope.affectedCards.push($scope.cardCols[col][rank]);
rank += 1; 55 55 rank += 1;
if (rank < 0) break; 56 56 if (rank < 0) break;
if (rank == $scope.cardCols[col].length) break; 57 57 if (rank == $scope.cardCols[col].length) break;
} 58 58 }
} 59 59 }
rank += s; 60 60 rank += s;
//console.log('after a whiel, rank:' + rank); 61 61 //console.log('after a whiel, rank:' + rank);
62 62
console.log($scope.affectedCards); 63 63 console.log($scope.affectedCards);
64 64
var upMove = $scope.height; 65 65 var upMove = $scope.height;
var downMove = $scope.height; 66 66 var downMove = $scope.height;
$scope.$apply(function() { 67 67 $scope.$apply(function() {
$('.card.flashy.card-moveUp').css({ 68 68 $('.card.flashy.card-moveUp').css({
'margin-top': 300 + 'px !important', // how much moveUp moves 69 69 'margin-top': 300 + 'px !important', // how much moveUp moves
}); 70 70 });
71 71
$('.card.flashy.card-moveDown').css({ 72 72 $('.card.flashy.card-moveDown').css({
'margin-top': 300 + 'px !important', // how much moveDown moves 73 73 'margin-top': 300 + 'px !important', // how much moveDown moves
}); 74 74 });
}); 75 75 });
76 76
if (s > 0) { 77 77 if (s > 0) {
card.moveUp = true; 78 78 card.moveUp = true;
upMove *= $scope.affectedCards.length; 79 79 upMove *= $scope.affectedCards.length;
/*for (i=0; i<$scope.affectedCards.length; i++) { 80 80 /*for (i=0; i<$scope.affectedCards.length; i++) {
$scope.affectedCards[i].moveDown = true; 81 81 $scope.affectedCards[i].moveDown = true;
}*/ 82 82 }*/
} else { 83 83 } else {
card.moveDown = true; 84 84 card.moveDown = true;
downMove *= $scope.affectedCards; 85 85 downMove *= $scope.affectedCards;
/*for (i=0; i<$scope.affectedCards.length; i++) { 86 86 /*for (i=0; i<$scope.affectedCards.length; i++) {
$scope.affectedCards[i].moveUp = true; 87 87 $scope.affectedCards[i].moveUp = true;
}*/ 88 88 }*/
} 89 89 }
90 90
$timeout(function() { 91 91 $timeout(function() {
$('.card.flashy.card-moveUp').css({ 92 92 $('.card.flashy.card-moveUp').css({
'margin-top': '6px', 93 93 'margin-top': '6px',
}); 94 94 });
95 95
$('.card.flashy.card-moveDown').css({ 96 96 $('.card.flashy.card-moveDown').css({
'margin-top': '6px', 97 97 'margin-top': '6px',
}); 98 98 });
if (s > 0) { 99 99 if (s > 0) {
card.moveUp = false; 100 100 card.moveUp = false;
for (i = 0; i < $scope.affectedCards.length; i++) { 101 101 for (i = 0; i < $scope.affectedCards.length; i++) {
$scope.affectedCards[i].moveDown = false; 102 102 $scope.affectedCards[i].moveDown = false;
} 103 103 }
} else { 104 104 } else {
card.moveDown = false; 105 105 card.moveDown = false;
for (i = 0; i < $scope.affectedCards.length; i++) { 106 106 for (i = 0; i < $scope.affectedCards.length; i++) {
$scope.affectedCards[i].moveUp = false; 107 107 $scope.affectedCards[i].moveUp = false;
} 108 108 }
} 109 109 }
}, 1000); 110 110 }, 1000);
$scope.cardCols[col].splice(rank, 0, $scope.cardCols[col].splice(card.colRank, 1)[0]); 111 111 $scope.cardCols[col].splice(rank, 0, $scope.cardCols[col].splice(card.colRank, 1)[0]);
card.colRank = rank; 112 112 card.colRank = rank;
//$scope.updateColRanks($scope.cardCols[card.colNum]); // can be optimized out 113 113 //$scope.updateColRanks($scope.cardCols[card.colNum]); // can be optimized out
}; 114 114 };
115 115
$scope.feed_ws = $websocket($scope.ws_host + '/ws/feed/' + sectionId + '?subscribe-broadcast'); 116 116 $scope.feed_ws = $websocket($scope.ws_host + '/ws/feed/' + sectionId + '?subscribe-broadcast');
$scope.feed_ws.onMessage(function(e) { 117 117 $scope.feed_ws.onMessage(function(e) {
data = JSON.parse(e.data); 118 118 data = JSON.parse(e.data);
console.log(data.event_type, 'message', data.flashcard); 119 119 console.log(data.event_type, 'message', data.flashcard);
if (data.event_type == 'new_card') { 120 120 if (data.event_type == 'new_card') {
$scope.addCardToGrid(new Flashcard(data.flashcard, $scope.deck)); 121 121 $scope.addCardToGrid(new Flashcard(data.flashcard, $scope.deck));
} else if (data.event_type == 'score_change') { 122 122 } else if (data.event_type == 'score_change') {
card = new Flashcard(data.flashcard); // doesnt create a card if it exists 123 123 card = new Flashcard(data.flashcard); // doesnt create a card if it exists
//card.score = data.flashcard.score; 124 124 //card.score = data.flashcard.score;
console.log('score change'); 125 125 console.log('score change');
$scope.updateCardScore(card, data.flashcard.score - card.score); 126 126 $scope.updateCardScore(card, data.flashcard.score - card.score);
} 127 127 }
}); 128 128 });
129 129
$scope.pushCard = function() { 130 130 $scope.pushCard = function() {
var myCard = { 131 131 var myCard = {
// we can't trim this string because it'd mess up the blanks. Something to fix. 132 132 // we can't trim this string because it'd mess up the blanks. Something to fix.
'text': $('#new-card-input').text(), 133 133 'text': $('#new-card-input').text(),
'mask': $scope.newCardBlanks, 134 134 'mask': $scope.newCardBlanks,
section: $scope.section.id 135 135 section: $scope.section.id
}; 136 136 };
if (!$scope.submit_enabled) { 137 137 if (!$scope.submit_enabled) {
return; 138 138 return;
} 139 139 }
if (myCard.text == '') { 140 140 if (myCard.text == '') {
console.log('blank flashcard not pushed:' + myCard.text); 141 141 console.log('blank flashcard not pushed:' + myCard.text);
return closeNewCard(); 142 142 return closeNewCard();
} 143 143 }
$http.post('/api/flashcards/', myCard). 144 144 $http.post('/api/flashcards/', myCard).
success(function(data) { 145 145 success(function(data) {
console.log('flashcard pushed: ' + myCard.text); 146 146 console.log('flashcard pushed: ' + myCard.text);
if (!UserService.hasVerifiedEmail()) { 147 147 if (!UserService.hasVerifiedEmail()) {
Materialize.toast("<p>Thanks for contributing! However, others won't see your card until you verify your email address<p>", 4000); 148 148 Materialize.toast("<p>Thanks for contributing! However, others won't see your card until you verify your email address<p>", 4000);
} 149 149 }
}); 150 150 });
return $scope.closeNewCardModal(); 151 151 return $scope.closeNewCardModal();
}; 152 152 };
153 153
/* Key bindings for the whole feed window. Hotkey it up! */ 154 154 /* Key bindings for the whole feed window. Hotkey it up! */
var listenForC = true; 155 155 var listenForC = true;
156 156
// Need to pass these options into openmodal and leanmodal, 157 157 // Need to pass these options into openmodal and leanmodal,
// otherwise the ready handler doesn't get called 158 158 // otherwise the ready handler doesn't get called
159 159
modal_options = { 160 160 modal_options = {
dismissible: true, // Modal can be dismissed by clicking outside of the modal 161 161 dismissible: true, // Modal can be dismissed by clicking outside of the modal
opacity: 0, // Opacity of modal background 162 162 opacity: 0, // Opacity of modal background
in_duration: 300, // Transition in duration 163 163 in_duration: 300, // Transition in duration
out_duration: 200, // Transition out duration 164 164 out_duration: 200, // Transition out duration
ready: function() { 165 165 ready: function() {
$('#new-card-input').focus(); 166 166 $('#new-card-input').focus();
document.execCommand('selectAll', false, null); 167 167 document.execCommand('selectAll', false, null);
} 168 168 }
}; 169 169 };
170 170
$(document).keydown(function(e) { 171 171 $(document).keydown(function(e) {
var keyed = e.which; 172 172 var keyed = e.which;
if (keyed == 67 && listenForC) { // "c" for compose 173 173 if (keyed == 67 && listenForC) { // "c" for compose
if ($scope.currentEditingCard) return; 174 174 if ($scope.currentEditingCard) return;
$scope.openNewCardModal(); 175 175 $scope.openNewCardModal();
e.preventDefault(); 176 176 e.preventDefault();
return false; 177 177 return false;
} else if (keyed == 27) { // clear on ESC 178 178 } else if (keyed == 27) { // clear on ESC
$scope.closeNewCardModal(); 179 179 $scope.closeNewCardModal();
$scope.discardEditChanges(); 180 180 $scope.discardEditChanges();
} 181 181 }
}); 182 182 });
183 183
$scope.openNewCardModal = function() { 184 184 $scope.openNewCardModal = function() {
$('#newCard').openModal(modal_options); 185 185 $('#newCard').openModal(modal_options);
listenForC = false; 186 186 listenForC = false;
$('#new-card-input').html('Write a flashcard!'); 187 187 $('#new-card-input').html('Write a flashcard!');
}; 188 188 };
189 189
$scope.closeNewCardModal = function() { 190 190 $scope.closeNewCardModal = function() {
listenForC = true; 191 191 listenForC = true;
$('#new-card-input').html('').blur(); 192 192 $('#new-card-input').html('').blur();
$('#newCard').closeModal(modal_options); 193 193 $('#newCard').closeModal(modal_options);
}; 194 194 };
195 195
$('.tooltipped').tooltip({delay: 50}); 196 196 $('.tooltipped').tooltip({delay: 50});
// the "href" attribute of .modal-trigger must specify the modal ID that wants to be triggered 197 197 // the "href" attribute of .modal-trigger must specify the modal ID that wants to be triggered
$('.modal-trigger').leanModal(modal_options); 198 198 $('.modal-trigger').leanModal(modal_options);
199 199
$('#new-card-input').on('keydown', function(e) { 200 200 $('#new-card-input').on('keydown', function(e) {
if (e.which == 13) { 201 201 if (e.which == 13) {
e.preventDefault(); 202 202 e.preventDefault();
if ($scope.submit_enabled) { 203 203 if ($scope.submit_enabled) {
$scope.pushCard(); 204 204 $scope.pushCard();
} 205 205 }
return false; 206 206 return false;
} 207 207 }
}); 208 208 });
209 209
$('button#blank-selected').click(function() { 210 210
console.log(window.getSelection()); 211
document.execCommand('bold'); 212
}); 213
$scope.newCardBlanks = []; 214 211 $scope.newCardBlanks = [];
$scope.refreshNewCardInput = function() { 215 212 $scope.refreshNewCardInput = function() {
$scope.newCardText = $('#new-card-input').text(); 216 213 $scope.newCardText = $('#new-card-input').text();
$scope.submit_enabled = $scope.newCardText.length >= 5 && $scope.newCardText.length <= 160; 217 214 $scope.submit_enabled = $scope.newCardText.length >= 5 && $scope.newCardText.length <= 160;
var i = 0; 218 215 var i = 0;
$scope.newCardBlanks = []; 219 216 $scope.newCardBlanks = [];
$('#new-card-input')[0].childNodes.forEach(function(node) { 220 217 $('#new-card-input')[0].childNodes.forEach(function(node) {
node = $(node)[0]; 221 218 node = $(node)[0];
if (node.tagName == 'B') { 222 219 if (node.tagName == 'B') {
var text = $(node).text(); 223 220 var text = $(node).text();
var leftspaces = 0, rightspaces = 0; 224 221 var leftspaces = 0, rightspaces = 0;
// awful way to find the first non-space character from the left or the right. thanks.js 225 222 // awful way to find the first non-space character from the left or the right. thanks.js
while (text[leftspaces] == ' ' || text[leftspaces] == '\xa0') leftspaces++; 226 223 while (text[leftspaces] == ' ' || text[leftspaces] == '\xa0') leftspaces++;
while (text[text.length - 1 - rightspaces] == ' ' || text[text.length - 1 - rightspaces] == '\xa0') rightspaces++; 227 224 while (text[text.length - 1 - rightspaces] == ' ' || text[text.length - 1 - rightspaces] == '\xa0') rightspaces++;
console.log(leftspaces, text.length); 228 225 console.log(leftspaces, text.length);
if (leftspaces != text.length) $scope.newCardBlanks.push([i + leftspaces, i + text.length - rightspaces]); 229 226 if (leftspaces != text.length) $scope.newCardBlanks.push([i + leftspaces, i + text.length - rightspaces]);
i += text.length; 230 227 i += text.length;
} else if (!node.data) { 231 228 } else if (!node.data) {
i += $(node).text().length; 232 229 i += $(node).text().length;
} else { 233 230 } else {
i += node.data.length; 234 231 i += node.data.length;
} 235 232 }
}); 236 233 });
$scope.newCardBlanks.sort(function(a, b) { 237 234 $scope.newCardBlanks.sort(function(a, b) {
return a[0] - b[0]; 238 235 return a[0] - b[0];
}); 239 236 });
i = 0; 240 237 i = 0;
newtext = ''; 241 238 newtext = '';
$scope.newCardBlanks.forEach(function(blank) { 242 239 $scope.newCardBlanks.forEach(function(blank) {
newtext += $scope.newCardText.slice(i, blank[0]); 243 240 newtext += $scope.newCardText.slice(i, blank[0]);
newtext += '<b>' + $scope.newCardText.slice(blank[0], blank[1]) + '</b>'; 244 241 newtext += '<b>' + $scope.newCardText.slice(blank[0], blank[1]) + '</b>';
i = blank[1]; 245 242 i = blank[1];
}); 246 243 });
newtext += $scope.newCardText.slice(i); 247 244 newtext += $scope.newCardText.slice(i);
//$scope.newCardFormattedText = newtext; 248 245 //$scope.newCardFormattedText = newtext;
}; 249 246 };
$scope.shuffleCards = function() { 250 247 $scope.shuffleCards = function() {
$timeout(function() { 251 248 $timeout(function() {
(function(o) { 252 249 (function(o) {
for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x); 253 250 for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o; 254 251 return o;
})($scope.cardCols[0]); 255 252 })($scope.cardCols[0]);
}); 256 253 });
}; 257 254 };
scripts/FlashcardFactory.js View file @ 8e3988f
angular.module('flashy.FlashcardFactory', ['ui.router']). 1 1 angular.module('flashy.FlashcardFactory', ['ui.router']).
factory('Flashcard', function ($http, UserService) { 2 2 factory('Flashcard', function ($http, UserService) {
var FlashcardCache = []; 3 3 var FlashcardCache = [];
var Deck = null; 4 4 var Deck = null;
var Flashcard = function (data) { 5 5 var Flashcard = function (data) {
if (typeof data == 'number') return FlashcardCache[data]; 6 6 if (typeof data == 'number') return FlashcardCache[data];
if (FlashcardCache[data.id]) return FlashcardCache[data.id]; 7 7 if (FlashcardCache[data.id]) return FlashcardCache[data.id];
for (var k in data) this[k] = data[k]; 8 8 for (var k in data) this[k] = data[k];
this.mask = data.display_mask; 9 9 this.mask = data.display_mask;
this.moveUp = false; 10 10 this.moveUp = false;
this.moveDown = false; 11 11 this.moveDown = false;
FlashcardCache[this.id] = this; 12 12 FlashcardCache[this.id] = this;
this.refreshFormattedText(); 13 13 this.refreshFormattedText();
}; 14 14 };
Flashcard.prototype.refreshFormattedText = function () { 15 15 Flashcard.prototype.refreshFormattedText = function () {
this.textPieces = []; 16 16 this.textPieces = [];
this.mask.sort(function (a, b) { 17 17 this.mask.sort(function (a, b) {
return a[0] - b[0]; 18 18 return a[0] - b[0];
}); 19 19 });
var i = 0; 20 20 var i = 0;
this.mask.forEach(function (blank) { 21 21 this.mask.forEach(function (blank) {
this.textPieces.push({text: this.text.slice(i, blank[0])}); 22 22 this.textPieces.push({text: this.text.slice(i, blank[0])});
this.textPieces.push({text: this.text.slice(blank[0], blank[1]), blank: true}); 23 23 this.textPieces.push({text: this.text.slice(blank[0], blank[1]), blank: true});
i = blank[1]; 24 24 i = blank[1];
}, this); 25 25 }, this);
this.textPieces.push({text: this.text.slice(i)}); 26 26 this.textPieces.push({text: this.text.slice(i)});
this.formatted_text = ''; 27 27 this.formatted_text = '';
for (i in this.textPieces) { 28 28 for (i in this.textPieces) {
p = this.textPieces[i]; 29 29 p = this.textPieces[i];
this.formatted_text += p.blank ? '<b>' + p.text + '</b>' : p.text; 30 30 this.formatted_text += p.blank ? '<b>' + p.text + '</b>' : p.text;
} 31 31 }
return this; 32 32 return this;
}; 33 33 };
Flashcard.prototype.isAuthoredByUser = function () { 34 34 Flashcard.prototype.isAuthoredByUser = function () {
return this.is_authored_by_user; 35 35 return this.is_authored_by_user;
}; 36 36 };
Flashcard.prototype.isInDeck = function () { 37 37 Flashcard.prototype.isInDeck = function () {
return !(typeof Deck.contains(this.id) === 'undefined'); 38 38 return !(typeof Deck.contains(this.id) === 'undefined');
}; 39 39 };
Flashcard.prototype.pullUnpull = function () { 40 40 Flashcard.prototype.pullUnpull = function () {
if (this.isInDeck()) this.unpull(); 41 41 if (this.isInDeck()) this.unpull();
else this.pull(); 42 42 else this.pull();
}; 43 43 };
Flashcard.prototype.pull = function () { 44 44 Flashcard.prototype.pull = function () {
if (this.isInDeck()) return console.log('Not pulling', this.id, "because it's already in deck"); 45 45 if (this.isInDeck()) return console.log('Not pulling', this.id, "because it's already in deck");
return $http.post('/api/flashcards/' + this.id + '/pull/'); 46 46 return $http.post('/api/flashcards/' + this.id + '/pull/');
}; 47 47 };
Flashcard.prototype.unpull = function () { 48 48 Flashcard.prototype.unpull = function () {
if (!this.isInDeck()) return console.log('Not unpulling', this.id, "because it's not in deck"); 49 49 if (!this.isInDeck()) return console.log('Not unpulling', this.id, "because it's not in deck");
return $http.post('/api/flashcards/' + this.id + '/unpull/'); 50 50 return $http.post('/api/flashcards/' + this.id + '/unpull/');
}; 51 51 };
Flashcard.prototype.hide = function () { 52 52 Flashcard.prototype.hide = function () {
return $http.post('/api/flashcards/' + this.id + '/hide/'); 53 53 return $http.post('/api/flashcards/' + this.id + '/hide/');
}; 54 54 };
Flashcard.prototype.report = function (reason) { 55 55 Flashcard.prototype.report = function (reason) {
if (!reason) reason = 'unspecified'; 56 56 if (!reason) reason = 'unspecified';
this.isReported = true; 57 57 this.isReported = true;
return $http.post('/api/flashcards/' + this.id + '/report/', {reason: reason}).success(function () { 58 58 return $http.post('/api/flashcards/' + this.id + '/report/', {reason: reason}).success(function () {
Materialize.toast('Report successful; thanks for letting us know!', 4000); 59 59 Materialize.toast('Report successful; thanks for letting us know!', 4000);
}); 60 60 });
}; 61 61 };
62 62
Flashcard.prototype.unhide = function () { 63 63 Flashcard.prototype.unhide = function () {
return $http.post('/api/flashcards/' + this.id + '/unhide/'); 64 64 return $http.post('/api/flashcards/' + this.id + '/unhide/');
}; 65 65 };
66 66
Flashcard.prototype.edit = function (newversion) { 67 67 Flashcard.prototype.edit = function (newversion) {
this.text = newversion.text; 68 68 this.text = newversion.text;
this.mask = newversion.display_mask; 69 69 this.mask = newversion.display_mask;
this.material_date = newversion.material_date; 70 70 this.material_date = newversion.material_date;
return this.refreshFormattedText(); 71 71 return this.refreshFormattedText();
}; 72 72 };
73
Flashcard.prototype.editModalOptions = { 74 73 Flashcard.prototype.editModalOptions = {
dismissible: true, // Modal can be dismissed by clicking outside of the modal 75 74 dismissible: true, // Modal can be dismissed by clicking outside of the modal
opacity: 0, // Opacity of modal background 76 75 opacity: 0, // Opacity of modal background
in_duration: 300, // Transition in duration 77 76 in_duration: 300, // Transition in duration
out_duration: 200, // Transition out duration 78 77 out_duration: 200, // Transition out duration
}; 79 78 };
80 79
Flashcard.cleanup = function () { 81 80 Flashcard.cleanup = function () {
Deck = null; 82 81 Deck = null;
FlashcardCache = []; 83 82 FlashcardCache = [];
}; 84 83 };
85 84
scripts/HelpController.js View file @ 8e3988f
angular.module('flashy.HelpController', ['ui.router']). 1 1 angular.module('flashy.HelpController', ['ui.router']).
2 2
controller('HelpController', ['$scope', '$state', '$http', '$timeout', 'UserService', 3 3 controller('HelpController', function($scope, $state, $http, $timeout) {
function($scope, $state, $http, $timeout, UserService) { 4 4 $scope.toggleContent = function(event, index) {
$scope.toggleContent = function(event, index) { 5 5 console.log(event, index);
console.log(event, index); 6
7 6
if ($('#content-' + index).hasClass('open')) { // let's close it 8 7 if ($('#content-' + index).hasClass('open')) { // let's close it
// Note: 250 is duration (ms) of animation 9 8 // Note: 250 is duration (ms) of animation
$('#content-' + index).slideUp(250).removeClass('open'); 10 9 $('#content-' + index).slideUp(250).removeClass('open');
} else { // let'd open it 11 10 } else { // let'd open it
$('#content-' + index).slideDown(250).addClass('open'); 12 11 $('#content-' + index).slideDown(250).addClass('open');
} 13 12 }
14 13
// event.currentTarget 15 14 // event.currentTarget
}; 16 15 };
17 16
$scope.closeContent = function(event) { 18 17 $scope.closeContent = function(event) {
19 18
}; 20 19 };
21 20
// JSON OF FAQ ENTRIES 22 21 // JSON OF FAQ ENTRIES
$scope.entries = [ 23 22 $scope.entries = [
{ 24 23 {
// icon: 'mdi-editor-insert-emoticon small', 25 24 // icon: 'mdi-editor-insert-emoticon small',
question: 'What is Flashy?', 26 25 question: 'What is Flashy?',
answer: '<p>Flashy is a service for creating, sharing, and reviewing flashcards for your courses.' + 27 26 answer: '<p>Flashy is a service for creating, sharing, and reviewing flashcards for your courses.' +
'</p><p>Flashy is optimized for contributing cards in real time during lecture to a shared live' + 28 27 '</p><p>Flashy is optimized for contributing cards in real time during lecture to a shared live' +
" feed. Don't want to contribute cards? That's fine! By adding others' cards to your deck, you" + 29 28 " feed. Don't want to contribute cards? That's fine! By adding others' cards to your deck, you" +
' help identify high-quality cards which should remain at the top of the feed for others to choose.' + 30 29 ' help identify high-quality cards which should remain at the top of the feed for others to choose.' +
'</p><p>Based on the principles of spaced repetition, Flashy also intelligently determines which' + 31 30 '</p><p>Based on the principles of spaced repetition, Flashy also intelligently determines which' +
' cards you are most at risk of forgetting, based on your review history. Receive push ' + 32 31 ' cards you are most at risk of forgetting, based on your review history. Receive push ' +
"notifications on your Android device's Chrome browser without installing any other app," + 33 32 "notifications on your Android device's Chrome browser without installing any other app," +
" and we'll notify you when you have a few cards which need to be reviewed.</p>" 34 33 " and we'll notify you when you have a few cards which need to be reviewed.</p>"
}, 35 34 },
{ 36 35 {
// icon: 'mdi-file-cloud-queue small', 37 36 // icon: 'mdi-file-cloud-queue small',
question: 'Does Flashy work outside of UCSD?', 38 37 question: 'Does Flashy work outside of UCSD?',
answer: "To simplify development, Flashy was configured only for courses at UCSD. If you'd like Flashy for your school, please send us an email to let us know!" 39 38 answer: "To simplify development, Flashy was configured only for courses at UCSD. If you'd like Flashy for your school, please send us an email to let us know!"
}, 40 39 },
{ 41 40 {
// icon: 'mdi-hardware-security small', 42 41 // icon: 'mdi-hardware-security small',
question: 'How do registration and verification work?', 43 42 question: 'How do registration and verification work?',
answer: "An account is required to use Flashy. We store the cards you save to your deck with your account. When you register, you'll be able to use the site immediately, but you must verify ownership of your email address within 24 hours. After 24 hours have passed, you'll need to verify your address before continuing to use the site. Don't worry, your cards and deck won't be deleted." 44 43 answer: "An account is required to use Flashy. We store the cards you save to your deck with your account. When you register, you'll be able to use the site immediately, but you must verify ownership of your email address within 24 hours. After 24 hours have passed, you'll need to verify your address before continuing to use the site. Don't worry, your cards and deck won't be deleted."
}, 45 44 },
{ 46 45 {
question: "My question isn't answered above", 47 46 question: "My question isn't answered above",
answer: ' <a href="mailto:or.so.help.me@flashy.cards">Send us an email!</a>' 48 47 answer: ' <a href="mailto:or.so.help.me@flashy.cards">Send us an email!</a>'
}]; 49 48 }];
50 49
// Functions 51 50 // Functions
$scope.toggleContent = function(event, index) { 52 51 $scope.toggleContent = function(event, index) {
if ($('#content-' + index).hasClass('open')) { // let's close it 53 52 if ($('#content-' + index).hasClass('open')) { // let's close it
// Note: 250 is duration (ms) of animation 54 53 // Note: 250 is duration (ms) of animation
$('#content-' + index).slideUp(250).removeClass('open'); 55 54 $('#content-' + index).slideUp(250).removeClass('open');
} else { // let's open it 56 55 } else { // let's open it
$('#content-' + index).slideDown(250).addClass('open'); 57 56 $('#content-' + index).slideDown(250).addClass('open');
} 58 57 }
}; 59 58 };
60 59
$scope.expandAllContent = function() { 61 60 $scope.expandAllContent = function() {
for (var i = 0; i < $scope.entries.length; i++) { 62 61 for (var i = 0; i < $scope.entries.length; i++) {
$('#content-' + i).slideDown(0).addClass('open'); 63 62 $('#content-' + i).slideDown(0).addClass('open');
} 64 63 }
}; 65 64 };
66 65
$scope.collapseAllContent = function() { 67 66 $scope.collapseAllContent = function() {
for (var i = 0; i < $scope.entries.length; i++) { 68 67 for (var i = 0; i < $scope.entries.length; i++) {
$('#content-' + i).slideUp(0).removeClass('open'); 69 68 $('#content-' + i).slideUp(0).removeClass('open');
} 70 69 }
}; 71 70 };
}]); 72 71 });
73 72
templates/feed.html View file @ 8e3988f
<!-- Edit Modal --> 1 1 <!-- Edit Modal -->
<div id="editModal" class="modal row" style="max-height:none;"> 2 2 <div id="editModal" class="modal row" style="max-height:none;">
<form id="edit-card-form"> 3 3 <form id="edit-card-form">
<div class="modal-content col"> 4 4 <div class="modal-content col">
<div class="row" style="margin-bottom:0"> 5 5 <div class="row" style="margin-bottom:0">
<div class="card cyan-text text-darken-2" 6 6 <div class="card cyan-text text-darken-2"
style="width:300px; height:180px; margin-bottom:0; font-size:120%;"> 7 7 style="width:300px; height:180px; margin-bottom:0; font-size:120%;">
<div class="valign-wrapper"> 8 8 <div class="valign-wrapper">
<div id="edit-card-input" ng-model="editCardFormattedText" style="outline:0px solid transparent;" 9 9 <div id="edit-card-input" ng-model="editCardFormattedText" style="outline:0px solid transparent;"
class="card-content valign center-align" 10 10 class="card-content valign center-align"
contenteditable select-non-editable="true" ng-change="refreshEditCardInput()"> 11 11 contenteditable select-non-editable="true" ng-change="refreshEditCardInput()">
</div> 12 12 </div>
</div> 13 13 </div>
</div> 14 14 </div>
</div> 15 15 </div>
</div> 16 16 </div>
<div class="col"> 17 17 <div class="col">
<div class="row"> 18 18 <div class="row">
</div> 19 19 </div>
<div class="row"> 20 20 <div class="row">
<button class="btn modal-close" type="submit" ng-click="saveEditChanges()" 21 21 <button class="btn modal-close" type="submit" ng-click="saveEditChanges()"
data-position="left" 22 22 data-position="left"
data-delay="50" ng-class="submit_enabled?{}:'disabled'"> 23 23 data-delay="50" ng-class="submit_enabled?{}:'disabled'">
Save Changes 24 24 Save Changes
<i class="mdi-action-done right"></i> 25 25 <i class="mdi-action-done right"></i>
</button> 26 26 </button>
</div> 27 27 </div>
28 28
29 29
<div class="row"> 30 30 <div class="row">
<button class="btn modal-close red" ng-click="discardEditChanges()" 31 31 <button class="btn modal-close red" ng-click="discardEditChanges()"
data-position="left" 32 32 data-position="left"
data-delay="50"> 33 33 data-delay="50">
Discard Changes 34 34 Discard Changes
<i class="mdi-content-clear right"></i> 35 35 <i class="mdi-content-clear right"></i>
</button> 36 36 </button>
</div> 37 37 </div>
38 38
<div class="row"> 39 39 <div class="row">
<button id="blank-selected" style="float:left" class="btn" data-position="right" 40 40 <button class="blank-selected-btn btn tooltipped" style="float:left" onclick="document.execCommand('bold')" data-position="right" data-delay="50">
onclick="document.execCommand('bold')" data-delay="50"> 41
Blank Selected Text 42 41 Blank Selected Text
</button> 43 42 </button>
</div> 44 43 </div>
45 44
46 45
<div class="row" ng-show="editCardText" ng-style="(editCardText.length>160)?{color:'red'}:{}"> 47 46 <div class="row" ng-show="editCardText" ng-style="(editCardText.length>160)?{color:'red'}:{}">
{{editCardText.length}}/160 characters 48 47 {{editCardText.length}}/160 characters
</div> 49 48 </div>
<div class="row" ng-show="editCardText.length < 5"> 50 49 <div class="row" ng-show="editCardText.length < 5">
Please write a little more! 51 50 Please write a little more!
</div> 52 51 </div>
<div class="row" ng-show="editCardText.length > 140"> 53 52 <div class="row" ng-show="editCardText.length > 140">
Good flashcards have a<br> 54 53 Good flashcards have a<br>
single atomic fact 55 54 single atomic fact
</div> 56 55 </div>
</div> 57 56 </div>
</form> 58 57 </form>
</div> 59 58 </div>
60 59
61 60
<div class="row"> 62 61 <div class="row">
<h2 class="weight" ng-cloak ng-show="showGrid && cards.length == 0">No cards. Be the first one to add a card!</h2> 63 62 <h2 class="weight" ng-cloak ng-show="showGrid && cards.length == 0">No cards. Be the first one to add a card!</h2>
64 63
<div class="progress center-align" style="margin: 70px auto auto;width:50%;" ng-if="!cardCols.length || !showGrid"> 65 64 <div class="progress center-align" style="margin: 70px auto auto;width:50%;" ng-if="!cardCols.length || !showGrid">
<div class="indeterminate"></div> 66 65 <div class="indeterminate"></div>
</div> 67 66 </div>
68 67
<div class="cardColumn" ng-repeat="col in cardCols"> 69 68 <div class="cardColumn" ng-repeat="col in cardCols">
<div class="repeated-card" ng-repeat="card in col"> 70 69 <div class="repeated-card" ng-repeat="card in col">
<flashcard flashcard-obj="card" edit-fn="editCard"/> 71 70 <flashcard flashcard-obj="card" edit-fn="editCard"/>
</div> 72 71 </div>
</div> 73 72 </div>
</div> 74 73 </div>
75 74
76 75
<!--Lil plus button in corner--> 77 76 <!--Lil plus button in corner-->
<div class="fixed-action-btn" style="bottom: 24px; right: 24px;"> 78 77 <div class="fixed-action-btn" style="bottom: 24px; right: 24px;">
<a data-target="newCard" class="btn-floating btn-large modal-trigger tooltipped" data-position="left" 79 78 <a data-target="newCard" class="btn-floating btn-large modal-trigger tooltipped" data-position="left"
data-delay="50" ng-click="openNewCardModal()" 80 79 data-delay="50" ng-click="openNewCardModal()"
data-tooltip="(C)ompose"> 81 80 data-tooltip="(C)ompose">
<i class="large mdi-content-add"></i> 82 81 <i class="large mdi-content-add"></i>
</a> 83 82 </a>
</div> 84 83 </div>
85 84
<div id="newCard" class="modal bottom-sheet row" style="max-height:none;"> 86 85 <div id="newCard" class="modal bottom-sheet row" style="max-height:none;">
<form id="new-card-form" ng-submit="false"> 87 86 <form id="new-card-form" ng-submit="false">
<div class="modal-content col"> 88 87 <div class="modal-content col">
<div class="row" style="margin-bottom:0"> 89 88 <div class="row" style="margin-bottom:0">
<div class="card cyan-text text-darken-2" 90 89 <div class="card cyan-text text-darken-2"
style="width:300px; height:180px; margin-bottom:0; font-size:120%;"> 91 90 style="width:300px; height:180px; margin-bottom:0; font-size:120%;">
<div class="valign-wrapper"> 92 91 <div class="valign-wrapper">
<div id="new-card-input" ng-model="newCardFormattedText" style="outline:0px solid transparent;" 93 92 <div id="new-card-input" ng-model="newCardFormattedText" style="outline:0px solid transparent;"
class="card-content valign center-align" 94 93 class="card-content valign center-align"
contenteditable select-non-editable="true" ng-change="refreshNewCardInput()"> 95 94 contenteditable select-non-editable="true" ng-change="refreshNewCardInput()">
96 95
</div> 97 96 </div>
</div> 98 97 </div>
99 98
</div> 100 99 </div>
</div> 101 100 </div>
</div> 102 101 </div>
103 102
<div class="col"> 104 103 <div class="col">
<div class="row"> 105 104 <div class="row">
106 105
</div> 107 106 </div>
<div class="row"> 108 107 <div class="row">
<button class="btn modal-close tooltipped" type="submit" ng-click="pushCard()" 109 108 <button class="btn modal-close tooltipped" type="submit" ng-click="pushCard()"
data-position="left" 110 109 data-position="left"
data-delay="50" ng-class="submit_enabled?{}:'disabled'" 111 110 data-delay="50" ng-class="submit_enabled?{}:'disabled'"
data-tooltip="Enter">Contribute 112 111 data-tooltip="Enter">Contribute
<i class="mdi-hardware-keyboard-return right"></i> 113 112 <i class="mdi-hardware-keyboard-return right"></i>
</button> 114 113 </button>
</div> 115 114 </div>
<div class="row"> 116 115 <div class="row">
<button id="blank-selected" style="float:left" class="btn tooltipped" data-position="right" data-delay="50" 117 116 <button style="float:left" class="blank-selected-btn btn tooltipped" data-position="right" data-delay="50"
data-tooltip="Ctrl-B">Blank Selected Text 118 117 data-tooltip="Ctrl-B" onclick="document.execCommand('bold')" >Blank Selected Text
</button> 119 118 </button>
</div> 120 119 </div>
<div class="row" ng-show="newCardText" ng-style="(newCardText.length>160)?{color:'red'}:{}"> 121 120 <div class="row" ng-show="newCardText" ng-style="(newCardText.length>160)?{color:'red'}:{}">