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