Compare View
Commits (3)
Diff
Showing 4 changed files Side-by-side Diff
scripts/FeedController.js
View file @
0c0dbdc
... | ... | @@ -0,0 +1,208 @@ |
1 | +angular.module('flashy.FeedController', | |
2 | + ['ui.router', | |
3 | + 'ngAnimate', | |
4 | + 'ngWebSocket', | |
5 | + 'contenteditable', | |
6 | + 'flashy.DeckFactory']).controller('FeedController', | |
7 | + function($scope, $rootScope, $state, $http, $window, $timeout, $stateParams, $websocket, $interval, UserService, Flashcard, Deck) { | |
8 | + angular.module('flashy.CardGridController').CardGridController.apply(this, arguments); | |
9 | + var promise; | |
10 | + $rootScope.debugFlashcard = false; | |
11 | + | |
12 | + $scope.updateCardScore = function(card, scoreChange) { | |
13 | + console.log('update card score'); | |
14 | + // if no colNum is attached, then this doesn't exist on the feed yet | |
15 | + if (!card.colNum) return; | |
16 | + /*$scope.cardCols[card.colNum].sort(function(a, b) { | |
17 | + return b.score - a.score; | |
18 | + });*/ | |
19 | + | |
20 | + card.score += scoreChange; | |
21 | + var col = card.colNum; | |
22 | + var rank = card.colRank; | |
23 | + var s = Math.sign(scoreChange); | |
24 | + | |
25 | + rank -= s; | |
26 | + if (rank < 0) return; | |
27 | + if (rank == $scope.cardCols[col].length) return; | |
28 | + | |
29 | + $scope.affectedCards = []; | |
30 | + while (s * $scope.cardCols[col][rank].score < s * card.score) { | |
31 | + $scope.affectedCards.push($scope.cardCols[col][rank]); | |
32 | + rank -= s; | |
33 | + if (rank < 0) break; | |
34 | + if (rank == $scope.cardCols[col].length) break; | |
35 | + } | |
36 | + | |
37 | + /*var direction = s ? 'margin- | |
38 | + var computedStyle = $boxTwo.css('margin-left'); | |
39 | + $boxTwo.removeClass('horizTranslate'); | |
40 | + $boxTwo.css('margin-left', computedStyle);*/ | |
41 | + | |
42 | + $scope.cardCols[col].splice(rank + s, 0, $scope.cardCols[col].splice(card.colRank, 1)[0]); | |
43 | + $scope.updateColRanks($scope.cardCols[card.colNum]); // can be optimized out | |
44 | + }; | |
45 | + | |
46 | + | |
47 | + | |
48 | + $scope.feed_ws = $websocket($scope.ws_host + '/ws/feed/' + $scope.sectionId + '?subscribe-broadcast'); | |
49 | + $scope.feed_ws.onMessage(function (e) { | |
50 | + | |
51 | + | |
52 | + console.log("oh yeaaaaaaaaaaaaaaaaaaa"); | |
53 | + | |
54 | + data = JSON.parse(e.data); | |
55 | + console.log('message', data); | |
56 | + if (data.event_type == 'new_card') { | |
57 | + $scope.addCardToGrid(new Flashcard(data.flashcard, $scope.deck)); | |
58 | + } else if (data.event_type == 'score_change') { | |
59 | + card = new Flashcard(data.flashcard); // doesnt create a card if it exists | |
60 | + console.log('score change'); | |
61 | + $scope.updateCardScore(card, data.flashcard.score - card.score); | |
62 | + } | |
63 | + }); | |
64 | + | |
65 | + $scope.pushCard = function() { | |
66 | + var myCard = { | |
67 | + // we can't trim this string because it'd mess up the blanks. Something to fix. | |
68 | + 'text': $('#new-card-input').text(), | |
69 | + 'mask': $scope.newCardBlanks, | |
70 | + section: $scope.section.id | |
71 | + }; | |
72 | + if (myCard.text == '') { | |
73 | + console.log('blank flashcard not pushed:' + myCard.text); | |
74 | + return closeNewCard(); | |
75 | + } | |
76 | + $http.post('/api/flashcards/', myCard). | |
77 | + success(function(data) { | |
78 | + console.log('flashcard pushed: ' + myCard.text); | |
79 | + if (!UserService.hasVerifiedEmail()) { | |
80 | + Materialize.toast("<p>Thanks for contributing! However, others won't see your card until you verify your email address<p>", 4000); | |
81 | + } | |
82 | + }); | |
83 | + return $scope.closeNewCardModal(); | |
84 | + }; | |
85 | + | |
86 | + /* Key bindings for the whole feed window. Hotkey it up! */ | |
87 | + var listenForC = true; | |
88 | + | |
89 | + // Need to pass these options into openmodal and leanmodal, | |
90 | + // otherwise the ready handler doesn't get called | |
91 | + | |
92 | + modal_options = { | |
93 | + dismissible: true, // Modal can be dismissed by clicking outside of the modal | |
94 | + opacity: 0, // Opacity of modal background | |
95 | + in_duration: 300, // Transition in duration | |
96 | + out_duration: 200, // Transition out duration | |
97 | + ready: function() { | |
98 | + $('#new-card-input').focus(); | |
99 | + document.execCommand('selectAll', false, null); | |
100 | + } | |
101 | + }; | |
102 | + | |
103 | + $(document).keydown(function(e) { | |
104 | + var keyed = e.which; | |
105 | + if (keyed == 67 && listenForC) { // "c" for compose | |
106 | + $scope.openNewCardModal(); | |
107 | + e.preventDefault(); | |
108 | + return false; | |
109 | + } else if (keyed == 27) { // clear on ESC | |
110 | + $scope.closeNewCardModal(); | |
111 | + } | |
112 | + }); | |
113 | + | |
114 | + $scope.openNewCardModal = function() { | |
115 | + $('#newCard').openModal(modal_options); | |
116 | + listenForC = false; | |
117 | + $('#new-card-input').html('Write a flashcard!'); | |
118 | + }; | |
119 | + | |
120 | + $scope.closeNewCardModal = function() { | |
121 | + listenForC = true; | |
122 | + $('#new-card-input').html('').blur(); | |
123 | + $('#newCard').closeModal(modal_options); | |
124 | + }; | |
125 | + | |
126 | + $('.tooltipped').tooltip({delay: 50}); | |
127 | + // the "href" attribute of .modal-trigger must specify the modal ID that wants to be triggered | |
128 | + $('.modal-trigger').leanModal(modal_options); | |
129 | + $('#new-card-input').on('keydown', function(e) { | |
130 | + if (e.which == 13) { | |
131 | + e.preventDefault(); | |
132 | + if ($scope.submit_enabled) { | |
133 | + $scope.pushCard(); | |
134 | + listenForC = true; | |
135 | + } | |
136 | + return false; | |
137 | + } else { | |
138 | + | |
139 | + } | |
140 | + }); | |
141 | + $('button#blank-selected').click(function() { | |
142 | + console.log(window.getSelection()); | |
143 | + document.execCommand('bold'); | |
144 | + }); | |
145 | + $scope.newCardBlanks = []; | |
146 | + $scope.refreshNewCardInput = function() { | |
147 | + $scope.newCardText = $('#new-card-input').text(); | |
148 | + $scope.submit_enabled = $scope.newCardText.length >= 5 && $scope.newCardText.length <= 160; | |
149 | + var i = 0; | |
150 | + $scope.newCardBlanks = []; | |
151 | + $('#new-card-input')[0].childNodes.forEach(function(node) { | |
152 | + node = $(node)[0]; | |
153 | + if (node.tagName == 'B') { | |
154 | + var text = $(node).text(); | |
155 | + var leftspaces = 0, rightspaces = 0; | |
156 | + // awful way to find the first non-space character from the left or the right. thanks.js | |
157 | + while (text[leftspaces] == ' ' || text[leftspaces] == '\xa0') leftspaces++; | |
158 | + while (text[text.length - 1 - rightspaces] == ' ' || text[text.length - 1 - rightspaces] == '\xa0') rightspaces++; | |
159 | + console.log(leftspaces, text.length); | |
160 | + if (leftspaces != text.length) $scope.newCardBlanks.push([i + leftspaces, i + text.length - rightspaces]); | |
161 | + i += text.length; | |
162 | + } else if (!node.data) { | |
163 | + i += $(node).text().length; | |
164 | + } else { | |
165 | + i += node.data.length; | |
166 | + } | |
167 | + }); | |
168 | + $scope.newCardBlanks.sort(function(a, b) { | |
169 | + return a[0] - b[0]; | |
170 | + }); | |
171 | + i = 0; | |
172 | + newtext = ''; | |
173 | + $scope.newCardBlanks.forEach(function(blank) { | |
174 | + newtext += $scope.newCardText.slice(i, blank[0]); | |
175 | + newtext += '<b>' + $scope.newCardText.slice(blank[0], blank[1]) + '</b>'; | |
176 | + i = blank[1]; | |
177 | + }); | |
178 | + newtext += $scope.newCardText.slice(i); | |
179 | + //$scope.newCardFormattedText = newtext; | |
180 | + }; | |
181 | + $scope.shuffleCards = function() { | |
182 | + $timeout(function() { | |
183 | + (function(o) { | |
184 | + for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x); | |
185 | + return o; | |
186 | + })($scope.cardCols[0]); | |
187 | + }); | |
188 | + }; | |
189 | + $scope.$on('$destroy', function() { | |
190 | + $scope.feed_ws.close(); | |
191 | + $interval.cancel(promise); | |
192 | + }); | |
193 | + return $http.get('/api/sections/' + sectionId + '/feed/'). | |
194 | + success(function(data) { | |
195 | + console.log(data); | |
196 | + $scope.cards = data.map(function(card) { | |
197 | + return new Flashcard(card, $scope.deck); | |
198 | + }); | |
199 | + $scope.refreshLayout().then(function() { | |
200 | + $timeout($scope.refreshColumnWidth).then(function() { | |
201 | + $scope.showGrid = true; | |
202 | + }); | |
203 | + console.log('layout done'); | |
204 | + }); | |
205 | + }); | |
206 | + | |
207 | + | |
208 | + }); | |
0 | 209 | \ No newline at end of file |
scripts/FlashcardDirective.js
View file @
0c0dbdc
1 | 1 | angular.module('flashy.FlashcardDirective', []). |
2 | 2 | |
3 | 3 | directive('flashcard', |
4 | - function($http, $state, $window) { | |
4 | + | |
5 | + | |
6 | + function($http, $state, $window) { | |
5 | 7 | return { |
6 | - templateUrl: '/app/templates/flashcard.html', | |
7 | - restrict: 'E', | |
8 | - scope: { | |
9 | - flashcard: '=flashcardObj', // flashcard-obj in parent html | |
10 | - refresh: '&' // eval refresh in parent html | |
11 | - }, | |
12 | - colNum: 0, | |
13 | - colRank: 0, | |
14 | - //link: function(scope, element) { | |
15 | - // $('.tooltipped').tooltip(); | |
16 | - // /* Handles width of the card */ | |
17 | - //} | |
8 | + templateUrl: '/app/templates/flashcard.html', | |
9 | + restrict: 'E', | |
10 | + scope: { | |
11 | + flashcard: '=flashcardObj', // flashcard-obj in parent html | |
12 | + refresh: '&' // eval refresh in parent html | |
13 | + }, | |
14 | + colNum: 0, | |
15 | + colRank: 0, | |
16 | + link: function(scope, element) { | |
17 | + $('.tooltipped').tooltip(); | |
18 | + // /* Handles width of the card */ | |
19 | + } | |
18 | 20 | }; |
19 | - } | |
21 | + } | |
22 | + | |
20 | 23 | ); |
21 | - | |
22 | 24 | \ No newline at end of file |
25 | + |
scripts/FlashcardFactory.js
View file @
0c0dbdc
1 | 1 | angular.module('flashy.FlashcardFactory', ['ui.router']). |
2 | - factory('Flashcard', function ($http) { | |
2 | + factory('Flashcard', function ($http, UserService) { | |
3 | 3 | var FlashcardCache = []; |
4 | 4 | var Deck = null; |
5 | 5 | var Flashcard = function (data) { |
... | ... | @@ -54,5 +54,111 @@ angular.module('flashy.FlashcardFactory', ['ui.router']). |
54 | 54 | Deck = deck; |
55 | 55 | } |
56 | 56 | |
57 | + Flashcard.prototype.edit = function () { | |
58 | + | |
59 | + | |
60 | + var editableText = this.formatted_text; | |
61 | + | |
62 | + //$('#flashcardEditText').html(this.formatted_text); | |
63 | + | |
64 | + $('.modal-trigger').leanModal({ | |
65 | + dismissible: true, // Modal can be dismissed by clicking outside of the modal | |
66 | + opacity: .5, // Opacity of modal background | |
67 | + in_duration: 300, // Transition in duration | |
68 | + out_duration: 200, // Transition out duration | |
69 | + ready: function () { | |
70 | + | |
71 | + $('#edit-card-input').html(editableText); | |
72 | + | |
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 | + | |
85 | + | |
86 | + | |
87 | + Flashcard.prototype.refreshEditCardInput = function () { | |
88 | + | |
89 | + console.log("ASDFASDFASDFASFD"); | |
90 | + | |
91 | + | |
92 | + this.editCardText = $('#edit-card-input').text(); | |
93 | + | |
94 | + | |
95 | + this.submit_enabled = this.editCardText.length >= 5 && this.editCardText.length <= 160; | |
96 | + | |
97 | + | |
98 | + | |
99 | + var i = 0; | |
100 | + this.editCardBlanks = []; | |
101 | + $('#edit-card-input')[0].childNodes.forEach(function (node) { | |
102 | + node = $(node)[0]; | |
103 | + if (node.tagName == 'B') { | |
104 | + var text = $(node).text(); | |
105 | + var leftspaces = 0, rightspaces = 0; | |
106 | + // awful way to find the first non-space character from the left or the right. thanks.js | |
107 | + while (text[leftspaces] == ' ' || text[leftspaces] == '\xa0') leftspaces++; | |
108 | + while (text[text.length - 1 - rightspaces] == ' ' || text[text.length - 1 - rightspaces] == '\xa0') rightspaces++; | |
109 | + console.log(leftspaces, text.length); | |
110 | + if (leftspaces != text.length) $scope.editCardBlanks.push([i + leftspaces, i + text.length - rightspaces]); | |
111 | + i += text.length; | |
112 | + } else if (!node.data) { | |
113 | + i += $(node).text().length; | |
114 | + } else { | |
115 | + i += node.data.length; | |
116 | + } | |
117 | + }); | |
118 | + this.editCardBlanks.sort(function (a, b) { | |
119 | + return a[0] - b[0]; | |
120 | + }); | |
121 | + i = 0; | |
122 | + newtext = ''; | |
123 | + this.editCardBlanks.forEach(function (blank) { | |
124 | + newtext += this.editCardText.slice(i, blank[0]); | |
125 | + newtext += '<b>' + this.editCardText.slice(blank[0], blank[1]) + '</b>'; | |
126 | + i = blank[1]; | |
127 | + }); | |
128 | + newtext += this.editCardText.slice(i); | |
129 | + //$scope.newCardFormattedText = newtext;*/ | |
130 | + | |
131 | + | |
132 | + }; | |
133 | + | |
134 | + | |
135 | + | |
136 | + | |
137 | + Flashcard.prototype.pushCard = function () { | |
138 | + | |
139 | + //console.log() | |
140 | + | |
141 | + var myCard = { | |
142 | + 'text': $('#edit-card-input').text(), | |
143 | + 'mask': this.editCardBlanks, | |
144 | + //section: this.section.id | |
145 | + }; | |
146 | + if (myCard.text == '') { | |
147 | + console.log('blank flashcard not pushed:' + myCard.text); | |
148 | + return closeNewCard(); | |
149 | + } | |
150 | + $http.patch('/api/flashcards/' + this.id, myCard). | |
151 | + success(function (data) { | |
152 | + console.log('flashcard pushed: ' + myCard.text); | |
153 | + if (!UserService.hasVerifiedEmail()) { | |
154 | + Materialize.toast("<p>Thanks for contributing! However, others won't see your card until you verify your email address<p>", 4000); | |
155 | + } | |
156 | + }); | |
157 | + //return .closeNewCardModal(); | |
158 | + | |
159 | + } | |
160 | + | |
161 | + | |
162 | + | |
57 | 163 | return Flashcard; |
58 | 164 | }); |
templates/flashcard.html
View file @
0c0dbdc
1 | +<div class="card flashy smallify black-text text-darken-2" ng-class="{'in-deck':flashcard.isInDeck()}"> | |
2 | + | |
1 | 3 | <div class="card flashy black-text" |
2 | 4 | ng-class="{'in-deck':flashcard.isInDeck()}"> |
3 | 5 | <div class="valign-wrapper"> |
4 | 6 | <div class="card-content valign center-align" ng-bind-html="flashcard.formatted_text"></div> |
5 | 7 | </div> |
8 | + | |
9 | + | |
10 | + | |
11 | + | |
12 | + | |
6 | 13 | <div class="card-overlay"> |
7 | 14 | <div class="top-box no-user-select" |
8 | 15 | ng-click="flashcard.pullUnpull()"> |
... | ... | @@ -12,15 +19,87 @@ |
12 | 19 | </div> |
13 | 20 | </div> |
14 | 21 | <div class="bottom-box no-user-select"> |
15 | - <div class="left-box tooltipped" data-position="bottom" data-tooltip="Info"> | |
16 | - <div class="center-me"><i class="mdi-action-info-outline small"></i></div> | |
22 | + | |
23 | + | |
24 | + <div class="left-box tooltipped" data-position=" bottom" data-tooltip="Info"> | |
25 | + <div class="center-me modal-trigger" href="#editModal" ng-click="flashcard.edit()"><i class="mdi-editor-border-color small"></i></div> | |
17 | 26 | </div> |
27 | + | |
28 | + | |
29 | + | |
30 | + | |
18 | 31 | <div class="right-box tooltipped" ng-click="flashcard.hide()" data-position="bottom" data-tooltip="Hide"> |
19 | 32 | <div class="center-me"><i class="mdi-action-delete small"></i></div> |
20 | 33 | </div> |
21 | 34 | |
22 | 35 | </div> |
23 | 36 | </div> |
37 | + | |
38 | + | |
39 | + <!--<div id="editModal" class="modal"> | |
40 | + <div class="modal-content"> | |
41 | + <h4 id="flashcardEditText"></h4> | |
42 | + </div> | |
43 | + <div class="modal-footer"> | |
44 | + | |
45 | + </div> | |
46 | + </div>--> | |
47 | + | |
48 | + | |
49 | + | |
50 | + <div id="editModal" class="modal row" style="max-height:none;"> | |
51 | + <form id="edit-card-form"> | |
52 | + <div class="modal-content col"> | |
53 | + <div class="row" style="margin-bottom:0"> | |
54 | + <div class="card cyan-text text-darken-2" | |
55 | + style="width:300px; height:180px; margin-bottom:0; font-size:120%;"> | |
56 | + <div class="valign-wrapper"> | |
57 | + <div id="edit-card-input" ng-model="newCardFormattedText" style="outline:0px solid transparent;" | |
58 | + class="card-content valign center-align" | |
59 | + contenteditable select-non-editable="true" ng-change="flashcard.refreshEditCardInput()"> | |
60 | + </div> | |
61 | + </div> | |
62 | + </div> | |
63 | + </div> | |
64 | + </div> | |
65 | + <div class="col"> | |
66 | + <div class="row"> | |
67 | + </div> | |
68 | + <div class="row"> | |
69 | + <button class="btn modal-close tooltipped" type="submit" ng-click="flashcard.pushCard()" | |
70 | + data-position="left" | |
71 | + data-delay="50" ng-class="flashcard.submit_enabled?{}:'disabled'" | |
72 | + data-tooltip="Enter"> | |
73 | + Edit | |
74 | + <i class="mdi-hardware-keyboard-return right"></i> | |
75 | + </button> | |
76 | + </div> | |
77 | + <div class="row"> | |
78 | + <button id="blank-selected" style="float:left" class="btn tooltipped" data-position="right" data-delay="50" | |
79 | + data-tooltip="Ctrl-B"> | |
80 | + Blank Selected Text | |
81 | + </button> | |
82 | + </div> | |
83 | + <div class="row" ng-show="flashcard.editCardText" ng-style="(flashcard.editCardText.length>160)?{color:'red'}:{}"> | |
84 | + {{flashcard.editCardText.length}}/160 characters | |
85 | + </div> | |
86 | + <div class="row" ng-show="flashcard.editCardText.length < 5"> | |
87 | + Please write a little more! | |
88 | + </div> | |
89 | + <div class="row" ng-show="flashcard.editCardText.length > 140"> | |
90 | + Good flashcards have a<br> | |
91 | + single atomic fact | |
92 | + </div> | |
93 | + </div> | |
94 | + </form> | |
95 | + </div> | |
96 | + | |
97 | + | |
98 | + | |
99 | + | |
100 | + | |
101 | + | |
102 | + | |
24 | 103 | <div ng-show="flashcard.isInDeck()" class="green-text" style="position:absolute; top:-9px;right:0px"> |
25 | 104 | <div class="center-me tooltipped" data-position="bottom" data-tooltip="In deck"><i |
26 | 105 | class="mdi-action-done small"></i></div> |
... | ... | @@ -28,4 +107,9 @@ |
28 | 107 | <div ng-show="false" style="position:absolute; bottom:0px; right:5px;"> |
29 | 108 | <span class="center-me">score:{{flashcard.score}}</span> |
30 | 109 | </div> |
110 | + | |
111 | + | |
112 | + | |
113 | + | |
114 | + | |
31 | 115 | </div> |