da366f2c941c86e33907dfc30617992906e7130d
1 /***************************************************************
4 * (c) 2007 Tobias Liebig <mail_typo3@etobi.de>
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.
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.
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.
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26 /* t3editor.js uses the Codemirror editor.
30 // collection of all t3editor instances on the current page
31 var t3e_instances
= {};
33 // path to the editor ext dir
34 // can be overwritten in class.tx_t3editor.php
35 var PATH_t3e
= "../../../sysext/t3editor/";
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.
45 function T3editor(textarea
) {
48 // memorize the textarea
49 this.textarea
= $(textarea
);
51 // outer wrap around the whole t3editor
52 this.outerdiv
= new Element("DIV", {
56 // place the div before the textarea
57 this.textarea
.parentNode
.insertBefore(this.outerdiv
, $(this.textarea
));
60 // an overlay that covers the whole editor
61 this.modalOverlay
= new Element("DIV", {
62 "class": "t3e_modalOverlay",
63 "id": "t3e_modalOverlay_wait"
66 this.modalOverlay
.hide();
67 this.modalOverlay
.setStyle(this.outerdiv
.getDimensions());
68 this.modalOverlay
.setStyle({
71 this.outerdiv
.appendChild(this.modalOverlay
);
74 // wrapping the Toolbar
75 this.toolbar_wrap = new Element("DIV", {
76 "class": "t3e_toolbar_wrap"
78 this.outerdiv.appendChild(this.toolbar_wrap);
81 // wrapping the linenumbers
82 this.linenum_wrap
= new Element("DIV", {
83 "class": "t3e_linenum_wrap"
85 // the "linenumber" list itself
86 this.linenum
= new Element("DL", {
87 "class": "t3e_linenum"
89 this.linenum_wrap
.appendChild(this.linenum
);
90 this.outerdiv
.appendChild(this.linenum_wrap
);
92 // wrapping the iframe
93 this.mirror_wrap
= new Element("DIV", {
94 "class": "t3e_iframe_wrap"
96 this.outerdiv
.appendChild(this.mirror_wrap
);
98 // wrapping the statusbar
99 this.statusbar_wrap
= new Element("DIV", {
100 "class": "t3e_statusbar_wrap"
102 this.outerdiv
.appendChild(this.statusbar_wrap
);
104 this.statusbar_title
= new Element("SPAN", {
105 "class": "t3e_statusbar_title"
107 this.statusbar_wrap
.appendChild(this.statusbar_title
);
108 this.statusbar_title
.update( this.textarea
.readAttribute('alt') );
110 this.t3e_statusbar_status
= new Element("SPAN", {
111 "class": "t3e_statusbar_status"
113 this.statusbar_wrap
.appendChild(this.t3e_statusbar_status
);
114 this.t3e_statusbar_status
.update( '' );
116 var textareaDim
= $(this.textarea
).getDimensions();
118 this.linenum_wrap
.setStyle({
119 height
: (textareaDim
.height
) + 'px'
131 content
: $(this.textarea
).value
,
132 parserfile
: ["tokenizetyposcript.js", "parsetyposcript.js"],
133 stylesheet
: PATH_t3e
+ "css/t3editor_inner.css",
134 path
: PATH_t3e
+ "jslib/codemirror/",
136 saveFunction
: this.saveFunction
.bind(this),
137 initCallback
: this.init
.bind(this)
141 this.mirror
= new CodeMirror(this.mirror_wrap
, options
);
145 T3editor
.prototype = {
148 var textareaDim
= $(this.textarea
).getDimensions();
150 this.textarea
.hide();
152 this.resize(textareaDim
.width
, textareaDim
.height
);
155 // indicates is content is modified and not safed yet
158 // check if code in editor has been modified since last saving
159 checkTextModified
: function() {
160 if (!this.textModified
) {
161 this.textModified
= true;
162 this.updateLinenum();
166 // scroll the line numbers
170 if (typeof(this.mirror
.editor
.win
.pageYOffset
) == 'number') {
171 // Netscape compliant
172 scrOfY
= this.mirror
.editor
.win
.pageYOffset
;
173 scrOfX
= this.mirror
.editor
.win
.pageXOffset
;
174 } else if (this.mirror
.editor
.doc
.body
&& (this.mirror
.editor
.doc
.body
.scrollLeft
|| this.mirror
.editor
.doc
.body
.scrollTop
)) {
176 scrOfY
= this.mirror
.editor
.doc
.body
.scrollTop
;
177 scrOfX
= this.mirror
.editor
.doc
.body
.scrollLeft
;
178 } else if (this.mirror
.editor
.doc
.documentElement
179 && (this.mirror
.editor
.doc
.documentElement
.scrollLeft
180 || this.mirror
.editor
.doc
.documentElement
.scrollTop
)) {
181 // IE6 standards compliant mode
182 scrOfY
= this.mirror
.editor
.doc
.documentElement
.scrollTop
;
183 scrOfX
= this.mirror
.editor
.doc
.documentElement
.scrollLeft
;
185 this.linenum_wrap
.scrollTop
= scrOfY
;
189 // update the line numbers
190 updateLinenum
: function(code
) {
193 code
= this.mirror
.editor
.container
.innerHTML
;
194 theMatch
= code
.match(/<br/gi);
196 theMatch
= code
.match(/\n/gi);
201 } else if (Prototype
.Browser
.IE
) {
205 var bodyContentLineCount
= theMatch
.length
;
206 disLineCount
= this.linenum
.childNodes
.length
;
207 while (disLineCount
!= bodyContentLineCount
) {
208 if (disLineCount
> bodyContentLineCount
) {
209 this.linenum
.removeChild(this.linenum
.lastChild
);
211 } else if (disLineCount
< bodyContentLineCount
) {
212 ln
= $(document
.createElement('dt'));
213 ln
.update(disLineCount
+ 1 + '.');
214 ln
.addClassName(disLineCount
% 2 == 1 ? 'even': 'odd');
215 ln
.setAttribute('id', 'ln' + (disLineCount
+ 1));
216 this.linenum
.appendChild(ln
);
221 this.t3e_statusbar_status
.update(
222 (this.textModified
? ' <span alt="document has been modified">*</span> ': '') + bodyContentLineCount
+ ' lines');
225 saveFunction
: function() {
226 this.modalOverlay
.show();
227 this.textarea
.value
= this.mirror
.editor
.getCode();
228 $('submitAjax').value
= '1';
229 Form
.request($(this.textarea
.form
), {
230 onComplete
: this.saveFunctionComplete
.bind(this)
234 // callback if ajax saving was successful
235 saveFunctionComplete
: function(ajaxrequest
) {
237 if (ajaxrequest
.status
== 200
238 && ajaxrequest
.headerJSON
.result
== true) {
240 this.textModified
= false;
241 this.updateLinenum();
243 alert("An error occured while saving the data.");
245 $('submitAjax').value
= '0';
246 this.modalOverlay
.hide();
250 // find matching bracket
251 checkBracketAtCursor
: function() {
252 var cursor
= this.mirror
.editor
.win
.select
.markSelection(this.mirror
.editor
.win
);
254 if (!cursor
|| !cursor
.start
) return;
256 this.cursorObj
= cursor
.start
;
258 // remove current highlights
259 Selector
.findChildElements(this.mirror
.editor
.doc
,
260 $A(['.highlight-bracket', '.error-bracket'])
261 ).each(function(item
) {
262 item
.className
= item
.className
.replace(' highlight-bracket', '');
263 item
.className
= item
.className
.replace(' error-bracket', '');
266 if (!cursor
.start
|| !cursor
.start
.node
|| !cursor
.start
.node
.parentNode
|| !cursor
.start
.node
.parentNode
.className
) {
270 // if cursor is behind an bracket, we search for the matching one
272 // we have an opening bracket, search forward for a closing bracket
273 if (cursor
.start
.node
.parentNode
.className
.indexOf('curly-bracket-open') != -1) {
274 var maybeMatch
= cursor
.start
.node
.parentNode
.nextSibling
;
277 if (maybeMatch
.className
.indexOf('curly-bracket-open') != -1) {
280 if (maybeMatch
.className
.indexOf('curly-bracket-close') != -1) {
284 maybeMatch
.className
+= ' highlight-bracket';
285 cursor
.start
.node
.parentNode
.className
+= ' highlight-bracket';
289 maybeMatch
= maybeMatch
.nextSibling
;
293 // we have a closing bracket, search backward for an opening bracket
294 if (cursor
.start
.node
.parentNode
.className
.indexOf('curly-bracket-close') != -1) {
295 var maybeMatch
= cursor
.start
.node
.parentNode
.previousSibling
;
298 if (maybeMatch
.className
.indexOf('curly-bracket-close') != -1) {
301 if (maybeMatch
.className
.indexOf('curly-bracket-open') != -1) {
305 maybeMatch
.className
+= ' highlight-bracket';
306 cursor
.start
.node
.parentNode
.className
+= ' highlight-bracket';
310 maybeMatch
= maybeMatch
.previousSibling
;
314 if (cursor
.start
.node
.parentNode
.className
.indexOf('curly-bracket-') != -1
315 && maybeMatch
== null) {
316 cursor
.start
.node
.parentNode
.className
+= ' error-bracket';
320 // close an opend bracket
321 autoCloseBracket
: function(prevNode
) {
322 if (prevNode
&& prevNode
.className
.indexOf('curly-bracket-open') != -1) {
323 this.mirror
.editor
.win
.select
.insertNewlineAtCursor(this.mirror
.editor
.win
);
324 this.mirror
.editor
.win
.select
.insertTextAtCursor(this.mirror
.editor
.win
, "}");
328 // click event. Refresh cursor object.
330 this.refreshCursorObj();
331 this.checkBracketAtCursor();
335 refreshCursorObj
: function() {
336 var cursor
= this.mirror
.editor
.win
.select
.markSelection(this.mirror
.editor
.win
);
337 this.cursorObj
= cursor
.start
;
340 // toggle between the textarea and t3editor
341 toggleView
: function(checkboxEnabled
) {
342 if (checkboxEnabled
) {
343 this.textarea
.value
= this.mirror
.editor
.getCode();
344 this.outerdiv
.hide();
345 this.textarea
.show();
346 /* this.saveButtons.each(function(button) {
347 Event.stopObserving(button, 'click', this.saveAjaxEvent);
351 this.mirror
.editor
.importCode(this.textarea
.value
);
352 this.textarea
.hide();
353 this.outerdiv
.show();
354 /* this.saveButtons.each(function(button) {
355 Event.observe(button, 'click', this.saveAjaxEvent);
362 resize
: function(width
, height
) {
364 newheight
= (height
- 1);
365 newwidth
= (width
+ 11);
366 if (Prototype
.Browser
.IE
) newwidth
= newwidth
+ 8;
368 $(this.outerdiv
).setStyle({
369 height
: newheight
+ 'px',
370 width
: newwidth
+ 'px'
373 this.linenum_wrap
.setStyle({
374 height
: (height
- 22) + 'px' // less footer height
377 numwwidth
= this.linenum_wrap
.getWidth();
379 if (Prototype
.Browser
.IE
) numwwidth
= numwwidth
- 17;
380 if (!Prototype
.Browser
.IE
) numwwidth
= numwwidth
- 11;
382 $(this.mirror_wrap
.firstChild
).setStyle({
383 'height': ((height
- 22) + 'px'),
384 'width': ((width
- numwwidth
) + 'px')
387 $(this.modalOverlay
).setStyle(this.outerdiv
.getDimensions());
393 // toggle between normal view and fullscreen mode
394 toggleFullscreen
: function() {
395 if (this.outerdiv
.hasClassName('t3e_fullscreen')) {
396 // turn fullscreen off
398 // unhide the scrollbar of the body
399 this.outerdiv
.offsetParent
.setStyle({
403 this.outerdiv
.removeClassName('t3e_fullscreen');
404 h
= this.textarea
.getDimensions().height
;
405 w
= this.textarea
.getDimensions().width
;
408 // turn fullscreen on
409 this.outerdiv
.addClassName('t3e_fullscreen');
410 h
= this.outerdiv
.offsetParent
.getHeight();
411 w
= this.outerdiv
.offsetParent
.getWidth();
413 // less scrollbar width
416 // hide the scrollbar of the body
417 this.outerdiv
.offsetParent
.setStyle({
420 this.outerdiv
.offsetParent
.scrollTop
= 0;
425 } // T3editor.prototype
428 // fix prototype issue: ajax request do not respect charset of the page and screw up code
429 if (document
.characterSet
!= "UTF-8") {
430 encodeURIComponent
= escape
;
434 // ------------------------------------------------------------------------
438 * toggle between enhanced editor (t3editor) and simple textarea
440 function t3editor_toggleEditor(checkbox
, index
) {
441 if (!Prototype
.Browser
.MobileSafari
442 && !Prototype
.Browser
.IE
443 && !Prototype
.Browser
.WebKit
) {
445 if (index
== undefined
) {
446 $$('textarea.t3editor').each(
447 function(textarea
, i
) {
448 t3editor_toggleEditor(checkbox
, i
);
451 if (t3e_instances
[index
] != undefined
) {
452 var t3e
= t3e_instances
[index
];
453 t3e
.toggleView(checkbox
.checked
);
454 } else if (!checkbox
.checked
) {
455 var t3e
= new T3editor($$('textarea.t3editor')[index
], index
);
456 t3e_instances
[index
] = t3e
;
462 // ------------------------------------------------------------------------
465 if (!Prototype
.Browser
.MobileSafari
466 // && !Prototype.Browser.IE
467 && !Prototype
.Browser
.WebKit
) {
469 // everything ready: turn textarea's into fancy editors
470 Event
.observe(window
, 'load',
472 $$('textarea.t3editor').each(
473 function(textarea
, i
) {
474 if ($('t3editor_disableEditor_' + (i
+ 1) + '_checkbox')
475 && !$('t3editor_disableEditor_' + (i
+ 1) + '_checkbox').checked
) {
476 var t3e
= new T3editor(textarea
);
477 t3e_instances
[i
] = t3e
;