]> git.ipfire.org Git - ipfire.org.git/blame - src/static/js/editor.js
wiki: editor: Indent everything using tabs
[ipfire.org.git] / src / static / js / editor.js
CommitLineData
feeace6e 1class Editor {
7b5802bb
MT
2 constructor(parent) {
3 this.parent = $(parent);
4 this.data = this.parent.data();
7b1f068c 5
7b5802bb 6 // Hide the preview here
7b1f068c
MT
7 this.preview = $("#preview");
8 this.preview.hide();
feeace6e 9
7b5802bb
MT
10 // Get the textarea
11 this.textarea = this.parent.find("textarea");
feeace6e 12
7b5802bb
MT
13 // Initialise selection
14 this.selection = {
15 start : 0,
16 end : 0,
17 text : "",
18 length : 0,
19 };
8eff1fbe 20
7b5802bb
MT
21 // Make the textarea magic
22 this.activateTextArea();
feeace6e 23
7b5802bb
MT
24 // Bind all keys
25 this.bindKeys();
feeace6e 26
7b5802bb
MT
27 // Change timer for preview
28 var update = null;
7b1f068c 29
7b5802bb 30 console.log("Editor initialised for " + this.parent);
717811a7 31
7b5802bb
MT
32 // Set focus on the textarea
33 this.textarea.focus();
34 }
feeace6e 35
7b5802bb
MT
36 activateTextArea() {
37 var editor = this;
feeace6e 38
7b5802bb
MT
39 // Render preview when content has changed
40 this.textarea.on("keyup change", function(e) {
41 if (editor.update)
42 clearTimeout(editor.update);
7b1f068c 43
7b5802bb 44 var content = editor.textarea.val();
7b1f068c
MT
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
7b5802bb 55 // Render content and show it
7b1f068c
MT
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);
7b5802bb
MT
68 });
69
70 // Remember any selected text
71 this.textarea.on("select keyup click", function(e) {
72 // Ignore any keyboard shortcuts
73 if (e.ctrlKey)
74 return;
75
76 // Save selected text
77 editor.selection = {
78 start : this.selectionStart,
79 end : this.selectionEnd,
80 text : this.value.slice(this.selectionStart, this.selectionEnd),
81 length: this.selectionEnd - this.selectionStart,
82 };
83
84 console.debug("Something got selected:");
85 console.debug(editor.selection);
86 })
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
105 // C - Code
106 case 67:
107 editor.code();
108 break;
109
110 // H - Headline
111 case 72:
112 editor.headline();
113 break;
114
115 // L - Link
116 case 76:
117 editor.link();
118 break;
119 }
120 });
121 }
122
123 bindKeys() {
124 // Typography
125 this.parent.find("#bold").click(this.bold.bind(this));
126 this.parent.find("#italic").click(this.italic.bind(this));
127 this.parent.find("#code").click(this.code.bind(this));
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));
133
134 // Links
135 this.parent.find("#link").click(this.link.bind(this));
136 }
137
138 // Functions to modify the text
139
140 replaceSelection(replacement) {
141 // Get the DOM element
142 var textarea = this.textarea.get(0);
143
144 // Write text to textarea and move the cursor to the end
145 textarea.setRangeText(replacement,
146 this.selection.start, this.selection.end, "end");
147 }
148
149 insertAtCursor(insertion) {
150 this.replaceSelection(insertion);
151 }
152
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 }
162
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
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
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() {
7b5802bb
MT
241 // Initialise all editors
242 $(".editor").each(function(i, e) {
243 new Editor(e);
244 });
feeace6e 245});