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