WIP: Note editing, markdown to html
[oweals/karmaworld.git] / karmaworld / assets / js / note-detail.js
1
2 function rescalePdf(viewer) {
3   var scaleBase = 750;
4   var outlineWidth = 250;
5   var frameWidth = parseInt($('#note_container')[0].clientWidth);
6   var pdfWidth = frameWidth;
7
8   if ($(viewer.sidebar).hasClass('opened')){
9     pdfWidth = pdfWidth - 250;
10   }
11
12   var newPdfScale = pdfWidth / scaleBase;
13   viewer.rescale(newPdfScale);
14 }
15
16 function setupPdfViewer(noteframe, pdfViewer) {
17
18   $('#plus-btn').click(function (){
19     pdfViewer.rescale(1.20, true, [0,0]);
20   });
21
22   $('#minus-btn').click(function (){
23     pdfViewer.rescale(0.80, true, [0,0]);
24   });
25
26   // detect if the PDF viewer wants to show an outline
27   // at all
28   if ($(pdfViewer.sidebar).hasClass('opened')) {
29     var body = $(document.body);
30     // if the screen is less than 64em wide, hide the outline
31     if (parseInt($(body.width()).toEm({scope: body})) < 64) {
32       $(pdfViewer.sidebar).removeClass('opened');
33     }
34   }
35
36   $('#outline-btn').click(function() {
37     $(pdfViewer.sidebar).toggleClass('opened');
38     // rescale the PDF to fit the available space
39     rescalePdf(pdfViewer);
40   });
41
42   $('#scroll-to').change(function() {
43     page = parseInt($(this).val());
44     pdfViewer.scroll_to(page, [0,0]);
45   });
46
47   // rescale the PDF to fit the available space
48   rescalePdf(pdfViewer);
49 }
50
51 function writeNoteFrame(contents) {
52   var dstFrame = document.getElementById('noteframe');
53   if (dstFrame) {
54     var dstDoc = dstFrame.contentDocument || dstFrame.contentWindow.document;
55     dstDoc.write(contents);
56     dstDoc.close();
57   }
58 }
59
60 function setupAnnotator(noteElement, readOnly) {
61   noteElement.annotator({readOnly: readOnly});
62   noteElement.annotator('addPlugin', 'Store', {
63     prefix: '/ajax/annotations',
64     loadFromSearch: {
65       'uri': note_id
66     },
67     annotationData: {
68       'uri': note_id
69     }
70   });
71 }
72
73 function injectRemoteScript(url, noteframe, onload) {
74   var injectScript = noteframe.document.createElement("script");
75   injectScript.src = url;
76   injectScript.onload = onload;
77   noteframe.document.head.appendChild(injectScript);
78 }
79
80 function injectScript(scriptText, noteframe) {
81   var injectScript = noteframe.document.createElement("script");
82   injectScript.innerHTML = scriptText;
83   noteframe.document.body.appendChild(injectScript);
84 }
85
86 function injectRemoteCSS(url, noteframe) {
87   var injectCSS = noteframe.document.createElement("link");
88   injectCSS.href = url;
89   injectCSS.type = 'text/css';
90   injectCSS.rel = 'stylesheet';
91   noteframe.document.head.appendChild(injectCSS);
92 }
93
94 function tabHandler(event) {
95   // check for:
96   // key pressed was TAB
97   // key was pressed in last row
98   if (event.which == 9) {
99     var totalForms = parseInt($('#id_form-TOTAL_FORMS').attr('value'));
100     var formIndex = parseInt($(this).closest('div.keyword-form-row').data('index'));
101     if (formIndex === totalForms-1) {
102       addForm(event);
103       event.preventDefault();
104     }
105   }
106 }
107
108 function addForm(event) {
109   var prototypeForm = $('#keyword-form-prototype div.keyword-form-row').clone().appendTo('#keyword-form-rows');
110   var newForm = $('.keyword-form-row:last');
111   var totalForms = $('#id_form-TOTAL_FORMS').attr('value');
112   var newIdRoot = 'id_form-' + totalForms + '-';
113   var newNameRoot = 'form-' + totalForms + '-';
114
115   newForm.data('index', totalForms);
116
117   var keywordInput = newForm.find('.keyword');
118   keywordInput.attr('id', newIdRoot + 'keyword');
119   keywordInput.attr('name', newNameRoot + 'keyword');
120   keywordInput.focus();
121
122   var definitionInput = newForm.find('.definition');
123   definitionInput.attr('id', newIdRoot + 'definition');
124   definitionInput.attr('name', newNameRoot + 'definition');
125   definitionInput.keydown(tabHandler);
126
127   var objectIdInput = newForm.find('.object-id');
128   objectIdInput.attr('id', newIdRoot + 'id');
129   objectIdInput.attr('name', newNameRoot + 'id');
130
131   $('#id_form-TOTAL_FORMS').attr('value', parseInt(totalForms)+1);
132
133   keywordInput.focus();
134 }
135
136 function initNoteContentPage() {
137
138   $("#thank-button").click(function(event) {
139     event.preventDefault();
140
141     // increment number in page right away
142     var thankNumber = $("#thank-number");
143     thankNumber.text(parseInt(thankNumber.text()) + 1);
144
145     // disable thank button so it can't
146     // be pressed again
147     $(this).hide();
148     $('#thank-button-disabled').show();
149     $(this).unbind('click');
150
151     // tell server that somebody thanked
152     // this note
153     $.ajax({
154       url: note_thank_url,
155       dataType: "json",
156       type: 'POST'
157     });
158   });
159
160   $("#flag-button").click(function(event) {
161     event.preventDefault();
162
163     if (confirm('Do you wish to flag this note for deletion?')) {
164       // disable thank button so it can't
165       // be pressed again
166       $(this).hide();
167       $('#flag-button-disabled').show();
168       $(this).unbind('click');
169
170       // tell server that somebody flagged
171       // this note
172       $.ajax({
173         url: note_flag_url,
174         dataType: "json",
175         type: 'POST'
176       });
177     }
178   });
179
180   $('#save_note_tags').click(function(event) {
181     $.ajax({
182       url: edit_note_tags_url,
183       dataType: 'json',
184       data: $('#note_tags_input').val(),
185       type: 'POST',
186       success: function(data) {
187         $('#note_tags_form').slideUp();
188         $('.tags').empty();
189         $.each(data.fields.tags, function(index, tag) {
190           $('.tags').append($('<span>', { class: 'tag-span', text: tag }));
191         });
192         $('#note-tag-dialog').foundation('reveal', 'close');
193       }
194     });
195   });
196
197   $("#note-download-button").click(function(event) {
198     if (confirm('It costs 2 karma points to download a note. Are you sure?')) {
199       // disable handler so it won't be run again
200       $(this).unbind('click');
201
202       // tell server that somebody downloaded
203       // this note
204       $.ajax({
205         url: note_downloaded_url,
206         dataType: "json",
207         type: 'POST',
208         async: false
209       })
210     };
211   });
212
213   $('#delete-note-button').click(function (event) {
214     if (!confirm("Are you sure you want to delete this note?")) {
215       event.preventDefault();
216     }
217   });
218
219   // Embed the converted markdown if it is on the page, else default to the iframe
220   if ($('#note-markdown').length > 0) {
221     var note_markdown = $('#note-markdown');
222     note_markdown.html(marked(note_markdown.data('markdown')));
223     setupAnnotator(note_markdown, !user_authenticated);
224   } else {
225     $.ajax(note_contents_url, {
226       type: 'GET',
227       xhrFields: {
228         onprogress: function (progress) {
229           var percentage = Math.floor((progress.loaded / progress.total) * 100);
230           writeNoteFrame("<h3 style='text-align: center'>" + percentage + "%</h3>");
231         }
232       },
233       success: function(data, textStatus, jqXHR) {
234         writeNoteFrame(data);
235
236         // run setupAnnotator in frame context
237         var parentFrame = document.getElementById('noteframe');
238         if (!parentFrame) {
239           return;
240         }
241         var noteframe = parentFrame.contentWindow;
242
243         injectRemoteCSS(annotator_css_url, noteframe);
244         injectScript("csrf_token = '" + csrf_token + "';", noteframe);
245
246         injectRemoteScript("https://code.jquery.com/jquery-2.1.0.min.js", noteframe,
247           function() {
248             injectRemoteScript(setup_ajax_url, noteframe);
249             injectRemoteScript(annotator_js_url, noteframe,
250               function() {
251                 var js = "$(function() { \
252                   var document_selector = $('body'); \
253                   if ($('#page-container').length > 0) { \
254                     document_selector = $('#page-container'); \
255                   } \
256                   document_selector.annotator({readOnly: " + !user_authenticated + "}); \
257                   document_selector.annotator('addPlugin', 'Store', { \
258                     prefix: '/ajax/annotations', \
259                     loadFromSearch: { \
260                     'uri': " + note_id + " \
261                   }, \
262                   annotationData: { \
263                     'uri': " + note_id + " \
264                   } \
265                 }); })";
266                 injectScript(js, noteframe);
267
268                 if (pdfControls == true) {
269                   var pdfViewer = noteframe.pdf2htmlEX.defaultViewer;
270                   $(noteframe.document).ready(function() {
271                     setupPdfViewer(noteframe, pdfViewer);
272                   });
273                 }
274               });
275           });
276       },
277       error: function(data, textStatus, jqXHR) {
278         writeNoteFrame("<h3 style='text-align: center'>Sorry, your note could not be retrieved.</h3>");
279       }
280     });
281   }
282 }
283
284 function initNoteKeywordsPage() {
285   $('.definition').keydown(tabHandler);
286   $('#add-row-btn').click(addForm);
287
288   $('#keywords-data-table').dataTable({
289     // don't provide a option for the user to change the table page length
290     'bLengthChange': false,
291     'sDom': 't<"clear">'
292   });
293
294   $('#edit-keywords-button').click(function(event) {
295     $('#keywords-data-table').toggle();
296     $('#keyword-form').toggle();
297   });
298
299 }
300
301 function markQuestionCorrect(question) {
302   question.find('.quiz-question').addClass('correct');
303   question.find('.quiz-question').removeClass('incorrect');
304   question.find('.correct-label').show();
305   question.find('.incorrect-label').hide();
306 }
307
308 function markQuestionIncorrect(question) {
309   question.find('.quiz-question').addClass('incorrect');
310   question.find('.quiz-question').removeClass('correct');
311   question.find('.incorrect-label').show();
312   question.find('.correct-label').hide();
313 }
314
315 function initQuizPage() {
316   $('#check-answers-button').click(function() {
317     $('.quiz-question-wrapper').each(function() {
318       var choice = $(this).find('input:checked');
319       if (choice.length) {
320         if (choice.data('correct') == true) {
321           markQuestionCorrect($(this));
322         } else {
323           markQuestionIncorrect($(this));
324         }
325       }
326
327
328       var options_selected = $(this).find('option:selected');
329       if (options_selected.length > 0) {
330         var matching_correct = true;
331         options_selected.each(function() {
332           if ($(this).data('correct') == false) {
333             matching_correct = false;
334           }
335         });
336         if (matching_correct) {
337           markQuestionCorrect($(this));
338         } else {
339           markQuestionIncorrect($(this));
340         }
341       }
342     });
343   });
344 }
345