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