Commit 55223196b88f07f68f40531b45d1bb12a1d54021

Authored by Andrew Buss
1 parent fc88fb81de

let's step back a little

Showing 5 changed files with 244 additions and 319 deletions Inline Diff

scripts/CardGridController.js View file @ 5522319
angular.module('flashy.CardGridController', ['ui.router', 'ngAnimate', 'ngWebSocket', 'flashy.DeckFactory']).CardGridController = 1 1 angular.module('flashy.CardGridController', ['ui.router', 'ngAnimate', 'ngWebSocket', 'flashy.DeckFactory']).CardGridController =
function($scope, $rootScope, $state, $http, $window, $timeout, $stateParams, $websocket, $interval, UserService, Flashcard, Deck) { 2 2 function($scope, $rootScope, $state, $http, $window, $timeout, $stateParams, $websocket, $interval, UserService, Flashcard, Deck) {
sectionId = parseInt($stateParams.sectionId); 3 3 sectionId = parseInt($stateParams.sectionId);
$scope.cards = []; // all cards 4 4 $scope.cards = []; // all cards
$scope.cardCols = []; // organized data 5 5 $scope.cardCols = []; // organized data
$scope.affectedCards = []; // cards affected by a position change move 6 6 $scope.affectedCards = []; // cards affected by a position change move
$scope.numCols = 0; 7 7 $scope.numCols = 0;
$scope.height = 0; // height of a card 8 8 $scope.height = 0; // height of a card
$scope.section = $rootScope.SectionResource.get({sectionId: sectionId}); 9 9 $scope.section = $rootScope.SectionResource.get({sectionId: sectionId});
$scope.deck = new Deck(sectionId, { 10 10 $scope.deck = new Deck(sectionId, {
cardHideCallback: function(card) { 11 11 cardHideCallback: function(card) {
$scope.hideCardFromGrid(card); 12 12 $scope.hideCardFromGrid(card);
} 13 13 }
}); 14 14 });
15 15
Flashcard.linkDeck($scope.deck); 16 16 Flashcard.linkDeck($scope.deck);
17 17
$scope.moveQueue = []; 18 18 $scope.moveQueue = [];
19 19
$scope.procQueue; 20 20 $scope.procQueue;
$scope.addMove = function(m) { 21 21 $scope.addMove = function(m) {
if (!$scope.moveQueue) return m.go(); 22 22 if (!$scope.moveQueue) return m.go();
$scope.moveQueue.push(m); 23 23 $scope.moveQueue.push(m);
}; 24 24 };
25 25
$scope.showGrid = false; 26 26 $scope.showGrid = false;
//$scope.moveQueue = []; // queue of flashcard objects 27 27 //$scope.moveQueue = []; // queue of flashcard objects
$rootScope.currentSection = $scope.section; 28 28 $rootScope.currentSection = $scope.section;
29 29
$scope.refreshColumnWidth = function() { 30 30 $scope.refreshColumnWidth = function() {
avail = $window.innerWidth - 17; 31 31 avail = $window.innerWidth - 17;
width = Math.floor(avail / Math.floor(avail / 250)); 32 32 width = Math.floor(avail / Math.floor(avail / 250));
$('.cardColumn').css({ 33 33 $('.cardColumn').css({
width: width + 'px' 34 34 width: width + 'px'
}); 35 35 });
$('.cardColumn .card.flashy').css({ 36 36 $('.cardColumn .card.flashy').css({
width: width - 12 + 'px', 37 37 width: width - 12 + 'px',
'font-size': 100 * width / 250 + '%', 38 38 'font-size': 100 * width / 250 + '%',
height: (width * 3 / 5) + 'px' 39 39 height: (width * 3 / 5) + 'px'
}); 40 40 });
$('.cardColumn .card.flashy i.small').css({ 41 41 $('.cardColumn .card.flashy i.small').css({
'font-size': 200 * width / 250 + '%' 42 42 'font-size': 200 * width / 250 + '%'
}); 43 43 });
$('.cardColumn .card.flashy i.medium').css({ 44 44 $('.cardColumn .card.flashy i.medium').css({
'font-size': 400 * width / 250 + '%' 45 45 'font-size': 400 * width / 250 + '%'
}); 46 46 });
$scope.height = width * 3 / 5; 47 47 $scope.height = width * 3 / 5;
}; 48 48 };
49 49
$scope.refreshLayout = function() { 50 50 $scope.refreshLayout = function() {
numCols = Math.max(1, Math.floor(($window.innerWidth - 17) / 250)); 51 51 numCols = Math.max(1, Math.floor(($window.innerWidth - 17) / 250));
52 52
// check if we actually need to refresh the whole layout 53 53 // check if we actually need to refresh the whole layout
if (numCols == $scope.numCols) return $scope.refreshColumnWidth(); 54 54 if (numCols == $scope.numCols) return $scope.refreshColumnWidth();
$scope.numCols = numCols; 55 55 $scope.numCols = numCols;
console.log('refreshing layout for ' + numCols + ' columns'); 56 56 console.log('refreshing layout for ' + numCols + ' columns');
$scope.cardCols = []; 57 57 $scope.cardCols = [];
var cols = []; 58 58 var cols = [];
for (var i = 0; i < numCols; i++) cols.push([]); 59 59 for (var i = 0; i < numCols; i++) cols.push([]);
var n = 0; 60 60 var n = 0;
$scope.cards.forEach(function(card, j) { 61 61 $scope.cards.forEach(function(card, j) {
card.colNum = n++ % numCols; 62 62 card.colNum = n++ % numCols;
cols[card.colNum].push(card); 63 63 cols[card.colNum].push(card);
}); 64 64 });
for (i in cols) $scope.updateColRanks(cols[i]); 65 65 for (i in cols) $scope.updateColRanks(cols[i]);
console.log(cols); 66 66 console.log(cols);
return $timeout(function() { 67 67 return $timeout(function() {
$scope.cardCols = cols; 68 68 $scope.cardCols = cols;
$timeout($scope.refreshColumnWidth); 69 69 $timeout($scope.refreshColumnWidth);
}); 70 70 });
}; 71 71 };
72 72
angular.element($window).bind('resize', $scope.refreshLayout); 73 73 angular.element($window).bind('resize', $scope.refreshLayout);
74 74
$scope.addCardToGrid = function(card) { 75 75 $scope.addCardToGrid = function(card) {
var colNum = 0; 76 76 var colNum = 0;
var lowestCol = $scope.cardCols[0]; 77 77 var lowestCol = $scope.cardCols[0];
var lowestColNum = 0; 78 78 var lowestColNum = 0;
while (colNum < $scope.numCols) { 79 79 while (colNum < $scope.numCols) {
if ($scope.cardCols[colNum].length == 0) { 80 80 if ($scope.cardCols[colNum].length == 0) {
lowestCol = $scope.cardCols[colNum]; 81 81 lowestCol = $scope.cardCols[colNum];
break; 82 82 break;
} else if ($scope.cardCols[colNum].length < lowestCol.length) { 83 83 } else if ($scope.cardCols[colNum].length < lowestCol.length) {
lowestCol = $scope.cardCols[colNum]; 84 84 lowestCol = $scope.cardCols[colNum];
lowestColNum = colNum; 85 85 lowestColNum = colNum;
lowestColLen = $scope.cardCols[colNum].length; 86 86 lowestColLen = $scope.cardCols[colNum].length;
} 87 87 }
colNum++; 88 88 colNum++;
} 89 89 }
console.log(card); 90 90 console.log(card);
$scope.cards.push(data); 91 91 $scope.cards.push(data);
lowestCol.unshift(card); 92 92 lowestCol.unshift(card);
card.colNum = lowestColNum; 93 93 card.colNum = lowestColNum;
$scope.updateColRanks(lowestCol); 94 94 $scope.updateColRanks(lowestCol);
$timeout($scope.refreshColumnWidth); 95 95 $timeout($scope.refreshColumnWidth);
96 96
}; 97 97 };
98 98
$scope.updateColRanks = function(col) { 99 99 $scope.updateColRanks = function(col) {
for (i in col) 100 100 for (i in col)
col[i].colRank = parseInt(i); 101 101 col[i].colRank = parseInt(i);
}; 102 102 };
103 103
$scope.hideCardFromGrid = function(card) { 104 104 $scope.hideCardFromGrid = function(card) {
console.log('hiding', card); 105 105 console.log('hiding', card);
$scope.cardCols[card.colNum].splice(card.colRank, 1); 106 106 $scope.cardCols[card.colNum].splice(card.colRank, 1);
$scope.updateColRanks($scope.cardCols[card.colNum]); 107 107 $scope.updateColRanks($scope.cardCols[card.colNum]);
scripts/FeedController.js View file @ 5522319
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;
$rootScope.debugFlashcard = false; 10 10 sectionId = parseInt($stateParams.sectionId);
11 11
$scope.updateCardScore = function(card, scoreChange) { 12 12 $scope.updateCardScore = function(card, scoreChange) {
console.log('update card score'); 13 13 console.log('update card score');
// if no colNum is attached, then this doesn't exist on the feed yet 14 14 // if no colNum is attached, then this doesn't exist on the feed yet
if (!card.colNum) return; 15 15 if (!card.colNum) return;
16 16
card.score += scoreChange; 17 17 card.score += scoreChange;
var col = card.colNum; 18 18 var col = card.colNum;
var rank = card.colRank; 19 19 var rank = card.colRank;
var s = Math.sign(scoreChange); 20 20 var s = Math.sign(scoreChange);
21 21
rank -= s; 22 22 rank -= s;
if (rank < 0) return; 23 23 if (rank < 0) return;
if (rank == $scope.cardCols[col].length) return; 24 24 if (rank == $scope.cardCols[col].length) return;
25 25
$scope.affectedCards = []; 26 26 $scope.affectedCards = [];
while (s * $scope.cardCols[col][rank].score < s * card.score) { 27 27 while (s * $scope.cardCols[col][rank].score < s * card.score) {
$scope.affectedCards.push($scope.cardCols[col][rank]); 28 28 $scope.affectedCards.push($scope.cardCols[col][rank]);
rank -= s; 29 29 rank -= s;
if (rank < 0) break; 30 30 if (rank < 0) break;
if (rank == $scope.cardCols[col].length) break; 31 31 if (rank == $scope.cardCols[col].length) break;
} 32 32 }
rank += s; 33 33 rank += s;
34 34
var upMove = $scope.height; 35 35 var upMove = $scope.height;
var downMove = $scope.height; 36 36 var downMove = $scope.height;
if (s > 0) { 37 37 if (s > 0) {
card.moveUp = true; 38 38 card.moveUp = true;
upMove *= $scope.affectedCards.length; 39 39 upMove *= $scope.affectedCards.length;
/*for (i=0; i<$scope.affectedCards.length; i++) { 40 40 /*for (i=0; i<$scope.affectedCards.length; i++) {
$scope.affectedCards[i].moveDown = true; 41 41 $scope.affectedCards[i].moveDown = true;
}*/ 42 42 }*/
} else { 43 43 } else {
card.moveDown = true; 44 44 card.moveDown = true;
downMove *= $scope.affectedCards; 45 45 downMove *= $scope.affectedCards;
/*for (i=0; i<$scope.affectedCards.length; i++) { 46 46 /*for (i=0; i<$scope.affectedCards.length; i++) {
$scope.affectedCards[i].moveUp = true; 47 47 $scope.affectedCards[i].moveUp = true;
}*/ 48 48 }*/
} 49 49 }
50 50
$('.card.flashy.card-moveUp').css({ 51 51 $('.card.flashy.card-moveUp').css({
'margin-top': 300 + 'px', // how much moveUp moves 52 52 'margin-top': 300 + 'px', // how much moveUp moves
'z-index': '99' 53 53 'z-index': '99'
}); 54 54 });
55 55
$('.card.flashy.card-moveDown').css({ 56 56 $('.card.flashy.card-moveDown').css({
'margin-top': 300 + 'px', // how much moveDown moves 57 57 'margin-top': 300 + 'px', // how much moveDown moves
}); 58 58 });
59 59
$timeout(function() { 60 60 $timeout(function() {
$('.card.flashy.card-moveUp').css({ 61 61 $('.card.flashy.card-moveUp').css({
'margin-top': '6px', 62 62 'margin-top': '6px',
}); 63 63 });
64 64
$('.card.flashy.card-moveDown').css({ 65 65 $('.card.flashy.card-moveDown').css({
'margin-top': '6px', 66 66 'margin-top': '6px',
}); 67 67 });
if (s > 0) { 68 68 if (s > 0) {
card.moveUp = false; 69 69 card.moveUp = false;
for (i=0; i<$scope.affectedCards.length; i++) { 70 70 for (i = 0; i < $scope.affectedCards.length; i++) {
$scope.affectedCards[i].moveDown = false; 71 71 $scope.affectedCards[i].moveDown = false;
} 72 72 }
} else { 73 73 } else {
card.moveDown = false; 74 74 card.moveDown = false;
for (i=0; i<$scope.affectedCards.length; i++) { 75 75 for (i = 0; i < $scope.affectedCards.length; i++) {
$scope.affectedCards[i].moveUp = false; 76 76 $scope.affectedCards[i].moveUp = false;
} 77 77 }
} 78 78 }
$scope.cardCols[col].splice(rank, 0, $scope.cardCols[col].splice(card.colRank, 1)[0]); 79 79 $scope.cardCols[col].splice(rank, 0, $scope.cardCols[col].splice(card.colRank, 1)[0]);
}, 1000); 80 80 }, 1000);
81 81
$scope.updateColRanks($scope.cardCols[card.colNum]); // can be optimized out 82 82 $scope.updateColRanks($scope.cardCols[card.colNum]); // can be optimized out
}; 83 83 };
84 84
85 85
86 86 $scope.feed_ws = $websocket($scope.ws_host + '/ws/feed/' + sectionId + '?subscribe-broadcast');
$scope.feed_ws = $websocket($scope.ws_host + '/ws/feed/' + $scope.sectionId + '?subscribe-broadcast'); 87
$scope.feed_ws.onMessage(function(e) { 88 87 $scope.feed_ws.onMessage(function(e) {
88 console.log('oh yeaaaaaaaaaaaaaaaaaaa');
89 89
90
console.log('oh yeaaaaaaaaaaaaaaaaaaa'); 91
92
data = JSON.parse(e.data); 93 90 data = JSON.parse(e.data);
console.log('message', data); 94 91 console.log('message', data);
if (data.event_type == 'new_card') { 95 92 if (data.event_type == 'new_card') {
$scope.addCardToGrid(new Flashcard(data.flashcard, $scope.deck)); 96 93 $scope.addCardToGrid(new Flashcard(data.flashcard, $scope.deck));
} else if (data.event_type == 'score_change') { 97 94 } else if (data.event_type == 'score_change') {
card = new Flashcard(data.flashcard); // doesnt create a card if it exists 98 95 card = new Flashcard(data.flashcard); // doesnt create a card if it exists
console.log('score change'); 99 96 card.score = data.flashcard.score;
$scope.updateCardScore(card, data.flashcard.score - card.score); 100 97 console.log('score change');
98 $scope.updateCardScore(card, data.flashcard.score - card.score);
} 101 99 }
}); 102 100 });
103 101
$scope.pushCard = function() { 104 102 $scope.pushCard = function() {
var myCard = { 105 103 var myCard = {
// we can't trim this string because it'd mess up the blanks. Something to fix. 106 104 // we can't trim this string because it'd mess up the blanks. Something to fix.
'text': $('#new-card-input').text(), 107 105 'text': $('#new-card-input').text(),
'mask': $scope.newCardBlanks, 108 106 'mask': $scope.newCardBlanks,
section: $scope.section.id 109 107 section: $scope.section.id
}; 110 108 };
if (myCard.text == '') { 111 109 if (myCard.text == '') {
console.log('blank flashcard not pushed:' + myCard.text); 112 110 console.log('blank flashcard not pushed:' + myCard.text);
return closeNewCard(); 113 111 return closeNewCard();
} 114 112 }
$http.post('/api/flashcards/', myCard). 115 113 $http.post('/api/flashcards/', myCard).
success(function(data) { 116 114 success(function(data) {
console.log('flashcard pushed: ' + myCard.text); 117 115 console.log('flashcard pushed: ' + myCard.text);
if (!UserService.hasVerifiedEmail()) { 118 116 if (!UserService.hasVerifiedEmail()) {
Materialize.toast("<p>Thanks for contributing! However, others won't see your card until you verify your email address<p>", 4000); 119 117 Materialize.toast("<p>Thanks for contributing! However, others won't see your card until you verify your email address<p>", 4000);
} 120 118 }
}); 121 119 });
return $scope.closeNewCardModal(); 122 120 return $scope.closeNewCardModal();
}; 123 121 };
124 122
/* Key bindings for the whole feed window. Hotkey it up! */ 125 123 /* Key bindings for the whole feed window. Hotkey it up! */
var listenForC = true; 126 124 var listenForC = true;
127 125
// Need to pass these options into openmodal and leanmodal, 128 126 // Need to pass these options into openmodal and leanmodal,
// otherwise the ready handler doesn't get called 129 127 // otherwise the ready handler doesn't get called
130 128
modal_options = { 131 129 modal_options = {
dismissible: true, // Modal can be dismissed by clicking outside of the modal 132 130 dismissible: true, // Modal can be dismissed by clicking outside of the modal
opacity: 0, // Opacity of modal background 133 131 opacity: 0, // Opacity of modal background
in_duration: 300, // Transition in duration 134 132 in_duration: 300, // Transition in duration
out_duration: 200, // Transition out duration 135 133 out_duration: 200, // Transition out duration
ready: function() { 136 134 ready: function() {
$('#new-card-input').focus(); 137 135 $('#new-card-input').focus();
document.execCommand('selectAll', false, null); 138 136 document.execCommand('selectAll', false, null);
} 139 137 }
}; 140 138 };
141 139
$(document).keydown(function(e) { 142 140 $(document).keydown(function(e) {
var keyed = e.which; 143 141 var keyed = e.which;
if (keyed == 67 && listenForC) { // "c" for compose 144 142 if (keyed == 67 && listenForC) { // "c" for compose
$scope.openNewCardModal(); 145 143 $scope.openNewCardModal();
e.preventDefault(); 146 144 e.preventDefault();
return false; 147 145 return false;
} else if (keyed == 27) { // clear on ESC 148 146 } else if (keyed == 27) { // clear on ESC
$scope.closeNewCardModal(); 149 147 $scope.closeNewCardModal();
} 150 148 }
}); 151 149 });
152 150
$scope.openNewCardModal = function() { 153 151 $scope.openNewCardModal = function() {
$('#newCard').openModal(modal_options); 154 152 $('#newCard').openModal(modal_options);
listenForC = false; 155 153 listenForC = false;
$('#new-card-input').html('Write a flashcard!'); 156 154 $('#new-card-input').html('Write a flashcard!');
}; 157 155 };
158 156
$scope.closeNewCardModal = function() { 159 157 $scope.closeNewCardModal = function() {
listenForC = true; 160 158 listenForC = true;
$('#new-card-input').html('').blur(); 161 159 $('#new-card-input').html('').blur();
$('#newCard').closeModal(modal_options); 162 160 $('#newCard').closeModal(modal_options);
}; 163 161 };
164 162
$('.tooltipped').tooltip({delay: 50}); 165 163 $('.tooltipped').tooltip({delay: 50});
// the "href" attribute of .modal-trigger must specify the modal ID that wants to be triggered 166 164 // the "href" attribute of .modal-trigger must specify the modal ID that wants to be triggered
$('.modal-trigger').leanModal(modal_options); 167 165 $('.modal-trigger').leanModal(modal_options);
$('#new-card-input').on('keydown', function(e) { 168 166 $('#editCardA').leanModal(modal_options);
167 $('#new-card-input').on('keydown', function(e) {
if (e.which == 13) { 169 168 if (e.which == 13) {
e.preventDefault(); 170 169 e.preventDefault();
if ($scope.submit_enabled) { 171 170 if ($scope.submit_enabled) {
$scope.pushCard(); 172 171 $scope.pushCard();
listenForC = true; 173 172 listenForC = true;
} 174 173 }
return false; 175 174 return false;
} else { 176 175 } else {
177 176
} 178 177 }
}); 179 178 });
$('button#blank-selected').click(function() { 180 179 $('button#blank-selected').click(function() {
console.log(window.getSelection()); 181 180 console.log(window.getSelection());
document.execCommand('bold'); 182 181 document.execCommand('bold');
}); 183 182 });
$scope.newCardBlanks = []; 184 183 $scope.newCardBlanks = [];
$scope.refreshNewCardInput = function() { 185 184 $scope.refreshNewCardInput = function() {
$scope.newCardText = $('#new-card-input').text(); 186 185 $scope.newCardText = $('#new-card-input').text();
$scope.submit_enabled = $scope.newCardText.length >= 5 && $scope.newCardText.length <= 160; 187 186 $scope.submit_enabled = $scope.newCardText.length >= 5 && $scope.newCardText.length <= 160;
var i = 0; 188 187 var i = 0;
$scope.newCardBlanks = []; 189 188 $scope.newCardBlanks = [];
$('#new-card-input')[0].childNodes.forEach(function(node) { 190 189 $('#new-card-input')[0].childNodes.forEach(function(node) {
node = $(node)[0]; 191 190 node = $(node)[0];
if (node.tagName == 'B') { 192 191 if (node.tagName == 'B') {
var text = $(node).text(); 193 192 var text = $(node).text();
var leftspaces = 0, rightspaces = 0; 194 193 var leftspaces = 0, rightspaces = 0;
// awful way to find the first non-space character from the left or the right. thanks.js 195 194 // awful way to find the first non-space character from the left or the right. thanks.js
while (text[leftspaces] == ' ' || text[leftspaces] == '\xa0') leftspaces++; 196 195 while (text[leftspaces] == ' ' || text[leftspaces] == '\xa0') leftspaces++;
while (text[text.length - 1 - rightspaces] == ' ' || text[text.length - 1 - rightspaces] == '\xa0') rightspaces++; 197 196 while (text[text.length - 1 - rightspaces] == ' ' || text[text.length - 1 - rightspaces] == '\xa0') rightspaces++;
console.log(leftspaces, text.length); 198 197 console.log(leftspaces, text.length);
if (leftspaces != text.length) $scope.newCardBlanks.push([i + leftspaces, i + text.length - rightspaces]); 199 198 if (leftspaces != text.length) $scope.newCardBlanks.push([i + leftspaces, i + text.length - rightspaces]);
i += text.length; 200 199 i += text.length;
} else if (!node.data) { 201 200 } else if (!node.data) {
i += $(node).text().length; 202 201 i += $(node).text().length;
} else { 203 202 } else {
i += node.data.length; 204 203 i += node.data.length;
} 205 204 }
}); 206 205 });
$scope.newCardBlanks.sort(function(a, b) { 207 206 $scope.newCardBlanks.sort(function(a, b) {
return a[0] - b[0]; 208 207 return a[0] - b[0];
}); 209 208 });
i = 0; 210 209 i = 0;
newtext = ''; 211 210 newtext = '';
$scope.newCardBlanks.forEach(function(blank) { 212 211 $scope.newCardBlanks.forEach(function(blank) {
newtext += $scope.newCardText.slice(i, blank[0]); 213 212 newtext += $scope.newCardText.slice(i, blank[0]);
newtext += '<b>' + $scope.newCardText.slice(blank[0], blank[1]) + '</b>'; 214 213 newtext += '<b>' + $scope.newCardText.slice(blank[0], blank[1]) + '</b>';
i = blank[1]; 215 214 i = blank[1];
}); 216 215 });
newtext += $scope.newCardText.slice(i); 217 216 newtext += $scope.newCardText.slice(i);
//$scope.newCardFormattedText = newtext; 218 217 //$scope.newCardFormattedText = newtext;
}; 219 218 };
$scope.shuffleCards = function() { 220 219 $scope.shuffleCards = function() {
$timeout(function() { 221 220 $timeout(function() {
(function(o) { 222 221 (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); 223 222 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; 224 223 return o;
})($scope.cardCols[0]); 225 224 })($scope.cardCols[0]);
}); 226 225 });
}; 227 226 };
$scope.$on('$destroy', function() { 228 227 $scope.$on('$destroy', function() {
$scope.feed_ws.close(); 229 228 $scope.feed_ws.close();
$interval.cancel(promise); 230 229 $interval.cancel(promise);
}); 231 230 });
return $http.get('/api/sections/' + sectionId + '/feed/'). 232 231 return $http.get('/api/sections/' + sectionId + '/feed/').
success(function(data) { 233 232 success(function(data) {
console.log(data); 234 233 console.log(data);
$scope.cards = data.map(function(card) { 235 234 $scope.cards = data.map(function(card) {
return new Flashcard(card, $scope.deck); 236 235 return new Flashcard(card, $scope.deck);
}); 237 236 });
$scope.refreshLayout().then(function() { 238 237 $scope.refreshLayout().then(function() {
$timeout($scope.refreshColumnWidth).then(function() { 239 238 $timeout($scope.refreshColumnWidth).then(function() {
$scope.showGrid = true; 240 239 $scope.showGrid = true;
}); 241 240 });
console.log('layout done'); 242 241 console.log('layout done');
}); 243 242 });
}); 244 243 });
245 244
246 245
}); 247 246 });
248 247
scripts/FlashcardFactory.js View file @ 5522319
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.textPieces = []; 9 9 this.textPieces = [];
this.mask.sort(function (a, b) { 10 10 this.mask.sort(function (a, b) {
return a[0] - b[0]; 11 11 return a[0] - b[0];
}); 12 12 });
var i = 0; 13 13 var i = 0;
this.mask.forEach(function (blank) { 14 14 this.mask.forEach(function (blank) {
this.textPieces.push({text: this.text.slice(i, blank[0])}); 15 15 this.textPieces.push({text: this.text.slice(i, blank[0])});
this.textPieces.push({text: this.text.slice(blank[0], blank[1]), blank: true}); 16 16 this.textPieces.push({text: this.text.slice(blank[0], blank[1]), blank: true});
i = blank[1]; 17 17 i = blank[1];
}, this); 18 18 }, this);
this.textPieces.push({text: this.text.slice(i)}); 19 19 this.textPieces.push({text: this.text.slice(i)});
this.formatted_text = ''; 20 20 this.formatted_text = '';
for (i in this.textPieces) { 21 21 for (i in this.textPieces) {
p = this.textPieces[i]; 22 22 p = this.textPieces[i];
this.formatted_text += p.blank ? '<b>' + p.text + '</b>' : p.text; 23 23 this.formatted_text += p.blank ? '<b>' + p.text + '</b>' : p.text;
} 24 24 }
this.moveUp = false; 25 25 this.moveUp = false;
this.moveDown = false; 26 26 this.moveDown = false;
this.colNum = 0; 27 27 this.colNum = 0;
this.colRank = 0; 28 28 this.colRank = 0;
FlashcardCache[this.id] = this; 29 29 FlashcardCache[this.id] = this;
}; 30 30 };
31 31
32 Flashcard.editModalOptions = {
33 dismissible: true, // Modal can be dismissed by clicking outside of the modal
34 opacity: 0, // Opacity of modal background
35 in_duration: 300, // Transition in duration
36 out_duration: 200, // Transition out duration
37 ready: function () {
38
39 $('#edit-card-input').html(editableText);
40 console.log('opening card edit');
41
42 },
43 complete: function () {
44
45 console.log("EDIT MODAL CLOSED");
46
47 }
48 };
Flashcard.prototype.isInDeck = function () { 32 49 Flashcard.prototype.isInDeck = function () {
return !(typeof Deck.contains(this.id) === 'undefined'); 33 50 return !(typeof Deck.contains(this.id) === 'undefined');
}; 34 51 };
Flashcard.prototype.pullUnpull = function () { 35 52 Flashcard.prototype.pullUnpull = function () {
if (this.isInDeck()) this.unpull(); 36 53 if (this.isInDeck()) this.unpull();
else this.pull(); 37 54 else this.pull();
}; 38 55 };
Flashcard.prototype.pull = function () { 39 56 Flashcard.prototype.pull = function () {
if (this.isInDeck()) return console.log('Not pulling', this.id, "because it's already in deck"); 40 57 if (this.isInDeck()) return console.log('Not pulling', this.id, "because it's already in deck");
return $http.post('/api/flashcards/' + this.id + '/pull/'); 41 58 return $http.post('/api/flashcards/' + this.id + '/pull/');
}; 42 59 };
Flashcard.prototype.unpull = function () { 43 60 Flashcard.prototype.unpull = function () {
if (!this.isInDeck()) return console.log('Not unpulling', this.id, "because it's not in deck"); 44 61 if (!this.isInDeck()) return console.log('Not unpulling', this.id, "because it's not in deck");
return $http.post('/api/flashcards/' + this.id + '/unpull/'); 45 62 return $http.post('/api/flashcards/' + this.id + '/unpull/');
}; 46 63 };
Flashcard.prototype.hide = function () { 47 64 Flashcard.prototype.hide = function () {
return $http.post('/api/flashcards/' + this.id + '/hide/'); 48 65 return $http.post('/api/flashcards/' + this.id + '/hide/');
}; 49 66 };
Flashcard.prototype.unhide = function () { 50 67 Flashcard.prototype.unhide = function () {
return $http.post('/api/flashcards/' + this.id + '/unhide/'); 51 68 return $http.post('/api/flashcards/' + this.id + '/unhide/');
}; 52 69 };
Flashcard.cleanup = function () { 53 70 Flashcard.cleanup = function () {
Deck = null; 54 71 Deck = null;
FlashcardCache = []; 55 72 FlashcardCache = [];
}; 56 73 };
Flashcard.linkDeck = function(deck){ 57 74 Flashcard.linkDeck = function (deck) {
Deck = deck; 58 75 Deck = deck;
} 59 76 }
60 77
Flashcard.prototype.edit = function () { 61 78 Flashcard.prototype.edit = function () {
62 79 var editableText = this.formatted_text;
63 80 $('#editModalA').leanModal(Flashcard.modal_options);
var editableText = this.formatted_text; 64 81 $('#editModalA').openModal(Flashcard.modal_options);
65
$('.modal-trigger').leanModal({ 66
dismissible: true, // Modal can be dismissed by clicking outside of the modal 67
opacity: .5, // Opacity of modal background 68
in_duration: 300, // Transition in duration 69
out_duration: 200, // Transition out duration 70
ready: function () { 71
72
$('#edit-card-input').html(editableText); 73
74
75
}, // Callback for Modal open 76
complete: function () { 77
78
console.log("EDIT MODAL CLOSED"); 79
80
} // Callback for Modal close 81
}); 82
83
}; 84 82 };
85 83
Flashcard.prototype.refreshEditCardInput = function () { 86 84 Flashcard.prototype.refreshEditCardInput = function () {
87 85
this.editCardText = $('#edit-card-input').text(); 88 86 this.editCardText = $('#edit-card-input').text();
89 87
this.submit_enabled = this.editCardText.length >= 5 && this.editCardText.length <= 160; 90 88 this.submit_enabled = this.editCardText.length >= 5 && this.editCardText.length <= 160;
91 89
90 var i = 0;
91 this.editCardBlanks = [];
92 $('#edit-card-input')[0].childNodes.forEach(function (node) {
93 node = $(node)[0];
94 if (node.tagName == 'B') {
95 var text = $(node).text();
96 var leftspaces = 0, rightspaces = 0;
97 // awful way to find the first non-space character from the left or the right. thanks.js
98 while (text[leftspaces] == ' ' || text[leftspaces] == '\xa0') leftspaces++;
99 while (text[text.length - 1 - rightspaces] == ' ' || text[text.length - 1 - rightspaces] == '\xa0') rightspaces++;
100 console.log(leftspaces, text.length);
101 if (leftspaces != text.length) $scope.editCardBlanks.push([i + leftspaces, i + text.length - rightspaces]);
102 i += text.length;
103 } else if (!node.data) {
104 i += $(node).text().length;
105 } else {
106 i += node.data.length;
107 }
108 });
109 this.editCardBlanks.sort(function (a, b) {
110 return a[0] - b[0];
111 });
112 i = 0;
113 newtext = '';
114 this.editCardBlanks.forEach(function (blank) {
115 newtext += this.editCardText.slice(i, blank[0]);
116 newtext += '<b>' + this.editCardText.slice(blank[0], blank[1]) + '</b>';
117 i = blank[1];
118 });
119 newtext += this.editCardText.slice(i);
120 //$scope.newCardFormattedText = newtext;*/
92 121
93 122
var i = 0; 94
this.editCardBlanks = []; 95
$('#edit-card-input')[0].childNodes.forEach(function (node) { 96
node = $(node)[0]; 97
if (node.tagName == 'B') { 98
var text = $(node).text(); 99
var leftspaces = 0, rightspaces = 0; 100
// awful way to find the first non-space character from the left or the right. thanks.js 101
while (text[leftspaces] == ' ' || text[leftspaces] == '\xa0') leftspaces++; 102
while (text[text.length - 1 - rightspaces] == ' ' || text[text.length - 1 - rightspaces] == '\xa0') rightspaces++; 103
console.log(leftspaces, text.length); 104
if (leftspaces != text.length) $scope.editCardBlanks.push([i + leftspaces, i + text.length - rightspaces]); 105
i += text.length; 106
} else if (!node.data) { 107
i += $(node).text().length; 108
} else { 109
i += node.data.length; 110
} 111
}); 112
this.editCardBlanks.sort(function (a, b) { 113
return a[0] - b[0]; 114
}); 115
i = 0; 116
newtext = ''; 117
this.editCardBlanks.forEach(function (blank) { 118
newtext += this.editCardText.slice(i, blank[0]); 119
newtext += '<b>' + this.editCardText.slice(blank[0], blank[1]) + '</b>'; 120
i = blank[1]; 121
}); 122
newtext += this.editCardText.slice(i); 123
//$scope.newCardFormattedText = newtext;*/ 124
125
126
}; 127 123 };
128 124
129
130
131 125
Flashcard.prototype.pushCard = function () { 132 126 Flashcard.prototype.pushCard = function () {
133 127
//console.log() 134 128 //console.log()
135 129
var myCard = { 136 130 var myCard = {
'text': $('#edit-card-input').text(), 137 131 'text': $('#edit-card-input').text(),
'mask': this.editCardBlanks, 138 132 'mask': this.editCardBlanks,
//section: this.section.id 139 133 //section: this.section.id
}; 140 134 };
if (myCard.text == '') { 141 135 if (myCard.text == '') {
console.log('blank flashcard not pushed:' + myCard.text); 142 136 console.log('blank flashcard not pushed:' + myCard.text);
//return closeNewCard(); 143 137 //return closeNewCard();
144 138
$('#editModal').closeModal(modal_options); 145 139 $('#editModal').closeModal(modal_options);
146 140
} 147 141 }
$http.patch('/api/flashcards/' + this.id, myCard). 148 142 $http.patch('/api/flashcards/' + this.id, myCard).
success(function (data) { 149 143 success(function (data) {
console.log('flashcard pushed: ' + myCard.text); 150 144 console.log('flashcard pushed: ' + myCard.text);
if (!UserService.hasVerifiedEmail()) { 151 145 if (!UserService.hasVerifiedEmail()) {
Materialize.toast("<p>Thanks for contributing! However, others won't see your card until you verify your email address<p>", 4000); 152 146 Materialize.toast("<p>Thanks for contributing! However, others won't see your card until you verify your email address<p>", 4000);
} 153 147 }
}); 154 148 });
155 149
$('#editModal').closeModal(modal_options); 156 150 $('#editModal').closeModal(modal_options);
157 151
} 158 152 };
159 153
160 154
Flashcard.prototype.discardChanges = function() { 161 155 Flashcard.prototype.discardChanges = function () {
162 156
$('#editModal').closeModal(modal_options); 163 157 $('#editModal').closeModal(modal_options);
164 158
} 165 159 };
166 160
167 161
templates/feed.html View file @ 5522319
1 <a id="editModalA" href="#" data-target="newCard"></a>
<div id="editModal" class="modal row" style="max-height:none;"> 1 2 <div id="editModal" class="modal row" style="max-height:none;">
<form id="edit-card-form"> 2 3 <form id="edit-card-form">
<div class="modal-content col"> 3 4 <div class="modal-content col">
<div class="row" style="margin-bottom:0"> 4 5 <div class="row" style="margin-bottom:0">
<div class="card cyan-text text-darken-2" 5 6 <div class="card cyan-text text-darken-2"
style="width:300px; height:180px; margin-bottom:0; font-size:120%;"> 6 7 style="width:300px; height:180px; margin-bottom:0; font-size:120%;">
<div class="valign-wrapper"> 7 8 <div class="valign-wrapper">
<div id="edit-card-input" ng-model="newCardFormattedText" style="outline:0px solid transparent;" 8 9 <div id="edit-card-input" ng-model="newCardFormattedText" style="outline:0px solid transparent;"
class="card-content valign center-align" 9 10 class="card-content valign center-align"
contenteditable select-non-editable="true" ng-change="flashcard.refreshEditCardInput()"> 10 11 contenteditable select-non-editable="true" ng-change="flashcard.refreshEditCardInput()">
</div> 11 12 </div>
</div> 12 13 </div>
</div> 13 14 </div>
</div> 14 15 </div>
</div> 15 16 </div>
<div class="col"> 16 17 <div class="col">
<div class="row"> 17 18 <div class="row">
</div> 18 19 </div>
<div class="row"> 19 20 <div class="row">
<button class="btn modal-close tooltipped" type="submit" ng-click="flashcard.pushCard()" 20 21 <button class="btn modal-close tooltipped" type="submit" ng-click="flashcard.pushCard()"
data-position="left" 21 22 data-position="left"
data-delay="50" ng-class="flashcard.submit_enabled?{}:'disabled'" 22 23 data-delay="50" ng-class="flashcard.submit_enabled?{}:'disabled'"
data-tooltip="Enter"> 23 24 data-tooltip="Enter">
Edit 24 25 Edit
<i class="mdi-hardware-keyboard-return right"></i> 25 26 <i class="mdi-hardware-keyboard-return right"></i>
</button> 26 27 </button>
</div> 27 28 </div>
<div class="row"> 28 29 <div class="row">
<button id="blank-selected" style="float:left" class="btn tooltipped" data-position="right" data-delay="50" 29 30 <button id="blank-selected" style="float:left" class="btn tooltipped" data-position="right" data-delay="50"
data-tooltip="Ctrl-B"> 30 31 data-tooltip="Ctrl-B">
Blank Selected Text 31 32 Blank Selected Text
</button> 32 33 </button>
</div> 33 34 </div>
<div class="row" ng-show="flashcard.editCardText" 34 35 <div class="row" ng-show="flashcard.editCardText"
ng-style="(flashcard.editCardText.length>160)?{color:'red'}:{}"> 35 36 ng-style="(flashcard.editCardText.length>160)?{color:'red'}:{}">
{{flashcard.editCardText.length}}/160 characters 36 37 {{flashcard.editCardText.length}}/160 characters
</div> 37 38 </div>
<div class="row" ng-show="flashcard.editCardText.length < 5"> 38 39 <div class="row" ng-show="flashcard.editCardText.length < 5">
Please write a little more! 39 40 Please write a little more!
</div> 40 41 </div>
<div class="row" ng-show="flashcard.editCardText.length > 140"> 41 42 <div class="row" ng-show="flashcard.editCardText.length > 140">
Good flashcards have a<br> 42 43 Good flashcards have a<br>
single atomic fact 43 44 single atomic fact
</div> 44 45 </div>
</div> 45 46 </div>
</form> 46 47 </form>
</div> 47 48 </div>
48 49
49 50
<div class="row"> 50 51 <div class="row">
<h2 ng-cloak ng-show="showGrid && cards.length == 0">No cards. Be the first one to add a card!</h2> 51 52 <h2 ng-cloak ng-show="showGrid && cards.length == 0">No cards. Be the first one to add a card!</h2>
52 53
<div class="progress center-align" style="margin: 70px auto auto;width:50%;" ng-if="!cardCols.length || !showGrid"> 53 54 <div class="progress center-align" style="margin: 70px auto auto;width:50%;" ng-if="!cardCols.length || !showGrid">
<div class="indeterminate"></div> 54 55 <div class="indeterminate"></div>
</div> 55 56 </div>
56 57
<div class="cardColumn" ng-repeat="col in cardCols"> 57 58 <div class="cardColumn" ng-repeat="col in cardCols">
<div class="repeated-card" ng-repeat="card in col"> 58 59 <div class="repeated-card" ng-repeat="card in col">
<flashcard flashcard-obj="card"/> 59 60 <flashcard flashcard-obj="card"/>
</div> 60 61 </div>
</div> 61 62 </div>
</div> 62 63 </div>
63 64
64 65
<!--Lil plus button in corner--> 65 66 <!--Lil plus button in corner-->
<div class="fixed-action-btn" style="bottom: 24px; right: 24px;"> 66 67 <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" 67 68 <a data-target="newCard" class="btn-floating btn-large modal-trigger tooltipped" data-position="left"
data-delay="50" ng-click="openNewCardModal()" 68 69 data-delay="50" ng-click="openNewCardModal()"
data-tooltip="(C)ompose"> 69 70 data-tooltip="(C)ompose">
<i class="large mdi-content-add"></i> 70 71 <i class="large mdi-content-add"></i>
</a> 71 72 </a>
</div> 72 73 </div>
73 74
<div id="newCard" class="modal bottom-sheet row" style="max-height:none;"> 74 75 <div id="newCard" class="modal bottom-sheet row" style="max-height:none;">
<form id="new-card-form"> 75 76 <form id="new-card-form">
<div class="modal-content col"> 76 77 <div class="modal-content col">
<div class="row" style="margin-bottom:0"> 77 78 <div class="row" style="margin-bottom:0">
<div class="card cyan-text text-darken-2" 78 79 <div class="card cyan-text text-darken-2"
style="width:300px; height:180px; margin-bottom:0; font-size:120%;"> 79 80 style="width:300px; height:180px; margin-bottom:0; font-size:120%;">
<div class="valign-wrapper"> 80 81 <div class="valign-wrapper">
<div id="new-card-input" ng-model="newCardFormattedText" style="outline:0px solid transparent;" 81 82 <div id="new-card-input" ng-model="newCardFormattedText" style="outline:0px solid transparent;"
class="card-content valign center-align" 82 83 class="card-content valign center-align"
contenteditable select-non-editable="true" ng-change="refreshNewCardInput()"> 83 84 contenteditable select-non-editable="true" ng-change="refreshNewCardInput()">
84 85
</div> 85 86 </div>
</div> 86 87 </div>
87 88
</div> 88 89 </div>
</div> 89 90 </div>
</div> 90 91 </div>
91 92
<div class="col"> 92 93 <div class="col">
<div class="row"> 93 94 <div class="row">
94 95
</div> 95 96 </div>
<div class="row"> 96 97 <div class="row">
<button class="btn modal-close tooltipped" type="submit" ng-click="pushCard()" 97 98 <button class="btn modal-close tooltipped" type="submit" ng-click="pushCard()"
data-position="left" 98 99 data-position="left"
data-delay="50" ng-class="submit_enabled?{}:'disabled'" 99 100 data-delay="50" ng-class="submit_enabled?{}:'disabled'"
data-tooltip="Enter">Contribute 100 101 data-tooltip="Enter">Contribute
<i class="mdi-hardware-keyboard-return right"></i> 101 102 <i class="mdi-hardware-keyboard-return right"></i>
</button> 102 103 </button>
</div> 103 104 </div>
<div class="row"> 104 105 <div class="row">
<button id="blank-selected" style="float:left" class="btn tooltipped" data-position="right" data-delay="50" 105 106 <button id="blank-selected" style="float:left" class="btn tooltipped" data-position="right" data-delay="50"
data-tooltip="Ctrl-B">Blank Selected Text 106 107 data-tooltip="Ctrl-B">Blank Selected Text
</button> 107 108 </button>
</div> 108 109 </div>
<div class="row" ng-show="newCardText" ng-style="(newCardText.length>160)?{color:'red'}:{}"> 109 110 <div class="row" ng-show="newCardText" ng-style="(newCardText.length>160)?{color:'red'}:{}">
{{newCardText.length}}/160 characters 110 111 {{newCardText.length}}/160 characters
templates/flashcard.html View file @ 5522319
<div class="card flashy smallify black-text text-darken-2" 1 1 <div class="card flashy smallify black-text text-darken-2"
ng-class="{'in-deck':flashcard.isInDeck(), 'card-moveUp':flashcard.moveUp, 2 2 ng-class="{'in-deck':flashcard.isInDeck(), 'card-moveUp':flashcard.moveUp,
'card-moveDown':flashcard.moveDown}"> 3 3 'card-moveDown':flashcard.moveDown}">
<div class="valign-wrapper"> 4 4 <div class="valign-wrapper">
<div class="card-content valign center-align" ng-bind-html="flashcard.formatted_text"></div> 5 5 <div class="card-content valign center-align" ng-bind-html="flashcard.formatted_text"></div>
</div> 6 6 </div>
7 7
8 8
<div class="card-overlay"> 9 9 <div class="card-overlay">
<div class="top-box no-user-select" 10 10 <div class="top-box no-user-select"
ng-click="flashcard.pullUnpull()"> 11 11 ng-click="flashcard.pullUnpull()">
<div class="center-me"> 12 12 <div class="center-me">
<i ng-if="flashcard.isInDeck()" class="fadey mdi-content-remove-circle-outline medium"></i> 13 13 <i ng-if="flashcard.isInDeck()" class="fadey mdi-content-remove-circle-outline medium"></i>
<i ng-if="!flashcard.isInDeck()" class="fadey mdi-content-add-circle-outline medium"></i> 14 14 <i ng-if="!flashcard.isInDeck()" class="fadey mdi-content-add-circle-outline medium"></i>
</div> 15 15 </div>
</div> 16 16 </div>
<div class="bottom-box no-user-select"> 17 17 <div class="bottom-box no-user-select">
18 18
<div class="left-box tooltipped" data-position=" bottom" data-tooltip="Edit"> 19 19 <div class="left-box tooltipped">
<div class="center-me modal-trigger" href="#editModal" ng-click="flashcard.edit()"><i class="mdi-editor-border-color small"></i></div> 20 20 <div class="center-me modal-trigger" ng-click="flashcard.edit()"><i
21 class="mdi-editor-border-color small"></i></div>
</div> 21 22 </div>
22 23
23 24
<div class="right-box tooltipped" ng-click="flashcard.hide()" data-position="bottom" data-tooltip="Hide"> 24
<div class="left-box"> 25
<a class="center-me modal-trigger" href="#editModal" ng-click="flashcard.edit()"><i 26
class="mdi-editor-border-color small"></i></a> 27
</div> 28
29
30
<div class="right-box" ng-click="flashcard.hide()"> 31 25 <div class="right-box" ng-click="flashcard.hide()">
<div class="center-me"><i class="mdi-action-delete small"></i></div> 32 26 <div class="center-me"><i class="mdi-action-delete small"></i></div>
</div> 33 27 </div>
34 28
</div> 35 29 </div>
</div> 36 30 </div>
37 31
<!-- Edit Modal --> 38
<div id="editModal" class="modal row" style="max-height:none;"> 39
<form id="edit-card-form"> 40
<div class="modal-content col"> 41
<div class="row" style="margin-bottom:0"> 42
<div class="card cyan-text text-darken-2" 43
style="width:300px; height:180px; margin-bottom:0; font-size:120%;"> 44
<div class="valign-wrapper"> 45
<div id="edit-card-input" ng-model="newCardFormattedText" style="outline:0px solid transparent;" 46
class="card-content valign center-align" 47
contenteditable select-non-editable="true" ng-change="flashcard.refreshEditCardInput()"> 48
</div> 49
</div> 50
</div> 51
</div> 52
</div> 53
<div class="col"> 54
<div class="row"> 55
</div> 56
<div class="row"> 57
<button class="btn modal-close tooltipped" type="submit" ng-click="flashcard.pushCard()" 58
data-position="left" 59
data-delay="50" ng-class="flashcard.submit_enabled?{}:'disabled'" 60
data-tooltip="Enter"> 61
Edit 62
<i class="mdi-action-done right"></i> 63
</button> 64
</div> 65
66
67
<div class="row"> 68
<button class="btn modal-close" ng-click="flashcard.discardChanges()" 69
data-position="left" 70
data-delay="50"> 71
Discard Changes 72
<i class="mdi-content-clear right"></i> 73
</button> 74
</div> 75
76
<!--<div class="row"> 77
<button id="blank-selected" style="float:left" class="btn tooltipped" data-position="right" data-delay="50" 78
data-tooltip="Ctrl-B"> 79
Blank Selected Text 80
</button> 81
</div>--> 82
83
84
<div class="row" ng-show="flashcard.editCardText" ng-style="(flashcard.editCardText.length>160)?{color:'red'}:{}"> 85
{{flashcard.editCardText.length}}/160 characters 86
</div> 87
<div class="row" ng-show="flashcard.editCardText.length < 5"> 88
Please write a little more! 89
</div> 90
<div class="row" ng-show="flashcard.editCardText.length > 140"> 91
Good flashcards have a<br> 92
single atomic fact 93
</div> 94
</div> 95
</form> 96
</div> 97
98
99
100
<!--<div id="editModal" class="modal"> 101 32 <!--<div id="editModal" class="modal">