ce56cd7fde45a5554e951f4f1c1ca87318e5121b
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Resources / Public / JavaScript / jsfunc.tbe_editor.js
1 /*
2 * This file is part of the TYPO3 CMS project.
3 *
4 * It is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License, either version 2
6 * of the License, or any later version.
7 *
8 * For the full copyright and license information, please read the
9 * LICENSE.txt file that was distributed with this source code.
10 *
11 * The TYPO3 project - inspiring people to share!
12 */
13
14 /**
15 * Contains JavaScript for TYPO3 Core Form generator - AKA "TCEforms"
16 *
17 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
18 * @coauthor Oliver Hader <oh@inpublica.de>
19 */
20
21 var TBE_EDITOR = {
22 /* Example:
23 elements: {
24 'data-parentPid-table-uid': {
25 'field': {
26 'range': [0, 100],
27 'rangeImg': '',
28 'required': true,
29 'requiredImg': ''
30 }
31 }
32 },
33 */
34
35 elements: {},
36 nested: {'field':{}, 'level':{}},
37 ignoreElements: [],
38 actionChecks: { submit: [] },
39
40 formname: '',
41 formnameUENC: '',
42 isChanged: 0,
43
44 backPath: '',
45
46 isPalettedoc: null,
47 doSaveFieldName: 0,
48
49 labels: {},
50 images: {
51 req: new Image(),
52 cm: new Image(),
53 sel: new Image(),
54 clear: new Image()
55 },
56
57 clearBeforeSettingFormValueFromBrowseWin: [],
58
59 // Handling of data structures:
60 removeElement: function(record) {
61 if (TBE_EDITOR.elements && TBE_EDITOR.elements[record]) {
62 delete(TBE_EDITOR.elements[record]);
63 }
64 },
65 removeElementArray: function(removeStack) {
66 if (removeStack && removeStack.length) {
67 TBE_EDITOR.ignoreElements = removeStack;
68 for (var i=removeStack.length; i>=0; i--) {
69 TBE_EDITOR.removeElement(removeStack[i]);
70 }
71 TBE_EDITOR.ignoreElements = [];
72 }
73 },
74 getElement: function(record, field, type) {
75 var result = null;
76 var element;
77
78 if (TBE_EDITOR.elements && TBE_EDITOR.elements[record] && TBE_EDITOR.elements[record][field]) {
79 element = TBE_EDITOR.elements[record][field];
80 if (type) {
81 if (element[type]) result = element;
82 } else {
83 result = element;
84 }
85 }
86
87 return result;
88 },
89 checkElements: function(type, recentUpdated, record, field) {
90 return (document.getElementsByClassName('has-error').length == 0);
91 },
92 // Check all the input fields on a given level of nesting - if only on is unfilled, the whole level is marked as required:
93 checkNested: function(nestedLevelIdent) {
94 var nestedLevel, isClean;
95 if (nestedLevelIdent && TBE_EDITOR.nested.level && TBE_EDITOR.nested.level[nestedLevelIdent]) {
96 nestedLevel = TBE_EDITOR.nested.level[nestedLevelIdent];
97 if (!nestedLevel.clean) {
98 if (typeof nestedLevel.item == 'object') {
99 $H(nestedLevel.item).each(
100 function(pair) {
101 if (isClean || typeof isClean == 'undefined') {
102 isClean = (
103 TBE_EDITOR.checkElements('required', false, pair.value[0], pair.value[1]) &&
104 TBE_EDITOR.checkElements('range', false, pair.value[0], pair.value[1])
105 );
106 }
107 }
108 );
109 if (typeof isClean != 'undefined' && !isClean) {
110 return false;
111 }
112 }
113 if (typeof nestedLevel.sub == 'object') {
114 $H(nestedLevel.sub).each(
115 function(pair) {
116 if (isClean || typeof isClean == 'undefined') {
117 isClean = TBE_EDITOR.checkNested(pair.key);
118 }
119 }
120 );
121 if (typeof isClean != 'undefined' && !isClean) {
122 return false;
123 }
124 }
125 // Store the result, that this level (the fields on this and the sub levels) are clean:
126 nestedLevel.clean = true;
127 }
128 }
129 return true;
130 },
131 addActionChecks: function(type, checks) {
132 TBE_EDITOR.actionChecks[type].push(checks);
133 },
134
135 fieldChanged_fName: function(fName,el) {
136 var idx=2;
137 var table = TBE_EDITOR.split(fName, "[", idx);
138 var uid = TBE_EDITOR.split(fName, "[", idx+1);
139 var field = TBE_EDITOR.split(fName, "[", idx+2);
140
141 table = table.substr(0,table.length-1);
142 uid = uid.substr(0,uid.length-1);
143 field = field.substr(0,field.length-1);
144 TBE_EDITOR.fieldChanged(table,uid,field,el);
145 },
146 fieldChanged: function(table,uid,field,el) {
147 var theField = 'data['+table+']['+uid+']['+field+']';
148 var theRecord = 'data['+table+']['+uid+']';
149 TBE_EDITOR.isChanged = 1;
150
151 // modify the "field has changed" info by adding a class to the container element (based on palette or main field)
152 var $formField = TYPO3.jQuery('[name="' + el + '"]');
153 var $paletteField = $formField.closest('.t3js-formengine-palette-field');
154 $paletteField.addClass('has-change');
155
156 // Set required flag:
157 var imgReqObjName = "req_"+table+"_"+uid+"_"+field;
158 if (TBE_EDITOR.getElement(theRecord,field,'required') && document[TBE_EDITOR.formname][theField]) {
159 if (TBE_EDITOR.checkElements('required', false, theRecord, field)) {
160 TBE_EDITOR.setImage(imgReqObjName,TBE_EDITOR.images.clear);
161 } else {
162 TBE_EDITOR.setImage(imgReqObjName,TBE_EDITOR.images.req);
163 }
164 }
165 if (TBE_EDITOR.getElement(theRecord,field,'range') && document[TBE_EDITOR.formname][theField]) {
166 if (TBE_EDITOR.checkElements('range', false, theRecord, field)) {
167 TBE_EDITOR.setImage(imgReqObjName,TBE_EDITOR.images.clear);
168 } else {
169 TBE_EDITOR.setImage(imgReqObjName,TBE_EDITOR.images.req);
170 }
171 }
172 if (TBE_EDITOR.isPalettedoc) { TBE_EDITOR.setOriginalFormFieldValue(theField) };
173 },
174 setOriginalFormFieldValue: function(theField) {
175 if (TBE_EDITOR.isPalettedoc && (TBE_EDITOR.isPalettedoc).document[TBE_EDITOR.formname] && (TBE_EDITOR.isPalettedoc).document[TBE_EDITOR.formname][theField]) {
176 (TBE_EDITOR.isPalettedoc).document[TBE_EDITOR.formname][theField].value = document[TBE_EDITOR.formname][theField].value;
177 }
178 },
179 isFormChanged: function(noAlert) {
180 if (TBE_EDITOR.isChanged && !noAlert && confirm(TBE_EDITOR.labels.fieldsChanged)) {
181 return 0;
182 }
183 return TBE_EDITOR.isChanged;
184 },
185 checkAndDoSubmit: function(sendAlert) {
186 if (TBE_EDITOR.checkSubmit(sendAlert)) { TBE_EDITOR.submitForm(); }
187 },
188 /**
189 * Checks if the form can be submitted according to any possible restrains like required values, item numbers etc.
190 * Returns true if the form can be submitted, otherwise false (and might issue an alert message, if "sendAlert" is 1)
191 * If "sendAlert" is false, no error message will be shown upon false return value (if "1" then it will).
192 * If "sendAlert" is "-1" then the function will ALWAYS return true regardless of constraints (except if login has expired) - this is used in the case where a form field change requests a form update and where it is accepted that constraints are not observed (form layout might change so other fields are shown...)
193 */
194 checkSubmit: function(sendAlert) {
195 var funcIndex, funcMax, funcRes;
196 var OK=1;
197
198 // $this->additionalJS_submit:
199 if (TBE_EDITOR.actionChecks && TBE_EDITOR.actionChecks.submit) {
200 for (funcIndex=0, funcMax=TBE_EDITOR.actionChecks.submit.length; funcIndex<funcMax; funcIndex++) {
201 try {
202 eval(TBE_EDITOR.actionChecks.submit[funcIndex]);
203 } catch(error) {}
204 }
205 }
206
207 if(!OK) {
208 if (!confirm(unescape("SYSTEM ERROR: One or more Rich Text Editors on the page could not be contacted. This IS an error, although it should not be regular.\nYou can save the form now by pressing OK, but you will loose the Rich Text Editor content if you do.\n\nPlease report the error to your administrator if it persists."))) {
209 return false;
210 } else {
211 OK = 1;
212 }
213 }
214 // $reqLinesCheck
215 if (!TBE_EDITOR.checkElements('required', false)) { OK = 0; }
216 // $reqRangeCheck
217 if (!TBE_EDITOR.checkElements('range', false)) { OK = 0; }
218
219 if (OK || sendAlert==-1) {
220 return true;
221 } else {
222 if(sendAlert) alert(TBE_EDITOR.labels.fieldsMissing);
223 return false;
224 }
225 },
226 checkRange: function(numberOfElements, lower, upper) {
227 // for backwards compatibility, check if we're dealing with an element as first parameter
228 if(typeof numberOfElements == 'object') {
229 numberOfElements = numberOfElements.length;
230 }
231
232 if (numberOfElements >= lower && numberOfElements <= upper) {
233 return true;
234 } else {
235 return false;
236 }
237 },
238 setImage: function(name,image) {
239 var object;
240 if (document[name]) {
241 object = document[name];
242 } else if (document.getElementById(name)) {
243 object = document.getElementById(name);
244 }
245 if (object) {
246 if (typeof image == 'object') {
247 document[name].src = image.src;
248 } else {
249 document[name].src = eval(image+'.src');
250 }
251 }
252 },
253 submitForm: function() {
254 if (TBE_EDITOR.doSaveFieldName) {
255 document[TBE_EDITOR.formname][TBE_EDITOR.doSaveFieldName].value=1;
256 }
257 // Set a short timeout to allow other JS processes to complete, in particular those from
258 // EXT:backend/Resources/Public/JavaScript/FormEngine.js (reference: http://forge.typo3.org/issues/58755).
259 // TODO: This should be solved in a better way when this script is refactored.
260 window.setTimeout('document[TBE_EDITOR.formname].submit()', 10);
261 },
262 split: function(theStr1, delim, index) {
263 var theStr = ""+theStr1;
264 var lengthOfDelim = delim.length;
265 sPos = -lengthOfDelim;
266 if (index<1) {index=1;}
267 for (var a=1; a<index; a++) {
268 sPos = theStr.indexOf(delim, sPos+lengthOfDelim);
269 if (sPos==-1) { return null; }
270 }
271 ePos = theStr.indexOf(delim, sPos+lengthOfDelim);
272 if(ePos == -1) { ePos = theStr.length; }
273 return (theStr.substring(sPos+lengthOfDelim,ePos));
274 },
275 curSelected: function(theField) {
276 var fObjSel = document[TBE_EDITOR.formname][theField];
277 var retVal="";
278 if (fObjSel) {
279 if (fObjSel.type=='select-multiple' || fObjSel.type=='select-one') {
280 var l=fObjSel.length;
281 for (a=0;a<l;a++) {
282 if (fObjSel.options[a].selected==1) {
283 retVal+=fObjSel.options[a].value+",";
284 }
285 }
286 }
287 }
288 return retVal;
289 },
290 rawurlencode: function(str,maxlen) {
291 var output = str;
292 if (maxlen) output = output.substr(0,200);
293 output = encodeURIComponent(output);
294 return output;
295 },
296 str_replace: function(match,replace,string) {
297 var input = ''+string;
298 var matchStr = ''+match;
299 if (!matchStr) { return string; }
300 var output = '';
301 var pointer=0;
302 var pos = input.indexOf(matchStr);
303 while (pos!=-1) {
304 output+=''+input.substr(pointer, pos-pointer)+replace;
305 pointer=pos+matchStr.length;
306 pos = input.indexOf(match,pos+1);
307 }
308 output+=''+input.substr(pointer);
309 return output;
310 },
311 toggle_display_states: function(id, state_1, state_2) {
312 var node = document.getElementById(id);
313 if (node) {
314 switch (node.style.display) {
315 case state_1:
316 node.style.display = state_2;
317 break;
318 case state_2:
319 node.style.display = state_1;
320 break;
321 }
322 }
323 return false;
324 },
325
326 /**
327 * Determines backend path to be used for e.g. ajax.php
328 * @return string
329 * @deprecated since TYPO3 CMS 7, will be removed with TYPO3 CMS 8
330 */
331 getBackendPath: function() {
332 if (typeof console != 'undefined') {
333 console.log('TS.getBackendPath() is deprecated since TYPO3 CMS 7, and will be removed in TYPO3 CMS 8.');
334 }
335 if (TYPO3) {
336 if (TYPO3.configuration && TYPO3.configuration.PATH_typo3) {
337 backendPath = TYPO3.configuration.PATH_typo3;
338 } else if (TYPO3.settings && TYPO3.settings.PATH_typo3) {
339 backendPath = TYPO3.settings.PATH_typo3;
340 }
341 }
342 return backendPath;
343 }
344 };
345
346 function typoSetup () {
347 this.passwordDummy = '********';
348 this.decimalSign = '.';
349 }
350 var TS = new typoSetup();
351 var evalFunc = new evalFunc();
352
353 // backwards compatibility for extensions
354 var TBE_EDITOR_setHiddenContent = TBE_EDITOR.setHiddenContent;
355 var TBE_EDITOR_isChanged = TBE_EDITOR.isChanged;
356 var TBE_EDITOR_fieldChanged_fName = TBE_EDITOR.fieldChanged_fName;
357 var TBE_EDITOR_fieldChanged = TBE_EDITOR.fieldChanged;
358 var TBE_EDITOR_setOriginalFormFieldValue = TBE_EDITOR.setOriginalFormFieldValue;
359 var TBE_EDITOR_isFormChanged = TBE_EDITOR.isFormChanged;
360 var TBE_EDITOR_checkAndDoSubmit = TBE_EDITOR.checkAndDoSubmit;
361 var TBE_EDITOR_checkSubmit = TBE_EDITOR.checkSubmit;
362 var TBE_EDITOR_checkRange = TBE_EDITOR.checkRange;
363 var TBE_EDITOR_setImage = TBE_EDITOR.setImage;
364 var TBE_EDITOR_submitForm = TBE_EDITOR.submitForm;
365 var TBE_EDITOR_split = TBE_EDITOR.split;
366 var TBE_EDITOR_curSelected = TBE_EDITOR.curSelected;
367 var TBE_EDITOR_rawurlencode = TBE_EDITOR.rawurlencode;
368 var TBE_EDITOR_str_replace = TBE_EDITOR.str_replace;
369
370
371 var typo3form = {
372 fieldSetNull: function(fieldName, isNull) {
373 if (document[TBE_EDITOR.formname][fieldName]) {
374 var formFieldItemWrapper = Element.up(document[TBE_EDITOR.formname][fieldName], '.t3js-formengine-field-item');
375
376 if (isNull) {
377 formFieldItemWrapper.addClassName('disabled');
378 } else {
379 formFieldItemWrapper.removeClassName('disabled');
380 }
381 }
382 },
383 fieldTogglePlaceholder: function(fieldName, showPlaceholder) {
384 if (!document[TBE_EDITOR.formname][fieldName]) {
385 return;
386 }
387
388 var formFieldItemWrapper = Element.up(document[TBE_EDITOR.formname][fieldName], '.t3js-formengine-field-item');
389 var placeholder = formFieldItemWrapper.select('.t3js-formengine-placeholder-placeholder')[0];
390 var formField = formFieldItemWrapper.select('.t3js-formengine-placeholder-formfield')[0];
391 if (showPlaceholder) {
392 placeholder.show();
393 formField.hide();
394 } else {
395 placeholder.hide();
396 formField.show();
397 }
398 },
399 fieldSet: function(theField, evallist, is_in, checkbox, checkboxValue) {
400 var i;
401
402 if (document[TBE_EDITOR.formname][theField]) {
403 var theFObj = new evalFunc_dummy (evallist,is_in, checkbox, checkboxValue);
404 var theValue = document[TBE_EDITOR.formname][theField].value;
405 if (checkbox && theValue==checkboxValue) {
406 document[TBE_EDITOR.formname][theField+"_hr"].value="";
407 if (document[TBE_EDITOR.formname][theField+"_cb"]) document[TBE_EDITOR.formname][theField+"_cb"].checked = "";
408 } else {
409 document[TBE_EDITOR.formname][theField+"_hr"].value = evalFunc.outputObjValue(theFObj, theValue);
410 if (document[TBE_EDITOR.formname][theField+"_cb"]) document[TBE_EDITOR.formname][theField+"_cb"].checked = "on";
411 }
412 }
413 },
414 fieldGet: function(theField, evallist, is_in, checkbox, checkboxValue, checkbox_off, checkSetValue) {
415 if (document[TBE_EDITOR.formname][theField]) {
416 var theFObj = new evalFunc_dummy (evallist,is_in, checkbox, checkboxValue);
417 if (checkbox_off) {
418 if (document[TBE_EDITOR.formname][theField+"_cb"].checked) {
419 var split = evallist.split(',');
420 for (var i = 0; split.length > i; i++) {
421 var el = split[i].replace(/ /g, '');
422 if (el == 'datetime' || el == 'date') {
423 var now = new Date();
424 checkSetValue = Date.parse(now)/1000 - now.getTimezoneOffset()*60;
425 break;
426 } else if (el == 'time' || el == 'timesec') {
427 checkSetValue = evalFunc_getTimeSecs(new Date());
428 break;
429 }
430 }
431 document[TBE_EDITOR.formname][theField].value=checkSetValue;
432 } else {
433 document[TBE_EDITOR.formname][theField].value=checkboxValue;
434 }
435 }else{
436 document[TBE_EDITOR.formname][theField].value = evalFunc.evalObjValue(theFObj, document[TBE_EDITOR.formname][theField+"_hr"].value);
437 }
438 typo3form.fieldSet(theField, evallist, is_in, checkbox, checkboxValue);
439 }
440 }
441 };
442
443 // backwards compatibility for extensions
444 var typo3FormFieldSet = typo3form.fieldSet;
445 var typo3FormFieldGet = typo3form.fieldGet;