Commit fcda9ffd202c5776281cd13f912e56c42671ce49

Authored by Tetranoir
1 parent 3c77a71a36

feed controller attempt at organizing by score (in progress, non breaky vers); t…

…ried to get verifyemail speshul authentication measures (none), hit a road block with http.interceptor

Showing 6 changed files with 61 additions and 23 deletions Inline Diff

angular.module('flashy', [ 1 1 angular.module('flashy', [
'flashy.LogoutController', 2 2 'flashy.LogoutController',
'flashy.LoginController', 3 3 'flashy.LoginController',
'flashy.RootController', 4 4 'flashy.RootController',
'flashy.FeedController', 5 5 'flashy.FeedController',
'flashy.DeckController', 6 6 'flashy.DeckController',
'flashy.ClassAddController', 7 7 'flashy.ClassAddController',
'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 12 //'flashy.SelectDirective',
// DOESNT WORK RN 13 13 // DOESNT WORK RN
'flashy.ResetPasswordController', 14 14 'flashy.ResetPasswordController',
'flashy.VerifyEmailController', 15 15 'flashy.VerifyEmailController',
'flashy.CardListController', 16 16 'flashy.CardListController',
'flashy.HelpController', 17 17 'flashy.HelpController',
'flashy.SettingsController', 18 18 'flashy.SettingsController',
'ngCookies']). 19 19 'ngCookies']).
config(function($stateProvider, $urlRouterProvider, $resourceProvider, $httpProvider, $locationProvider) { 20 20 config(function($stateProvider, $urlRouterProvider, $resourceProvider, $httpProvider, $locationProvider) {
'use strict'; 21 21 'use strict';
$httpProvider.defaults.withCredentials = true; 22 22 $httpProvider.defaults.withCredentials = true;
$httpProvider.defaults.xsrfCookieName = 'csrftoken'; 23 23 $httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'; 24 24 $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
$resourceProvider.defaults.stripTrailingSlashes = false; 25 25 $resourceProvider.defaults.stripTrailingSlashes = false;
var arrayMethods = Object.getOwnPropertyNames(Array.prototype); 26 26 var arrayMethods = Object.getOwnPropertyNames(Array.prototype);
arrayMethods.forEach(attachArrayMethodsToNodeList); 27 27 arrayMethods.forEach(attachArrayMethodsToNodeList);
function attachArrayMethodsToNodeList(methodName) { 28 28 function attachArrayMethodsToNodeList(methodName) {
if (methodName !== 'length') { 29 29 if (methodName !== 'length') {
NodeList.prototype[methodName] = Array.prototype[methodName]; 30 30 NodeList.prototype[methodName] = Array.prototype[methodName];
} 31 31 }
} 32 32 }
33 33
$httpProvider.interceptors.push(function($q, $rootScope) { 34 34 $httpProvider.interceptors.push(function($q, $rootScope) {
return { 35 35 return {
'responseError': function(rejection) { // need a better redirect 36 36 'responseError': function(rejection) { // need a better redirect
if (rejection.status >= 500) { 37 37 if (rejection.status >= 500) {
console.log('got error'); 38 38 console.log('got error');
console.log(rejection); 39 39 console.log(rejection);
$rootScope.$broadcast('server_error', rejection); 40 40 $rootScope.$broadcast('server_error', rejection);
} 41 41 }
if (rejection.status == 403) { 42 42 if (rejection.status == 403) {
console.log(rejection); 43 43 console.log(rejection);
if (rejection.data && rejection.data.detail == 'Please verify your email before continuing') { 44 44 if (rejection.data && rejection.data.detail == 'Please verify your email before continuing') {
$rootScope.$broadcast('account_locked'); 45 45 $rootScope.$broadcast('account_locked');
} 46 46 }
} 47 47 }
return $q.reject(rejection); 48 48 return $q.reject(rejection);
} 49 49 }
}; 50 50 };
}); 51 51 });
$locationProvider.html5Mode(true); 52 52 $locationProvider.html5Mode(true);
$urlRouterProvider.otherwise('/404'); 53 53 $urlRouterProvider.otherwise('/404');
var auth_resolve = { 54 54 var auth_resolve = {
authorize: function($q, $state, $stateParams, UserService) { 55 55 authorize: function($q, $state, $stateParams, UserService) {
console.log('resolving user before continuing'); 56 56 if (UserService.noAuthRequired($state)) {
57 console.log('no auth state ' + $state.name);
58 return $state.go(state.name);
59 }
60 console.log('resolving user before continuing for ' + $state.name);
var redirectAsNeeded = function() { 57 61 var redirectAsNeeded = function() {
if (!UserService.isLoggedIn()) { 58 62 if (!UserService.isLoggedIn()) {
console.log(UserService.getUserData()); 59 63 console.log(UserService.getUserData());
console.log('making the user log in'); 60 64 console.log('making the user log in');
$state.go('login'); 61 65 $state.go('login');
} 62 66 }
if (!UserService.authorizedFor($state, $stateParams)) { 63 67 if (!UserService.authorizedFor($state, $stateParams)) {
68 console.log('user not authorized for ' + $state.name);
$state.go('addclass'); 64 69 $state.go('addclass');
} 65 70 }
}; 66 71 };
if (UserService.isResolved()) return redirectAsNeeded(); 67 72 if (UserService.isResolved()) return redirectAsNeeded();
return UserService.getUserData().then(redirectAsNeeded); 68 73 return UserService.getUserData().then(redirectAsNeeded);
} 69 74 }
}; 70 75 };
$stateProvider. 71 76 $stateProvider.
state('login', { 72 77 state('login', {
url: '/login', 73 78 url: '/login',
templateUrl: 'templates/login.html', 74 79 templateUrl: 'templates/login.html',
controller: 'LoginController' 75 80 controller: 'LoginController'
}). 76 81 }).
state('logout', { 77 82 state('logout', {
resolve: auth_resolve, 78 83 resolve: auth_resolve,
url: '/logout', 79 84 url: '/logout',
templateUrl: 'templates/logout.html', 80 85 templateUrl: 'templates/logout.html',
controller: 'LogoutController' 81 86 controller: 'LogoutController'
}). 82 87 }).
state('root', { 83 88 state('root', {
resolve: auth_resolve, 84 89 resolve: auth_resolve,
url: '', 85 90 url: '',
controller: 'RootController' 86 91 controller: 'RootController'
}). 87 92 }).
state('feed', { 88 93 state('feed', {
resolve: auth_resolve, 89 94 resolve: auth_resolve,
url: '/feed/{sectionId}', 90 95 url: '/feed/{sectionId}',
templateUrl: 'templates/feed.html', 91 96 templateUrl: 'templates/feed.html',
controller: 'FeedController' 92 97 controller: 'FeedController'
}). 93 98 }).
state('cardlist', { 94 99 state('cardlist', {
resolve: auth_resolve, 95 100 resolve: auth_resolve,
url: '/cards/{sectionId}', 96 101 url: '/cards/{sectionId}',
templateUrl: 'templates/cardlist.html', 97 102 templateUrl: 'templates/cardlist.html',
controller: 'CardListController' 98 103 controller: 'CardListController'
}). 99 104 }).
state('addclass', { 100 105 state('addclass', {
resolve: auth_resolve, 101 106 resolve: auth_resolve,
url: '/addclass', 102 107 url: '/addclass',
templateUrl: 'templates/addclass.html', 103 108 templateUrl: 'templates/addclass.html',
controller: 'ClassAddController' 104 109 controller: 'ClassAddController'
}). 105 110 }).
state('deck', { 106 111 state('deck', {
resolve: auth_resolve, 107 112 resolve: auth_resolve,
url: '/deck/{sectionId}', 108 113 url: '/deck/{sectionId}',
templateUrl: 'templates/deck.html', 109 114 templateUrl: 'templates/deck.html',
controller: 'DeckController' 110 115 controller: 'DeckController'
}). 111 116 }).
state('study', { 112 117 state('study', {
resolve: auth_resolve, 113 118 resolve: auth_resolve,
url: '/study', 114 119 url: '/study',
templateUrl: 'templates/study.html', 115 120 templateUrl: 'templates/study.html',
controller: 'StudyController' 116 121 controller: 'StudyController'
}). 117 122 }).
state('flashcard', { 118 123 state('flashcard', {
resolve: auth_resolve, 119 124 resolve: auth_resolve,
url: '/flashcard', 120 125 url: '/flashcard',
templateUrl: 'templates/flashcard.html', 121 126 templateUrl: 'templates/flashcard.html',
controller: 'FlashcardController' 122 127 controller: 'FlashcardController'
}). 123 128 }).
state('settings', { 124 129 state('settings', {
resolve: auth_resolve, 125 130 resolve: auth_resolve,
url: '/settings', 126 131 url: '/settings',
templateUrl: 'templates/settings.html', 127 132 templateUrl: 'templates/settings.html',
controller: 'SettingsController' 128 133 controller: 'SettingsController'
}). 129 134 }).
state('requestpasswordreset', { 130 135 state('requestpasswordreset', {
url: '/requestpasswordreset', 131 136 url: '/requestpasswordreset',
templateUrl: 'templates/requestpasswordreset.html', 132 137 templateUrl: 'templates/requestpasswordreset.html',
controller: 'RequestResetController' 133 138 controller: 'RequestResetController'
}). 134 139 }).
state('resetpassword', { 135 140 state('resetpassword', {
url: '/resetpassword/{uid}/{token}', 136 141 url: '/resetpassword/{uid}/{token}',
templateUrl: 'templates/resetpassword.html', 137 142 templateUrl: 'templates/resetpassword.html',
controller: 'ResetPasswordController' 138 143 controller: 'ResetPasswordController'
}). 139 144 }).
state('verifyemail', { 140 145 state('verifyemail', {
resolve: auth_resolve, 141
url: '/verifyemail/{key}', 142 146 url: '/verifyemail/{key}',
templateUrl: 'templates/verifyemail.html', 143 147 templateUrl: 'templates/verifyemail.html',
controller: 'VerifyEmailController' 144 148 controller: 'VerifyEmailController'
}). 145 149 }).
state('404', { 146 150 state('404', {
url: '/404', 147 151 url: '/404',
template: "<h1>This page doesn't exist!</h1>" 148 152 template: "<h1>This page doesn't exist!</h1>"
}). 149 153 }).
state('help', { 150 154 state('help', {
resolve: auth_resolve, 151 155 resolve: auth_resolve,
url: '/help', 152 156 url: '/help',
templateUrl: 'templates/help.html', 153 157 templateUrl: 'templates/help.html',
controller: 'HelpController' 154 158 controller: 'HelpController'
}); 155 159 });
}). 156 160 }).
run(function($rootScope, $state, $stateParams, $location, UserService) { 157 161 run(function($rootScope, $state, $stateParams, $location, UserService) {
$rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error) { 158 162 $rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error) {
console.log('failed to change state: ' + error); 159 163 console.log('failed to change state: ' + error);
$state.go('login'); 160 164 $state.go('login');
}); 161 165 });
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) { 162 166 $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
if (['feed', 'deck', 'cardlist'].indexOf(toState.name) >= 0) { 163 167 if (['feed', 'deck', 'cardlist'].indexOf(toState.name) >= 0) {
localStorage.setItem('last_state', toState.name); 164 168 localStorage.setItem('last_state', toState.name);
localStorage.setItem('last_state_params', JSON.stringify(toParams)); 165 169 localStorage.setItem('last_state_params', JSON.stringify(toParams));
} 166 170 }
}); 167 171 });
}); 168 172 });
scripts/FeedController.js View file @ fcda9ff
angular.module('flashy.FeedController', ['ui.router', 'ngAnimate']). 1 1 angular.module('flashy.FeedController', ['ui.router', 'ngAnimate']).
2 2
controller('FeedController', function($scope, $rootScope, $stateParams, $state, $http, $window, $timeout, UserService) { 3 3 controller('FeedController', function($scope, $rootScope, $stateParams, $state, $http, $window, $timeout, UserService) {
console.log('Hello from feed'); 4 4 console.log('Hello from feed');
sectionId = parseInt($stateParams.sectionId); 5 5 sectionId = parseInt($stateParams.sectionId);
if (!UserService.isInSection(sectionId)) { 6 6 if (!UserService.isInSection(sectionId)) {
console.log('user is not enrolled in ' + sectionId); 7 7 console.log('user is not enrolled in ' + sectionId);
return $state.go('addclass'); 8 8 return $state.go('addclass');
} 9 9 }
$rootScope.currentSection = $rootScope.SectionResource.get({sectionId: sectionId}); 10 10 $rootScope.currentSection = $rootScope.SectionResource.get({sectionId: sectionId});
$scope.cards = false; 11 11 $scope.cards = false;
$scope.cardCols = []; // organized data 12 12 $scope.cardCols = []; // organized data
$scope.numCols = 0; 13 13 $scope.numCols = 0;
14 $scope.cardTable = {}; // look up table of cards, {'colNum':col, 'obj':card}
14 15
15
function calculate_cols() { 16 16 function calculate_cols() {
var avail = $window.innerWidth - 17; 17 17 var avail = $window.innerWidth - 17;
return Math.max(1, Math.floor(avail / 250)); 18 18 return Math.max(1, Math.floor(avail / 250));
} 19 19 }
20 20
$scope.refreshColumnWidth = function() { 21 21 $scope.refreshColumnWidth = function() {
console.log('refreshing column width'); 22 22 console.log('refreshing column width');
avail = $window.innerWidth - 17; 23 23 avail = $window.innerWidth - 17;
width = Math.floor(avail / Math.floor(avail / 250)); 24 24 width = Math.floor(avail / Math.floor(avail / 250));
$('.cardColumn').css({ 25 25 $('.cardColumn').css({
width: width + 'px', 26 26 width: width + 'px',
'font-size': 100 * width / 250 + '%' 27 27 'font-size': 100 * width / 250 + '%'
}); 28 28 });
$('.cardColumn .card.flashy').css({ 29 29 $('.cardColumn .card.flashy').css({
width: width - 12 + 'px', 30 30 width: width - 12 + 'px',
height: (width * 3 / 5) + 'px' 31 31 height: (width * 3 / 5) + 'px'
}); 32 32 });
}; 33 33 };
34 34
$scope.refreshLayout = function() { 35 35 $scope.refreshLayout = function() {
// check if we actually need to refresh the whole layout 36 36 // check if we actually need to refresh the whole layout
if (calculate_cols() == $scope.numCols) return $scope.refreshColumnWidth(); 37 37 if (calculate_cols() == $scope.numCols) return $scope.refreshColumnWidth();
$scope.numCols = calculate_cols(); 38 38 $scope.numCols = calculate_cols();
console.log('refreshing layout for ' + $scope.numCols + ' columns'); 39 39 console.log('refreshing layout for ' + $scope.numCols + ' columns');
$scope.cardCols = []; 40 40 $scope.cardCols = [];
var cols = []; 41 41 var cols = [];
for (i = 0; i < $scope.numCols; i++) cols.push([]); 42 42 for (i = 0; i < $scope.numCols; i++) cols.push([]);
$scope.cards.forEach(function(card, i) { 43 43 $scope.cards.forEach(function(card, i) {
cols[i % $scope.numCols].push(card); 44 44 cols[i % $scope.numCols].push(card);
45 $scope.cardTable[card.id] = {'colNum':i % $scope.numCols, 'obj':card};
}); 45 46 });
// wait until the next digest cycle to update cardCols 46 47 // wait until the next digest cycle to update cardCols
47 48
$timeout(function() { 48 49 $timeout(function() {
$scope.cardCols = cols; 49 50 $scope.cardCols = cols;
$timeout($scope.refreshColumnWidth); 50 51 $timeout($scope.refreshColumnWidth);
}); 51 52 });
52 53
}; 53 54 };
54 55
angular.element($window).bind('resize', $scope.refreshLayout); 55 56 angular.element($window).bind('resize', $scope.refreshLayout);
56 57
$scope.refreshCards = function() { 57 58 $scope.refreshCards = function() {
$http.get('/api/sections/' + sectionId + '/feed/'). 58 59 $http.get('/api/sections/' + sectionId + '/feed/').
success(function(data) { 59 60 success(function(data) {
console.log(data); 60 61 console.log(data);
$scope.cards = data; 61 62 $scope.cards = data;
$scope.refreshLayout(); 62 63 $scope.refreshLayout();
console.log('success in refresh cards...'); 63 64 console.log('success in refresh cards...');
}). 64 65 }).
error(function(err) { 65 66 error(function(err) {
console.log('refresh fail'); 66 67 console.log('refresh fail');
console.log(err); 67 68 console.log(err);
}); 68 69 });
}; 69 70 };
70 71
$scope.add = function(card) { 71 72 $scope.add = function(card) {
var colNum = 0; 72 73 var colNum = 0;
var lowestCol = {}; 73 74 var lowestCol = $scope.cardCols[0];
var lowestColLen = Infinity; 74 75 var lowestColNum = 0;
while (colNum < $scope.numCols) { 75 76 while (colNum < $scope.numCols) {
if ($scope.cardCols[colNum].length == 0) { 76 77 if ($scope.cardCols[colNum].length == 0) {
lowestCol = $scope.cardCols[colNum]; 77 78 lowestCol = $scope.cardCols[colNum];
break; 78 79 break;
} else if ($scope.cardCols[colNum].length < lowestColLen) { 79 80 } else if ($scope.cardCols[colNum].length < lowestCol.length) {
lowestCol = $scope.cardCols[colNum]; 80 81 lowestCol = $scope.cardCols[colNum];
82 lowestColNum = colNum;
lowestColLen = $scope.cardCols[colNum].length; 81 83 lowestColLen = $scope.cardCols[colNum].length;
} 82 84 }
colNum++; 83 85 colNum++;
} 84 86 }
/*if (colNum == $scope.numCols) { 85
colNum = Math.floor(Math.random() * $scope.numCols); 86
} 87
console.log('adding card to column ' + colNum);*/ 88
console.log(card); 89 87 console.log(card);
$scope.cards.push(data); 90 88 $scope.cards.push(data);
$timeout(function() { 91 89 $timeout(function() {
lowestCol.unshift(card); 92 90 lowestCol.unshift(card);
91 $scope.cardTable[card.id] = {'colNum':lowestColNum, 'obj':card};
$timeout($scope.refreshColumnWidth); 93 92 $timeout($scope.refreshColumnWidth);
}); 94 93 });
}; 95 94 };
95
96 $scope.sortAdd = function(card, array) {
97 console.log('sort add');
98 array.forEach(function(ele, i, ary) {
99 if (ele.score <= card.score) {
100 ary.splice(i, 0, card);
101 return;
102 }
103 });
104 };
96 105
$scope.hide = function(card) { 97 106 $scope.hide = function(card) {
console.log('hiding card'); 98 107 console.log('hiding card');
var found = -1; 99 108 var found = -1;
for (i = 0; i < $scope.cardCols.length; i++) { 100 109 col = $scope.cardTable[card.id].colNum;
found = $scope.cardCols[i].indexOf(card); 101 110 found = $scope.cardCols[col].indexOf(card);
if (found != -1) { 102 111 if (found != -1) {
$timeout(function() { 103 112 $scope.cardCols[col].splice(found, 1);
console.log('card to hide, found'); 104 113 console.log('card hidden');
}); 105 114 return col;
$scope.cardCols[i].splice(found, 1); 106 115 }
return; 107
} 108
} 109
console.log('Error finding card to hide:'); 110 116 console.log('Error finding card to hide:');
console.log(card); 111 117 console.log(card);
118 return -1;
}; 112 119 };
120
121 $scope.update = function(id, new_score) { // NOT WORKING
122 /*card = $scope.cardTable[id].obj;
123 if (new_score == card.score) {
124 console.log('score same, no update required');
125 return;
126 }
127 console.log('updating');
128 card.score = new_score;
129 console.log(card);
130
131 col = $scope.hide(card)
132 if (col != -1) {
133 $scope.sortAdd(card, $scope.cardCols[col]);
134 }*/
135 };
113 136
var loc = window.location, new_uri; 114 137 var loc = window.location, new_uri;
if (loc.protocol === 'https:') { 115 138 if (loc.protocol === 'https:') {
new_uri = 'wss:'; 116 139 new_uri = 'wss:';
} else { 117 140 } else {
new_uri = 'ws:'; 118 141 new_uri = 'ws:';
} 119 142 }
new_uri += '//' + loc.host; 120 143 new_uri += '//' + loc.host;
var ws = new WebSocket(new_uri + '/ws/feed/' + sectionId + '?subscribe-broadcast'); 121 144 var ws = new WebSocket(new_uri + '/ws/feed/' + sectionId + '?subscribe-broadcast');
122 145
ws.onopen = function() { 123 146 ws.onopen = function() {
console.log('websocket connected'); 124 147 console.log('websocket connected');
}; 125 148 };
ws.onmessage = function(e) { 126 149 ws.onmessage = function(e) {
150 data = JSON.parse(e.data);
console.log('got websocket message ' + e.data); 127 151 console.log('got websocket message ' + e.data);
data = JSON.parse(e.data); 128 152 console.log(data);
if (data.event_type == 'new_card') { 129 153 if (data.event_type == 'new_card') {
$scope.add(data.flashcard); 130 154 $scope.add(data.flashcard);
} else if (data.event_type == 'score_change') { 131 155 } else if (data.event_type == 'score_change') {
// 132 156 $scope.update(data.flashcard_id, data.new_score);
} 133 157 }
}; 134 158 };
ws.onerror = function(e) { 135 159 ws.onerror = function(e) {
console.error(e); 136 160 console.error(e);
}; 137 161 };
ws.onclose = function(e) { 138 162 ws.onclose = function(e) {
console.log('connection closed'); 139 163 console.log('connection closed');
}; 140 164 };
141 165
var resetModal = function() { 142 166 var resetModal = function() {
$('#new-card-input').html(''); 143 167 $('#new-card-input').html('');
$('#newCard').closeModal(modal_options); 144 168 $('#newCard').closeModal(modal_options);
}; 145 169 };
146 170
$scope.pushCard = function() { 147 171 $scope.pushCard = function() {
var i = 0; 148 172 var i = 0;
var blanks = []; 149 173 var blanks = [];
$('#new-card-input')[0].childNodes.forEach(function(node) { 150 174 $('#new-card-input')[0].childNodes.forEach(function(node) {
if (typeof node.data == 'undefined') { 151 175 if (typeof node.data == 'undefined') {
console.log('undefined node'); 152 176 console.log('undefined node');
return resetModal(); 153 177 return resetModal();
} 154 178 }
node = $(node)[0]; 155 179 node = $(node)[0];
console.log(node); 156 180 console.log(node);
if (node.tagName == 'B') { 157 181 if (node.tagName == 'B') {
text = $(node).text(); 158 182 text = $(node).text();
blanks.push([i, i + text.length]); 159 183 blanks.push([i, i + text.length]);
i += text.length; 160 184 i += text.length;
} else { 161 185 } else {
i += node.data.length; 162 186 i += node.data.length;
} 163 187 }
}); 164 188 });
var myCard = { 165 189 var myCard = {
'text': $('#new-card-input').text().trim(), 166 190 'text': $('#new-card-input').text().trim(),
'mask': blanks, 167 191 'mask': blanks,
section: sectionId 168 192 section: sectionId
}; 169 193 };
if (myCard.text == '') { 170 194 if (myCard.text == '') {
console.log('blank flashcard not pushed:' + myCard.text); 171 195 console.log('blank flashcard not pushed:' + myCard.text);
return resetModal(); 172 196 return resetModal();
} 173 197 }
$http.post('/api/flashcards/', myCard). 174 198 $http.post('/api/flashcards/', myCard).
success(function(data) { 175 199 success(function(data) {
console.log('flashcard pushed: ' + myCard.text); 176 200 console.log('flashcard pushed: ' + myCard.text);
if (!UserService.hasVerifiedEmail()) { 177 201 if (!UserService.hasVerifiedEmail()) {
Materialize.toast("<p>Thanks for contributing! However, others won't see your card until you verify your email address<p>", 4000); 178 202 Materialize.toast("<p>Thanks for contributing! However, others won't see your card until you verify your email address<p>", 4000);
} 179 203 }
180 204
}). 181 205 }).
error(function(error) { 182 206 error(function(error) {
console.log('something went wrong pushing a card!'); 183 207 console.log('something went wrong pushing a card!');
}); 184 208 });
return resetModal(); 185 209 return resetModal();
}; 186 210 };
187 211
/* Key bindings for the whole feed window. Hotkey it up! */ 188 212 /* Key bindings for the whole feed window. Hotkey it up! */
var listenForC = true; 189 213 var listenForC = true;
190 214
// Need to pass these options into openmodal and leanmodal, 191 215 // Need to pass these options into openmodal and leanmodal,
// otherwise the ready handler doesn't get called 192 216 // otherwise the ready handler doesn't get called
193 217
modal_options = { 194 218 modal_options = {
dismissible: true, // Modal can be dismissed by clicking outside of the modal 195 219 dismissible: true, // Modal can be dismissed by clicking outside of the modal
opacity: 0, // Opacity of modal background 196 220 opacity: 0, // Opacity of modal background
in_duration: 300, // Transition in duration 197 221 in_duration: 300, // Transition in duration
out_duration: 200, // Transition out duration 198 222 out_duration: 200, // Transition out duration
ready: function() { 199 223 ready: function() {
listenForC = false; 200 224 listenForC = false;
console.log('modal OPENING'); 201 225 console.log('modal OPENING');
$('#new-card-input').focus(); 202 226 $('#new-card-input').focus();
}, 203 227 },
complete: function() { 204 228 complete: function() {
listenForC = true; 205 229 listenForC = true;
console.log('modal done, closing'); 206 230 console.log('modal done, closing');
$('#new-card-input').blur(); 207 231 $('#new-card-input').blur();
} 208 232 }
}; 209 233 };
210 234
$(document).keydown(function(e) { 211 235 $(document).keydown(function(e) {
var keyed = e.which; 212 236 var keyed = e.which;
if (keyed == 67 && listenForC) { // "c" for compose 213 237 if (keyed == 67 && listenForC) { // "c" for compose
$('#newCard').openModal(modal_options); 214 238 $('#newCard').openModal(modal_options);
e.preventDefault(); 215 239 e.preventDefault();
listenForC = false; 216 240 listenForC = false;
return false; 217 241 return false;
} else if (keyed == 27) { // clear on ESC 218 242 } else if (keyed == 27) { // clear on ESC
listenForC = true; 219 243 listenForC = true;
document.getElementById('new-card-input').value = ''; 220 244 document.getElementById('new-card-input').value = '';
} 221 245 }
}); 222 246 });
$(document).ready(function() { 223 247 $(document).ready(function() {
$('.tooltipped').tooltip({delay: 50}); 224 248 $('.tooltipped').tooltip({delay: 50});
// the "href" attribute of .modal-trigger must specify the modal ID that wants to be triggered 225 249 // the "href" attribute of .modal-trigger must specify the modal ID that wants to be triggered
$('.modal-trigger').leanModal(modal_options); 226 250 $('.modal-trigger').leanModal(modal_options);
$('#new-card-input').on('keydown', function(e) { 227 251 $('#new-card-input').on('keydown', function(e) {
if (e.which == 13) { 228 252 if (e.which == 13) {
e.preventDefault(); 229 253 e.preventDefault();
$scope.pushCard(); 230 254 $scope.pushCard();
listenForC = true; 231 255 listenForC = true;
return false; 232 256 return false;
} 233 257 }
}); 234 258 });
$('button#blank-selected').click(function() { 235 259 $('button#blank-selected').click(function() {
console.log(window.getSelection()); 236 260 console.log(window.getSelection());
document.execCommand('bold'); 237 261 document.execCommand('bold');
}); 238 262 });
}); 239 263 });
$scope.refreshCards(); 240 264 $scope.refreshCards();
$scope.$on('$destroy', function() { 241 265 $scope.$on('$destroy', function() {
ws.close(); 242 266 ws.close();
$rootScope.currentSection = {}; 243 267 $rootScope.currentSection = {};
$(document).off('keydown'); 244 268 $(document).off('keydown');
}); 245 269 });
246 270
$scope.shuffleCards = function() { 247 271 $scope.shuffleCards = function() {
scripts/FlashcardDirective.js View file @ fcda9ff
angular.module('flashy.FlashcardDirective', []). 1 1 angular.module('flashy.FlashcardDirective', []).
2 2
directive('flashcard', ['$http', '$state', '$window', 3 3 directive('flashcard', ['$http', '$state', '$window',
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 = [];
15
scope.flashcard.mask.sort(function(a, b) { 15 16 scope.flashcard.mask.sort(function(a, b) {
return a[0] - b[0]; 16 17 return a[0] - b[0];
}); 17 18 });
var i = 0; 18 19 var i = 0;
scope.flashcard.mask.forEach(function(blank) { 19 20 scope.flashcard.mask.forEach(function(blank) {
scope.textPieces.push({text: scope.flashcard.text.slice(i, blank[0])}); 20 21 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 22 scope.textPieces.push({text: scope.flashcard.text.slice(blank[0], blank[1]), blank: true});
i = blank[1]; 22 23 i = blank[1];
}); 23 24 });
scope.textPieces.push({text: scope.flashcard.text.slice(i)}); 24 25 scope.textPieces.push({text: scope.flashcard.text.slice(i)});
/* Pulls card from feed into deck */ 25 26 /* Pulls card from feed into deck */
scope.pullCard = function(flashcard) { 26 27 scope.pullCard = function(flashcard) {
flashcard.is_in_deck = true; 27 28 flashcard.is_in_deck = true;
$http.post('/api/flashcards/' + flashcard.id + '/pull/', flashcard). 28 29 $http.post('/api/flashcards/' + flashcard.id + '/pull/', flashcard).
success(function(data) { 29 30 success(function(data) {
console.log('pulled flashcard #' + flashcard.id); 30 31 console.log('pulled flashcard #' + flashcard.id);
//scope.startShrink = true; 31 32 //scope.startShrink = true;
//scope.refresh(flashcard); 32 33 //scope.refresh(flashcard);
}). 33 34 }).
error(function(data) { 34 35 error(function(data) {
console.log('failed to pull flashcard #' + flashcard.id); 35 36 console.log('failed to pull flashcard #' + flashcard.id);
}); 36 37 });
}; 37 38 };
38 39
/* Unpulls card from deck */ 39 40 /* Unpulls card from deck */
scope.unpullCard = function(flashcard) { 40 41 scope.unpullCard = function(flashcard) {
console.log('unpulling card...'); 41 42 console.log('unpulling card...');
flashcard.is_in_deck = false; 42 43 flashcard.is_in_deck = false;
$http.post('/api/flashcards/' + flashcard.id + '/unpull/', 43 44 $http.post('/api/flashcards/' + flashcard.id + '/unpull/',
flashcard). 44 45 flashcard).
success(function(data) { 45 46 success(function(data) {
console.log('card unpull success'); 46 47 console.log('card unpull success');
//scope.startShrink = true; 47 48 //scope.startShrink = true;
//scope.refresh(flashcard); 48 49 //scope.refresh(flashcard);
}). 49 50 }).
error(function(data) { 50 51 error(function(data) {
console.log('card unpull FAILURE'); 51 52 console.log('card unpull FAILURE');
}); 52 53 });
}; 53 54 };
54 55
/* Hides card from feed */ 55 56 /* Hides card from feed */
scope.hideCard = function(flashcard) { 56 57 scope.hideCard = function(flashcard) {
if ($state.current.name == 'feed') { 57 58 if ($state.current.name == 'feed') {
$http.post('/api/flashcards/' + flashcard.id + '/hide/', 58 59 $http.post('/api/flashcards/' + flashcard.id + '/hide/',
flashcard). 59 60 flashcard).
success(function(data) { 60 61 success(function(data) {
console.log('card hide success'); 61 62 console.log('card hide success');
scope.startShrink = true; 62 63 scope.startShrink = true;
scope.refresh(flashcard); 63 64 scope.refresh(flashcard);
}). 64 65 }).
error(function(data) { 65 66 error(function(data) {
console.log('card hide FAILURE'); 66 67 console.log('card hide FAILURE');
}); 67 68 });
} 68 69 }
}; 69 70 };
scripts/UserService.js View file @ fcda9ff
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) {
_user = data; 6 6 _user = data;
if (!data.is_confirmed) { 7 7 if (!data.is_confirmed) {
Materialize.toast('Please verify your email address! ' + 8 8 Materialize.toast('Please verify your email address! ' +
'<a class="btn-flat cyan-text" ng-click="UserService.resendConfirmationEmail()">' + 9 9 '<a class="btn-flat cyan-text" ng-click="UserService.resendConfirmationEmail()">' +
'Resend Verification Email</a>', 4000); 10 10 'Resend Verification Email</a>', 4000);
} 11 11 }
_user.sectionIdList = _user.sections.map(function(x) { 12 12 _user.sectionIdList = _user.sections.map(function(x) {
return x.id; 13 13 return x.id;
}); 14 14 });
deferred.resolve(data); 15 15 deferred.resolve(data);
}; 16 16 };
this.login = login; 17 17 this.login = login;
$http.get('/api/me/').success(function(data) { 18 18 $http.get('/api/me/').success(function(data) {
console.log('user is logged in!'); 19 19 console.log('user is logged in!');
login(data); 20 20 login(data);
}).error(function(data) { 21 21 }).error(function(data) {
console.log(data); 22 22 console.log(data);
console.log('not logged in yet: ' + data.detail); 23 23 console.log('not logged in yet: ' + data.detail);
_user = {email: false}; 24 24 _user = {email: false};
deferred.resolve(_user); 25 25 deferred.resolve(_user);
}); 26 26 });
27 27
this.isResolved = function() { 28 28 this.isResolved = function() {
return !!_user; 29 29 return !!_user;
}; 30 30 };
this.getUserData = function() { 31 31 this.getUserData = function() {
if (this.isResolved()) return _user; 32 32 if (this.isResolved()) return _user;
else return deferred.promise; 33 33 else return deferred.promise;
}; 34 34 };
this.hasVerifiedEmail = function() { 35 35 this.hasVerifiedEmail = function() {
return this.isResolved() && _user.is_confirmed; 36 36 return this.isResolved() && _user.is_confirmed;
}; 37 37 };
this.logout = function() { 38 38 this.logout = function() {
_user = false; 39 39 _user = false;
deferred.resolve({}); 40 40 deferred.resolve({});
}; 41 41 };
this.addClass = function(section) { 42 42 this.addClass = function(section) {
_user.sections.push(section); 43 43 _user.sections.push(section);
_user.sectionIdList.push(section.id); 44 44 _user.sectionIdList.push(section.id);
}; 45 45 };
this.isLoggedIn = function() { 46 46 this.isLoggedIn = function() {
rv = this.isResolved() && _user.email; 47 47 rv = this.isResolved() && _user.email;
return rv; 48 48 return rv;
}; 49 49 };
this.isInSection = function(sectionId) { 50 50 this.isInSection = function(sectionId) {
return (_user.sectionIdList.indexOf(sectionId) >= 0); 51 51 return (_user.sectionIdList.indexOf(sectionId) >= 0);
}; 52 52 };
this.redirectToDefaultState = function($state) { 53 53 this.redirectToDefaultState = function($state) {
console.log('redirecting user to their default state'); 54 54 console.log('redirecting user to their default state');
if (!this.isLoggedIn()) return $state.go('login'); 55 55 if (!this.isLoggedIn()) return $state.go('login');
if (!_user.sections.length) return $state.go('addclass'); 56 56 if (!_user.sections.length) return $state.go('addclass');
last_state = localStorage.getItem('last_state'); 57 57 last_state = localStorage.getItem('last_state');
if (last_state) return $state.go(last_state, JSON.parse(localStorage.getItem('last_state_params'))); 58 58 if (last_state) return $state.go(last_state, JSON.parse(localStorage.getItem('last_state_params')));
$state.go('feed', {sectionId: _user.sections[0].id}); 59 59 $state.go('feed', {sectionId: _user.sections[0].id});
}; 60 60 };
this.authorizedFor = function(state, stateParams) { 61 61 this.authorizedFor = function(state, stateParams) {
if (['feed', 'deck', 'cardlist'].indexOf(state.name) >= 0) { 62 62 if (['feed', 'deck', 'cardlist'].indexOf(state.name) >= 0) {
if (_user.sectionIdList.indexOf(stateParams.sectionId) < 0) { 63 63 if (_user.sectionIdList.indexOf(stateParams.sectionId) < 0) {
return false; 64 64 return false;
} 65 65 }
} 66 66 }
return true; 67 67 return true;
}; 68 68 };
69 this.noAuthRequired = function(state) {
70 if (['verifyemail'].indexOf(state.name) >= 0) {
71 return true;
72 }
73 return false;
74 }
this.resendConfirmationEmail = function() { 69 75 this.resendConfirmationEmail = function() {
if (!this.isLoggedIn()) return console.log("Can't request resending a confirmation email since the user is not logged in."); 70 76 if (!this.isLoggedIn()) return console.log("Can't request resending a confirmation email since the user is not logged in.");
console.log('Requesting resend of confirmation email'); 71 77 console.log('Requesting resend of confirmation email');
$http.post('/api/resend_confirmation_email/').success(function() { 72 78 $http.post('/api/resend_confirmation_email/').success(function() {
Materialize.toast('Resent confirmation email! Check your spam folder too.', 4000); 73 79 Materialize.toast('Resent confirmation email! Check your spam folder too.', 4000);
}); 74 80 });
}; 75 81 };
templates/feed.html View file @ fcda9ff
<div class="row"> 1 1 <div class="row">
<h2 ng-cloak ng-show="cards.length == 0">No cards. Be the first one to add a card!</h2> 2 2 <h2 ng-cloak ng-show="cards.length == 0">No cards. Be the first one to add a card!</h2>
3 3
<div class="progress center-align" style="margin: 70px auto auto;width:50%;" ng-if="cards === false"> 4 4 <div class="progress center-align" style="margin: 70px auto auto;width:50%;" ng-if="cards === false">
<div class="indeterminate"></div> 5 5 <div class="indeterminate"></div>
</div> 6 6 </div>
<div class="cardColumn" ng-repeat="col in cardCols"> 7 7 <div class="cardColumn" ng-repeat="col in cardCols">
<div class="repeated-card" ng-repeat="card in col"> 8 8 <div class="repeated-card" ng-repeat="card in col track by $index">
<flashcard flashcard-obj="card" refresh="hide(card)"/> 9 9 <flashcard flashcard-obj="card" refresh="hide(card)"/>
</div> 10 10 </div>
</div> 11 11 </div>
12 12
<!--<div class="cardColumn" ng-repeat="col in cardCols"> 13 13 <!--<div class="cardColumn" ng-repeat="col in cardCols">
<flashcard flashcard-obj="card" ng-repeat="card in col"/> 14 14 <flashcard flashcard-obj="card" ng-repeat="card in col"/>
</div>--> 15 15 </div>-->
</div> 16 16 </div>
17 17
18 18
<!--Lil plus button in corner--> 19 19 <!--Lil plus button in corner-->
<div class="fixed-action-btn" style="bottom: 96px; right: 24px;"> 20 20 <div class="fixed-action-btn" style="bottom: 96px; right: 24px;">
<a data-target="newCard" class="btn-floating btn-large modal-trigger tooltipped" href="#newCard" data-position="left" 21 21 <a data-target="newCard" class="btn-floating btn-large modal-trigger tooltipped" href="#newCard" data-position="left"
data-delay="50" 22 22 data-delay="50"
data-tooltip="(C)ompose"> 23 23 data-tooltip="(C)ompose">
<i class="large mdi-content-add"></i> 24 24 <i class="large mdi-content-add"></i>
</a> 25 25 </a>
</div> 26 26 </div>
27 27
<div id="newCard" class="modal bottom-sheet"> 28 28 <div id="newCard" class="modal bottom-sheet">
<form id="new-card-form"> 29 29 <form id="new-card-form">
<div class="modal-content"> 30 30 <div class="modal-content">
<div class="input-field"> 31 31 <div class="input-field">
<!--<label id="newCardSign" for="newCard">New Flashcard Text</label>--> 32 32 <!--<label id="newCardSign" for="newCard">New Flashcard Text</label>-->
<div class="feed-modal-input" id="new-card-input" contenteditable style="outline:0px solid transparent;"> 33 33 <div class="feed-modal-input" id="new-card-input" contenteditable style="outline:0px solid transparent;">
34 34
</div> 35 35 </div>
</div> 36 36 </div>
</div> 37 37 </div>
<div class="modal-footer"> 38 38 <div class="modal-footer">
<button class="btn modal-close tooltipped" type="submit" ng-click="pushCard()" data-position="left" 39 39 <button class="btn modal-close tooltipped" type="submit" ng-click="pushCard()" data-position="left"
data-delay="50" 40 40 data-delay="50"
data-tooltip="Enter">Submit 41 41 data-tooltip="Enter">Submit
<i class="mdi-hardware-keyboard-return right"></i> 42 42 <i class="mdi-hardware-keyboard-return right"></i>
</button> 43 43 </button>
<button id="blank-selected" style="float:left" class="btn tooltipped" data-position="right" data-delay="50" 44 44 <button id="blank-selected" style="float:left" class="btn tooltipped" data-position="right" data-delay="50"
templates/flashcard.html View file @ fcda9ff
<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.is_in_deck"
ng-click="pullCard(flashcard)"> 11 11 ng-click="pullCard(flashcard)">
<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.is_in_deck"
ng-click="unpullCard(flashcard)"> 15 15 ng-click="unpullCard(flashcard)">
<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="flashcard.is_in_deck" class="green-text" style="position:absolute; top:0px;right:0px"> 29 29 <div ng-show="flashcard.is_in_deck" 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>
32 <div style="position:absolute; bottom:0px; right:5px">
33 <span class="center-me">score:{{flashcard.score}}</span>
34 </div>