]> git.ipfire.org Git - ipfire.org.git/blame - src/static/js/editor.js
wiki: Move preview code into Editor class
[ipfire.org.git] / src / static / js / editor.js
CommitLineData
feeace6e
MT
1class Editor {
2 constructor(parent) {
3 this.parent = $(parent);
7b1f068c
MT
4 this.data = this.parent.data();
5
6 // Hide the preview here
7 this.preview = $("#preview");
8 this.preview.hide();
feeace6e
MT
9
10 // Get the textarea
11 this.textarea = this.parent.find("textarea");
12
8eff1fbe
MT
13 // Initialise selection
14 this.selection = {
15 start : 0,
16 end : 0,
17 text : "",
18 length : 0,
19 };
20
feeace6e
MT
21 // Make the textarea magic
22 this.activateTextArea();
23
24 // Bind all keys
25 this.bindKeys();
26
7b1f068c
MT
27 // Change timer for preview
28 var update = null;
29
feeace6e 30 console.log("Editor initialised for " + this.parent);
717811a7
MT
31
32 // Set focus on the textarea
33 this.textarea.focus();
feeace6e
MT
34 }
35
36 activateTextArea() {
37 var editor = this;
38
7b1f068c
MT
39 // Render preview when content has changed
40 this.textarea.on("keyup change", function(e) {
41 if (editor.update)
42 clearTimeout(editor.update);
43
44 var content = editor.textarea.val();
45
46 // If the field is all empty, we will hide it
47 if (content)
48 editor.preview.show();
49 else
50 editor.preview.hide();
51
52 // Go into update mode
53 editor.preview.addClass("updating");
54
55 // Render content and show it
56 editor.update = setTimeout(function() {
57 var c = $("#preview-content");
58
59 $.post(editor.data.render_url, { content : content },
60 function(data) {
61 c.html(data);
62
63 // Update finished
64 editor.preview.removeClass("updating");
65 }
66 );
67 }, 750);
68 });
69
feeace6e 70 // Remember any selected text
18c36357 71 this.textarea.on("select keyup click", function(e) {
eea47e20
MT
72 // Ignore any keyboard shortcuts
73 if (e.ctrlKey)
74 return;
75
76 // Save selected text
feeace6e
MT
77 editor.selection = {
78 start : this.selectionStart,
79 end : this.selectionEnd,
18c36357
MT
80 text : this.value.slice(this.selectionStart, this.selectionEnd),
81 length: this.selectionEnd - this.selectionStart,
feeace6e
MT
82 };
83
84 console.debug("Something got selected:");
85 console.debug(editor.selection);
86 })
eea47e20
MT
87
88 // Bind keyboard shortcuts
89 this.textarea.on("keyup", function(e) {
90 // If Ctrl wasn't pressed this isn't for us
91 if (!e.ctrlKey)
92 return;
93
94 switch (e.which) {
95 // B - Bold
96 case 66:
97 editor.bold();
98 break;
99
100 // I - Italic
101 case 73:
102 editor.italic();
103 break;
104
8dea0e28
MT
105 // C - Code
106 case 67:
107 editor.code();
108 break;
109
eea47e20
MT
110 // H - Headline
111 case 72:
112 editor.headline();
113 break;
7b7e6981
MT
114
115 // L - Link
116 case 76:
117 editor.link();
118 break;
eea47e20
MT
119 }
120 });
feeace6e
MT
121 }
122
123 bindKeys() {
18c36357 124 // Typography
feeace6e
MT
125 this.parent.find("#bold").click(this.bold.bind(this));
126 this.parent.find("#italic").click(this.italic.bind(this));
8dea0e28 127 this.parent.find("#code").click(this.code.bind(this));
18c36357
MT
128
129 // Headlines
130 this.parent.find("#headline").click(this.headline.bind(this));
131 this.parent.find("#headline-down").click(this.headline_down.bind(this));
132 this.parent.find("#headline-up").click(this.headline_up.bind(this));
7b7e6981
MT
133
134 // Links
135 this.parent.find("#link").click(this.link.bind(this));
feeace6e
MT
136 }
137
138 // Functions to modify the text
139
140 replaceSelection(replacement) {
6eb53879
MT
141 // Get the DOM element
142 var textarea = this.textarea.get(0);
feeace6e 143
6eb53879
MT
144 // Write text to textarea and move the cursor to the end
145 textarea.setRangeText(replacement,
146 this.selection.start, this.selection.end, "end");
feeace6e
MT
147 }
148
18c36357
MT
149 insertAtCursor(insertion) {
150 this.replaceSelection(insertion);
151 }
152
feeace6e
MT
153 bold() {
154 console.debug("Converting into bold: " + this.selection.text);
155 this.replaceSelection("**" + this.selection.text + "**");
156 }
157
158 italic() {
159 console.debug("Converting into italic: " + this.selection.text);
160 this.replaceSelection("*" + this.selection.text + "*");
161 }
18c36357 162
8dea0e28
MT
163 code() {
164 var multiline = this.selection.text.indexOf("\n");
165
166 if (multiline >= 0) {
167 this.replaceSelection("```\n" + this.selection.text + "\n```\n\n");
168 } else {
169 this.replaceSelection("`" + this.selection.text + "`");
170 }
171 }
172
7b7e6981
MT
173 link() {
174 // Handle URLs
175 if (this.selection.text.startsWith("https://") || this.selection.text.startsWith("http://")) {
176 this.replaceSelection("[" + this.selection.text + "](" + this.selection.text + ")");
177 // Handle selected text
178 } else {
179 this.replaceSelection("[" + this.selection.text + "]()")
180 }
181 }
182
18c36357
MT
183 // Headlines
184
185 findLevel() {
186 // Get all text between start and cursor position
187 var text = this.textarea.val().slice(0, this.selection.start);
188
189 // Split it in lines and reverse
190 var lines = text.split("\n");
191 lines.reverse();
192
193 for (var line of lines) {
194 console.debug(line);
195
196 // Return the number of # found in the nearest headline
197 var match = line.match("^(#+)");
198 if (match) {
199 return match[1].length;
200 }
201 }
202
203 // If nothing was found, we are on level one
204 return 1;
205 }
206
207 insertHeadline(offset) {
208 // Find level of headlines
209 var level = Math.max(this.findLevel() + offset, 1);
210
211 console.debug("Adding headline (" + level + ")");
212 var headline = "#".repeat(level);
213
214 if (this.selection.length == 0) {
215 headline += " ...";
216 } else {
217 // Add some space if we don't have any, yet
218 if (!this.selection.text.startsWith(" "))
219 headline += " ";
220
221 headline += this.selection.text + "\n\n";
222 }
223
224 this.replaceSelection(headline);
225 }
226
227 headline() {
228 return this.insertHeadline(0);
229 }
230
231 headline_down() {
232 return this.insertHeadline(1);
233 }
234
235 headline_up() {
236 return this.insertHeadline(-1);
237 }
feeace6e
MT
238}
239
240$(document).ready(function() {
241 // Initialise all editors
242 $(".editor").each(function(i, e) {
243 new Editor(e);
244 });
245});