]> git.0d.be Git - chloro.git/blob - chloro/phyll/static/js/chloro.js
c47bb0a16ec5e159a34a9d48c271799a7af65b09
[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 && (
24             (sel.anchorOffset == 0 && sel.isCollapsed) || // first line
25             (sel.anchorNode.tagName === 'PRE'))) ||
26         (sel.anchorNode.parentNode.tagName === 'PRE')) {
27       // start of line
28       show_block_style_popup();
29     } else {
30       hide_block_style_popup();
31     }
32     return true;
33   });
34
35   $('#save').on('click', function() {
36     var text = $('div[contenteditable]')[0].innerHTML;
37     var csrf = $('[name=csrfmiddlewaretoken]').val();
38     $.post('api-save/',
39       { text: text, csrfmiddlewaretoken: csrf}
40     ).fail(function() {
41       $('#save').css('background', 'red');
42     });
43     return false;
44   });
45
46   var block_style_popup = null;
47   function block_style() {
48     var action = $(this).data('action');
49     var class_name = null;
50     if (action == 'code') {
51       action = 'pre';
52       class_name = 'screen';
53       if ($(this).hasClass('on')) {  // toggle off
54         action = 'p';
55         class_name = null;
56       }
57     }
58     document.execCommand('formatBlock', false, action);
59     var sel = window.getSelection();
60     if (class_name) {
61       $(sel.anchorNode).addClass(class_name);
62     }
63     var range = document.createRange();
64     range.setStart(sel.anchorNode, 0);
65     sel.removeAllRanges();
66     sel.addRange(range);
67   }
68   function show_block_style_popup() {
69     if (block_style_popup === null) {
70       block_style_popup = $(
71                       '<div class="style-popup">' +
72                       '<button data-action="code">code</button>' +
73                       '</div>');
74       block_style_popup.hide();
75       block_style_popup.insertAfter($('.actions'));
76       block_style_popup.find('button').on('click', block_style);
77     }
78     block_style_popup.css('position', 'absolute');
79     var sel = window.getSelection();
80     var anchor = sel.anchorNode;
81     if (anchor instanceof Text) {
82       anchor = anchor.parentNode;
83     }
84     if (anchor.tagName === "PRE") {
85       block_style_popup.find('[data-action=code]').addClass('on');
86     } else {
87       block_style_popup.find('[data-action=code]').removeClass('on');
88     }
89     var pos = $(anchor).offset();
90     block_style_popup.css('top', pos.top - 33);
91     block_style_popup.css('left', pos.left);
92     block_style_popup.show();
93   }
94   function hide_block_style_popup() {
95     if (block_style_popup) {
96       $(block_style_popup).hide();
97     }
98   }
99
100   var style_popup = null;
101   function update_style() {
102     var action = $(this).data('action');
103     var param = null;
104     if (action == 'code') {
105       action = 'insertHTML';
106       param = $('<code></code>', {text: window.getSelection().toString()})[0].outerHTML;
107     }
108     if (action == 'createLink') {
109       var sel = window.getSelection();
110       var $input = $('input[name=link-target]');
111       $input[0]._range = sel.getRangeAt(0);
112       if (sel.anchorNode instanceof Element) {
113         var elem = sel.anchorNode.childNodes[sel.anchorOffset];
114         if (elem.tagName == 'A') {
115           $input.val(elem.href);
116         }
117       }
118       $input.addClass('shown');
119       $input.focus();
120       return;
121     }
122     document.execCommand(action, false, param);
123   }
124   function validate_link(ev) {
125     var charCode = typeof ev.which == "number" ? ev.which : ev.keyCode;
126     if (ev.key == "Enter") {
127       var $input = $(this);
128       var range = this._range;
129       var url = $input.val();
130       $input.removeClass('shown');
131       var sel = window.getSelection();
132       sel.addRange(this._range);
133       if (url) {
134         document.execCommand('createLink', false, url);
135       } else {
136         document.execCommand('unlink', false, null);
137       }
138       sel.empty();
139       $input.val('');
140     }
141   }
142   function focusout_link(ev) {
143     var $input = $(this);
144     $input.removeClass('shown');
145     var range = this._range;
146     var sel = window.getSelection();
147     sel.addRange(this._range);
148   }
149
150   function show_style_popup(sel) {
151     if (style_popup === null) {
152       style_popup = $('<div class="style-popup short">' +
153                       '<button data-action="italic"><i>i</i></button>' +
154                       '<button data-action="bold"><b>b</b></button>' +
155                       '<button data-action="code">#</button>' +
156                       '<button data-action="removeFormat">×</button>' +
157                       '<button data-action="createLink">a</button>' +
158                       '<input name="link-target"/>' +
159                       '</div>');
160       style_popup.hide();
161       style_popup.insertAfter($('.actions'));
162       style_popup.find('button').on('click', update_style);
163       style_popup.find('[name=link-target]').on('keypress', validate_link).on('focusout', focusout_link);
164     }
165     style_popup.css('position', 'absolute');
166     var pos = sel.getRangeAt(0).getClientRects()[0];
167     style_popup.css('top', pos.top + window.scrollY - 33);
168     style_popup.css('left', pos.left + window.scrollX);
169     style_popup.show();
170   };
171   $(document).on('selectionchange', function(event) {
172     if ($('input[name=link-target].shown').length) {
173       return;
174     }
175     var sel = window.getSelection();
176     if ($(sel.anchorNode).parents('div[contenteditable]').length && sel.toString()) {
177       show_style_popup(sel);
178     } else if (style_popup) {
179       $(style_popup).hide();
180     }
181   });
182 });