add block toolbar to create a code section
[chloro.git] / chloro / phyll / static / js / chloro.js
1 $(function() {
2   $('div[contenteditable]').on('input', function(event) {
3     if (event.originalEvent.inputType == "insertParagraph") {
4       var sel = document.getSelection();
5       var anchorNode = sel.anchorNode;
6       var prev_p = sel.anchorNode.previousSibling;
7       if (! prev_p) return;
8       if (prev_p.tagName != 'P') {
9         prev_p = $(prev_p).parents('p')[0];
10       }
11       var title_match = prev_p.innerText.match(/^(h[1-6]). /);
12       if (title_match) {
13         var title = document.createElement(title_match[1]);
14         title.innerHTML = prev_p.innerHTML;
15         title.textContent = title.textContent.slice(4);
16         prev_p.replaceWith(title);
17       }
18     }
19     return true;
20   });
21   $('div[contenteditable]').on('keyup', function(event) {
22     var sel = document.getSelection();
23     if (sel.anchorNode instanceof Element && sel.anchorOffset == 0 && sel.isCollapsed) {
24       // start of line
25       show_block_style_popup();
26     } else {
27       hide_block_style_popup();
28     }
29     return true;
30   });
31
32   $('#save').on('click', function() {
33     var text = $('div[contenteditable]')[0].innerHTML;
34     var csrf = $('[name=csrfmiddlewaretoken]').val();
35     $.post('api-save/',
36       { text: text, csrfmiddlewaretoken: csrf}
37     ).fail(function() {
38       $('#save').css('background', 'red');
39     });
40     return false;
41   });
42
43   var block_style_popup = null;
44   function block_style() {
45     var action = $(this).data('action');
46     var class_name = null;
47     if (action == 'code') {
48       action = 'pre';
49       class_name = 'screen';
50     }
51     document.execCommand('formatBlock', false, action);
52     var sel = window.getSelection();
53     if (class_name) {
54       $(sel.anchorNode).addClass(class_name);
55     }
56     var range = document.createRange();
57     range.setStart(sel.anchorNode, 0);
58     sel.removeAllRanges();
59     sel.addRange(range);
60     hide_block_style_popup();
61   }
62   function show_block_style_popup() {
63     if (block_style_popup === null) {
64       block_style_popup = $(
65                       '<div class="style-popup">' +
66                       '<button data-action="code">code</button>' +
67                       '</div>');
68       block_style_popup.hide();
69       block_style_popup.insertAfter($('.actions'));
70       block_style_popup.find('button').on('click', block_style);
71     }
72     block_style_popup.css('position', 'absolute');
73     var sel = window.getSelection();
74     var pos = $(sel.anchorNode).offset();
75     block_style_popup.css('top', pos.top - 33);
76     block_style_popup.css('left', pos.left);
77     block_style_popup.show();
78   }
79   function hide_block_style_popup() {
80     if (block_style_popup) {
81       $(block_style_popup).hide();
82     }
83   }
84
85   var style_popup = null;
86   function update_style() {
87     var action = $(this).data('action');
88     var param = null;
89     if (action == 'code') {
90       action = 'insertHTML';
91       param = $('<code></code>', {text: window.getSelection().toString()})[0].outerHTML;
92     }
93     if (action == 'createLink') {
94       var sel = window.getSelection();
95       var $input = $('input[name=link-target]');
96       $input[0]._range = sel.getRangeAt(0);
97       if (sel.anchorNode instanceof Element) {
98         var elem = sel.anchorNode.childNodes[sel.anchorOffset];
99         if (elem.tagName == 'A') {
100           $input.val(elem.href);
101         }
102       }
103       $input.addClass('shown');
104       $input.focus();
105       return;
106     }
107     document.execCommand(action, false, param);
108   }
109   function validate_link(ev) {
110     var charCode = typeof ev.which == "number" ? ev.which : ev.keyCode;
111     if (ev.key == "Enter") {
112       var $input = $(this);
113       var range = this._range;
114       var url = $input.val();
115       $input.removeClass('shown');
116       var sel = window.getSelection();
117       sel.addRange(this._range);
118       if (url) {
119         document.execCommand('createLink', false, url);
120       } else {
121         document.execCommand('unlink', false, null);
122       }
123       sel.empty();
124       $input.val('');
125     }
126   }
127   function focusout_link(ev) {
128     var $input = $(this);
129     $input.removeClass('shown');
130     var range = this._range;
131     var sel = window.getSelection();
132     sel.addRange(this._range);
133   }
134
135   function show_style_popup(sel) {
136     if (style_popup === null) {
137       style_popup = $('<div class="style-popup short">' +
138                       '<button data-action="italic"><i>i</i></button>' +
139                       '<button data-action="bold"><b>b</b></button>' +
140                       '<button data-action="code">#</button>' +
141                       '<button data-action="removeFormat">×</button>' +
142                       '<button data-action="createLink">a</button>' +
143                       '<input name="link-target"/>' +
144                       '</div>');
145       style_popup.hide();
146       style_popup.insertAfter($('.actions'));
147       style_popup.find('button').on('click', update_style);
148       style_popup.find('[name=link-target]').on('keypress', validate_link).on('focusout', focusout_link);
149     }
150     style_popup.css('position', 'absolute');
151     var pos = sel.getRangeAt(0).getClientRects()[0];
152     style_popup.css('top', pos.top + window.scrollY - 33);
153     style_popup.css('left', pos.left + window.scrollX);
154     style_popup.show();
155   };
156   $(document).on('selectionchange', function(event) {
157     if ($('input[name=link-target].shown').length) {
158       return;
159     }
160     var sel = window.getSelection();
161     if ($(sel.anchorNode).parents('div[contenteditable]').length && sel.toString()) {
162       show_style_popup(sel);
163     } else if (style_popup) {
164       $(style_popup).hide();
165     }
166   });
167 });