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