8bdeb6da3b68cdab54ddd4738801029a2e13f97b
[Packages/TYPO3.CMS.git] / typo3 / sysext / t3editor / jslib / t3editor.js
1 /***************************************************************
2 * Copyright notice
3 *
4 * (c) 2007-2009 Tobias Liebig <mail_typo3@etobi.de>
5 * All rights reserved
6 *
7 * This script is part of the TYPO3 project. The TYPO3 project is
8 * free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * The GNU General Public License can be found at
14 * http://www.gnu.org/copyleft/gpl.html.
15 * A copy is found in the textfile GPL.txt and important notices to the license
16 * from the author is found in LICENSE.txt distributed with these scripts.
17 *
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26 /* t3editor.js uses the Codemirror editor.
27 */
28
29
30 // collection of all t3editor instances on the current page
31 var t3e_instances = {};
32
33 // path to the editor ext dir
34 // can be overwritten in class.tx_t3editor.php
35 var PATH_t3e = "../../../sysext/t3editor/";
36
37
38
39
40 /* Demonstration of embedding CodeMirror in a bigger application. The
41 * interface defined here is a mess of prompts and confirms, and
42 * should probably not be used in a real project.
43 */
44
45 function T3editor(textarea) {
46 var self = this;
47
48 // memorize the textarea
49 this.textarea = $(textarea);
50
51 // outer wrap around the whole t3editor
52 this.outerdiv = new Element("DIV", {
53 "class": "t3e_wrap"
54 });
55
56 // place the div before the textarea
57 this.textarea.parentNode.insertBefore(this.outerdiv, $(this.textarea));
58
59
60 // an overlay that covers the whole editor
61 this.modalOverlay = new Element("DIV", {
62 "class": "t3e_modalOverlay",
63 "id": "t3e_modalOverlay_wait"
64 });
65
66 this.modalOverlay.hide();
67 this.modalOverlay.setStyle(this.outerdiv.getDimensions());
68 this.modalOverlay.setStyle({
69 opacity: 0.8
70 });
71 this.outerdiv.appendChild(this.modalOverlay);
72
73 // wrapping the linenumbers
74 this.linenum_wrap = new Element("DIV", {
75 "class": "t3e_linenum_wrap"
76 });
77 // the "linenumber" list itself
78 this.linenum = new Element("DL", {
79 "class": "t3e_linenum"
80 });
81 this.linenum_wrap.appendChild(this.linenum);
82 this.outerdiv.appendChild(this.linenum_wrap);
83
84 // wrapping the iframe
85 this.mirror_wrap = new Element("DIV", {
86 "class": "t3e_iframe_wrap"
87 });
88 this.outerdiv.appendChild(this.mirror_wrap);
89
90 // wrapping the statusbar
91 this.statusbar_wrap = new Element("DIV", {
92 "class": "t3e_statusbar_wrap"
93 });
94 this.outerdiv.appendChild(this.statusbar_wrap);
95
96 this.statusbar_title = new Element("SPAN", {
97 "class": "t3e_statusbar_title"
98 });
99 this.statusbar_wrap.appendChild(this.statusbar_title);
100 this.statusbar_title.update( this.textarea.readAttribute('alt') );
101
102 this.t3e_statusbar_status = new Element("SPAN", {
103 "class": "t3e_statusbar_status"
104 });
105 this.statusbar_wrap.appendChild(this.t3e_statusbar_status);
106 this.t3e_statusbar_status.update( '' );
107
108 var textareaDim = $(this.textarea).getDimensions();
109
110 this.linenum_wrap.setStyle({
111 height: (textareaDim.height) + 'px'
112 });
113
114 // setting options
115 var options = {
116 height: (
117 textareaDim.height
118 ) + 'px',
119 width: (
120 textareaDim.width
121 - 40 // line numbers
122 ) + 'px',
123 content: $(this.textarea).value,
124 parserfile: ["tokenizetyposcript.js", "parsetyposcript.js"],
125 stylesheet: PATH_t3e + "css/t3editor_inner.css",
126 path: PATH_t3e + "jslib/codemirror/",
127 outerEditor: this,
128 saveFunction: this.saveFunction.bind(this),
129 initCallback: this.init.bind(this),
130 autoMatchParens: true
131 };
132
133 // get the editor
134 this.mirror = new CodeMirror(this.mirror_wrap, options);
135 this.tsCodeCompletion = new TsCodeCompletion(this.mirror,this.outerdiv);
136 }
137
138 T3editor.prototype = {
139 saveFunctionEvent: null,
140 saveButtons: null,
141 updateTextareaEvent: null,
142
143 init: function() {
144 var textareaDim = $(this.textarea).getDimensions();
145 // hide the textarea
146 this.textarea.hide();
147
148 // get the form object (needed for Ajax saving)
149 var form = $(this.textarea.form);
150 this.saveButtons = form.getInputs('image', 'submit');
151
152 // initialize ajax saving events
153 this.saveFunctionEvent = this.saveFunction.bind(this);
154 this.saveButtons.each(function(button) {
155 Event.observe(button,'click',this.saveFunctionEvent);
156 }.bind(this));
157
158 this.updateTextareaEvent = this.updateTextarea.bind(this);
159 Event.observe($(this.textarea.form), 'submit', this.updateTextareaEvent);
160
161 Event.observe(this.mirror.win.document, 'keyup', this.tsCodeCompletion.keyUp);
162 Event.observe(this.mirror.win.document, 'keydown', this.tsCodeCompletion.keyDown);
163 Event.observe(this.mirror.win.document, 'click', this.tsCodeCompletion.click);
164 this.resize(textareaDim.width, textareaDim.height );
165
166 this.updateLinenum();
167 },
168
169 // indicates is content is modified and not safed yet
170 textModified: false,
171
172 // check if code in editor has been modified since last saving
173 checkTextModified: function() {
174 if (!this.textModified) {
175 this.textModified = true;
176 this.updateLinenum();
177 }
178 },
179
180 // scroll the line numbers
181 scroll: function() {
182 var scrOfX = 0,
183 scrOfY = 0;
184 if (typeof(this.mirror.editor.win.pageYOffset) == 'number') {
185 // Netscape compliant
186 scrOfY = this.mirror.editor.win.pageYOffset;
187 scrOfX = this.mirror.editor.win.pageXOffset;
188 } else if (this.mirror.editor.doc.body && (this.mirror.editor.doc.body.scrollLeft || this.mirror.editor.doc.body.scrollTop)) {
189 // DOM compliant
190 scrOfY = this.mirror.editor.doc.body.scrollTop;
191 scrOfX = this.mirror.editor.doc.body.scrollLeft;
192 } else if (this.mirror.editor.doc.documentElement
193 && (this.mirror.editor.doc.documentElement.scrollLeft
194 || this.mirror.editor.doc.documentElement.scrollTop)) {
195 // IE6 standards compliant mode
196 scrOfY = this.mirror.editor.doc.documentElement.scrollTop;
197 scrOfX = this.mirror.editor.doc.documentElement.scrollLeft;
198 }
199 this.linenum_wrap.scrollTop = scrOfY;
200 },
201
202
203 // update the line numbers
204 updateLinenum: function() {
205 // escape if editor is not yet loaded
206 if (!this.mirror.editor) return;
207
208 var bodyContentLineCount = this.mirror.lineNumber(this.mirror.lastLine());
209 disLineCount = this.linenum.childNodes.length;
210 while (disLineCount != bodyContentLineCount) {
211 if (disLineCount > bodyContentLineCount) {
212 this.linenum.removeChild(this.linenum.lastChild);
213 disLineCount--;
214 } else if (disLineCount < bodyContentLineCount) {
215 ln = $(document.createElement('dt'));
216 ln.update(disLineCount + 1 + '.');
217 ln.addClassName(disLineCount % 2 == 1 ? 'even': 'odd');
218 ln.setAttribute('id', 'ln' + (disLineCount + 1));
219 this.linenum.appendChild(ln);
220 disLineCount++;
221 }
222 }
223
224 this.t3e_statusbar_status.update(
225 (this.textModified ? ' <span alt="document has been modified">*</span> ': '')
226 + bodyContentLineCount
227 + ' lines');
228 },
229
230 updateTextarea: function(event) {
231 this.textarea.value = this.mirror.getCode();
232 },
233
234 saveFunction: function(event) {
235 this.modalOverlay.show();
236 this.updateTextarea(event);
237
238 if (event) {
239 Event.stop(event);
240 }
241 params = $(this.textarea.form).serialize(true);
242 params = Object.extend( { ajaxID: 'tx_t3editor::saveCode' }, params);
243
244 new Ajax.Request(
245 URL_typo3 + 'ajax.php', {
246 parameters: params,
247 onComplete: this.saveFunctionComplete.bind(this)
248 }
249 );
250
251 },
252
253 // callback if ajax saving was successful
254 saveFunctionComplete: function(ajaxrequest) {
255 if (ajaxrequest.status == 200
256 && ajaxrequest.headerJSON.result == true) {
257
258 this.textModified = false;
259 this.updateLinenum();
260 } else {
261 alert("An error occured while saving the data.");
262 };
263 this.modalOverlay.hide();
264 },
265
266 // toggle between the textarea and t3editor
267 toggleView: function(checkboxEnabled) {
268 if (checkboxEnabled) {
269 this.textarea.value = this.mirror.editor.getCode();
270 this.outerdiv.hide();
271 this.textarea.show();
272 this.saveButtons.each(function(button) {
273 Event.stopObserving(button,'click',this.saveFunctionEvent);
274 }.bind(this));
275 Event.stopObserving($(this.textarea.form), 'submit', this.updateTextareaEvent);
276
277 } else {
278 this.mirror.editor.importCode(this.textarea.value);
279 this.textarea.hide();
280 this.outerdiv.show();
281 this.saveButtons.each(function(button) {
282 this.saveFunctionEvent = this.saveFunction.bind(this);
283 Event.observe(button,'click',this.saveFunctionEvent);
284 }.bind(this));
285 Event.observe($(this.textarea.form), 'submit', this.updateTextareaEvent);
286 }
287 },
288
289
290 resize: function(width, height) {
291 if (this.outerdiv) {
292 newheight = (height - 1);
293 newwidth = (width + 11);
294 if (Prototype.Browser.IE) newwidth = newwidth + 8;
295
296 $(this.outerdiv).setStyle({
297 height: newheight + 'px',
298 width: newwidth + 'px'
299 });
300
301 this.linenum_wrap.setStyle({
302 height: (height - 22) + 'px' // less footer height
303 });
304
305 numwwidth = this.linenum_wrap.getWidth();
306
307 if (Prototype.Browser.IE) numwwidth = numwwidth - 17;
308 if (!Prototype.Browser.IE) numwwidth = numwwidth - 11;
309
310 $(this.mirror_wrap.firstChild).setStyle({
311 'height': ((height - 22) + 'px'),
312 'width': ((width - numwwidth) + 'px')
313 });
314
315 $(this.modalOverlay).setStyle(this.outerdiv.getDimensions());
316
317 }
318
319 },
320
321 // toggle between normal view and fullscreen mode
322 toggleFullscreen: function() {
323 if (this.outerdiv.hasClassName('t3e_fullscreen')) {
324 // turn fullscreen off
325
326 // unhide the scrollbar of the body
327 this.outerdiv.offsetParent.setStyle({
328 overflow: ''
329 });
330
331 this.outerdiv.removeClassName('t3e_fullscreen');
332 h = this.textarea.getDimensions().height;
333 w = this.textarea.getDimensions().width;
334
335 } else {
336 // turn fullscreen on
337 this.outerdiv.addClassName('t3e_fullscreen');
338 h = this.outerdiv.offsetParent.getHeight();
339 w = this.outerdiv.offsetParent.getWidth();
340
341 // less scrollbar width
342 w = w - 13;
343
344 // hide the scrollbar of the body
345 this.outerdiv.offsetParent.setStyle({
346 overflow: 'hidden'
347 });
348 this.outerdiv.offsetParent.scrollTop = 0;
349 }
350 this.resize(w, h);
351 }
352
353 } // T3editor.prototype
354
355
356 // ------------------------------------------------------------------------
357
358
359 /**
360 * toggle between enhanced editor (t3editor) and simple textarea
361 */
362 function t3editor_toggleEditor(checkbox, index) {
363 if (!Prototype.Browser.MobileSafari
364 && !Prototype.Browser.WebKit) {
365
366 if (index == undefined) {
367 $$('textarea.t3editor').each(
368 function(textarea, i) {
369 t3editor_toggleEditor(checkbox, i);
370 }
371 );
372 } else {
373 if (t3e_instances[index] != undefined) {
374 var t3e = t3e_instances[index];
375 t3e.toggleView(checkbox.checked);
376 } else if (!checkbox.checked) {
377 var t3e = new T3editor($$('textarea.t3editor')[index], index);
378 t3e_instances[index] = t3e;
379 }
380 }
381 }
382 }
383
384 // ------------------------------------------------------------------------
385
386
387 if (!Prototype.Browser.MobileSafari) {
388
389 // everything ready: turn textarea's into fancy editors
390 Event.observe(window, 'load',
391 function() {
392 $$('textarea.t3editor').each(
393 function(textarea, i) {
394 if ($('t3editor_disableEditor_' + (i + 1) + '_checkbox')
395 && !$('t3editor_disableEditor_' + (i + 1) + '_checkbox').checked) {
396 var t3e = new T3editor(textarea);
397 t3e_instances[i] = t3e;
398 }
399 }
400 );
401 }
402 );
403 }
404