10e193c5da9fa5187c7f937b0f691e036f75dde1
[Packages/TYPO3.CMS.git] / t3lib / jsfunc.inline.js
1 /*<![CDATA[*/
2
3 /***************************************************************
4 * Inline-Relational-Record Editing
5 *
6 * $Id$
7 *
8 *
9 *
10 * Copyright notice
11 *
12 * (c) 2006-2009 Oliver Hader <oh@inpublica.de>
13 * All rights reserved
14 *
15 * This script is part of the TYPO3 project. The TYPO3 project is
16 * free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * The GNU General Public License can be found at
22 * http://www.gnu.org/copyleft/gpl.html.
23 *
24 * This script is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * This copyright notice MUST APPEAR in all copies of the script!
30 ***************************************************************/
31
32 var inline = {
33 prependFormFieldNames: 'data',
34 noTitleString: '[No title]',
35 lockedAjaxMethod: {},
36 sourcesLoaded: {},
37 data: {},
38
39 addToDataArray: function(object) {
40 $H(object).each(function(pair) {
41 inline.data[pair.key] = $H(inline.data[pair.key]).merge(pair.value).toObject();
42 });
43 },
44 setPrependFormFieldNames: function(value) { this.prependFormFieldNames = value; },
45 setNoTitleString: function(value) { this.noTitleString = value; },
46
47 expandCollapseRecord: function(objectId, expandSingle) {
48 var currentUid = this.parseFormElementName('none', objectId, 1);
49 var objectPrefix = this.parseFormElementName('full', objectId, 0, 1);
50
51 var currentState = '';
52 var collapse = new Array();
53 var expand = new Array();
54
55 // if only a single record should be visibly for that set of records
56 // and the record clicked itself is no visible, collapse all others
57 if (expandSingle && !Element.visible(objectId+'_fields'))
58 collapse = this.collapseAllRecords(objectId, objectPrefix, currentUid);
59
60 Element.toggle(objectId+'_fields');
61 currentState = Element.visible(objectId+'_fields') ? 1 : 0
62
63 if (this.isNewRecord(objectId))
64 this.updateExpandedCollapsedStateLocally(objectId, currentState);
65 else if (currentState)
66 expand.push(currentUid);
67 else if (!currentState)
68 collapse.push(currentUid);
69
70 this.setExpandedCollapsedState(objectId, expand.join(','), collapse.join(','));
71
72 return false;
73 },
74
75 collapseAllRecords: function(objectId, objectPrefix, callingUid) {
76 // get the form field, where all records are stored
77 var objectName = this.prependFormFieldNames+this.parseFormElementName('parts', objectId, 3, 2);
78 var formObj = document.getElementsByName(objectName);
79 var collapse = [];
80
81 if (formObj.length) {
82 // the uid of the calling object (last part in objectId)
83 var recObjectId = '';
84
85 var records = formObj[0].value.split(',');
86 for (var i=0; i<records.length; i++) {
87 recObjectId = objectPrefix+'['+records[i]+']';
88 if (records[i] != callingUid && Element.visible(recObjectId+'_fields')) {
89 Element.hide(recObjectId+'_fields');
90 if (this.isNewRecord(recObjectId)) this.updateExpandedCollapsedStateLocally(recObjectId, 0);
91 else collapse.push(records[i]);
92 }
93 }
94 }
95
96 return collapse;
97 },
98
99 updateExpandedCollapsedStateLocally: function(objectId, value) {
100 var ucName = 'uc[inlineView]'+this.parseFormElementName('parts', objectId, 3, 2);
101 var ucFormObj = document.getElementsByName(ucName);
102 if (ucFormObj.length) {
103 ucFormObj[0].value = value;
104 }
105 },
106
107 createNewRecord: function(objectId, recordUid) {
108 if (this.isBelowMax(objectId)) {
109 if (recordUid) {
110 objectId += '['+recordUid+']';
111 }
112 this.makeAjaxCall('createNewRecord', [this.getNumberOfRTE(), objectId], true);
113 } else {
114 alert('There are no more relations possible at this moment!');
115 }
116 return false;
117 },
118
119 synchronizeLocalizeRecords: function(objectId, type) {
120 var parameters = [this.getNumberOfRTE(), objectId, type];
121 this.makeAjaxCall('synchronizeLocalizeRecords', parameters, true);
122 },
123
124 setExpandedCollapsedState: function(objectId, expand, collapse) {
125 this.makeAjaxCall('setExpandedCollapsedState', [objectId, expand, collapse]);
126 },
127
128 makeAjaxCall: function(method, params, lock) {
129 var max, url='', urlParams='', options={};
130 if (method && params && params.length && this.lockAjaxMethod(method, lock)) {
131 url = 'ajax.php';
132 urlParams = '&ajaxID=t3lib_TCEforms_inline::'+method;
133 for (var i=0, max=params.length; i<max; i++) {
134 urlParams += '&ajax['+i+']='+params[i];
135 }
136 options = {
137 method: 'post',
138 parameters: urlParams,
139 onSuccess: function(xhr) { inline.processAjaxResponse(method, xhr); },
140 onFailure: function(xhr) { inline.showAjaxFailure(method, xhr); }
141 };
142
143 new Ajax.Request(url, options);
144 }
145 },
146
147 lockAjaxMethod: function(method, lock) {
148 if (!lock || !inline.lockedAjaxMethod[method]) {
149 inline.lockedAjaxMethod[method] = true;
150 return true;
151 } else {
152 return false;
153 }
154 },
155
156 unlockAjaxMethod: function(method) {
157 inline.lockedAjaxMethod[method] = false;
158 },
159
160 processAjaxResponse: function(method, xhr, json) {
161 var addTag=null, restart=false, processedCount=0, element=null, errorCatch=[], sourcesWaiting=[];
162 if (!json && xhr) {
163 json = eval('('+xhr.responseText+')');
164 }
165 // If there are elements the should be added to the <HEAD> tag (e.g. for RTEhtmlarea):
166 if (json.headData) {
167 var head = inline.getDomHeadTag();
168 var headTags = inline.getDomHeadChildren(head);
169 $A(json.headData).each(function(addTag) {
170 if (!restart) {
171 if (addTag && (addTag.innerHTML || !inline.searchInDomTags(headTags, addTag))) {
172 if (addTag.name=='SCRIPT' && addTag.innerHTML && processedCount) {
173 restart = true;
174 return false;
175 } else {
176 if (addTag.name=='SCRIPT' && addTag.innerHTML) {
177 try {
178 eval(addTag.innerHTML);
179 } catch(e) {
180 errorCatch.push(e);
181 }
182 } else {
183 element = inline.createNewDomElement(addTag);
184 // Set onload handler for external JS scripts:
185 if (addTag.name=='SCRIPT' && element.src) {
186 element.onload = inline.sourceLoadedHandler(element);
187 sourcesWaiting.push(element.src);
188 }
189 head.appendChild(element);
190 processedCount++;
191 }
192 json.headData.shift();
193 }
194 }
195 }
196 });
197 }
198 if (restart || processedCount) {
199 window.setTimeout(function() { inline.reprocessAjaxResponse(method, json, sourcesWaiting); }, 40);
200 } else {
201 if (method) {
202 inline.unlockAjaxMethod(method);
203 }
204 if (json.scriptCall && json.scriptCall.length) {
205 $A(json.scriptCall).each(function(value) { eval(value); });
206 }
207 }
208 },
209
210 // Check if dynamically added scripts are loaded and restart inline.processAjaxResponse():
211 reprocessAjaxResponse: function (method, json, sourcesWaiting) {
212 var sourcesLoaded = true;
213 if (sourcesWaiting && sourcesWaiting.length) {
214 $A(sourcesWaiting).each(function(source) {
215 if (!inline.sourcesLoaded[source]) {
216 sourcesLoaded = false;
217 return false;
218 }
219 });
220 }
221 if (sourcesLoaded) {
222 $A(sourcesWaiting).each(function(source) {
223 delete(inline.sourcesLoaded[source]);
224 });
225 window.setTimeout(function() { inline.processAjaxResponse(method, null, json); }, 80);
226 } else {
227 window.setTimeout(function() { inline.reprocessAjaxResponse(method, json, sourcesWaiting); }, 40);
228 }
229 },
230
231 sourceLoadedHandler: function(element) {
232 if (element && element.src) {
233 inline.sourcesLoaded[element.src] = true;
234 }
235 },
236
237 showAjaxFailure: function(method, xhr) {
238 inline.unlockAjaxMethod(method);
239 alert('Error: '+xhr.status+"\n"+xhr.statusText);
240 },
241
242 // foreign_selector: used by selector box (type='select')
243 importNewRecord: function(objectId) {
244 var selector = $(objectId+'_selector');
245 if (selector.selectedIndex != -1) {
246 var selectedValue = selector.options[selector.selectedIndex].value;
247 if (!this.data.unique || !this.data.unique[objectId]) {
248 selector.options[selector.selectedIndex].selected = false;
249 }
250 this.makeAjaxCall('createNewRecord', [this.getNumberOfRTE(), objectId, selectedValue], true);
251 }
252 return false;
253 },
254
255 // foreign_selector: used by element browser (type='group/db')
256 importElement: function(objectId, table, uid, type) {
257 window.setTimeout(
258 function() {
259 inline.makeAjaxCall('createNewRecord', [this.getNumberOfRTE(), objectId, uid], true);
260 },
261 10
262 );
263 },
264
265 // Check uniqueness for element browser:
266 checkUniqueElement: function(objectId, table, uid, type) {
267 if (this.checkUniqueUsed(objectId, uid, table)) {
268 return {passed: false,message: 'There is already a relation to the selected element!'};
269 } else {
270 return {passed: true};
271 }
272 },
273
274 // Checks if a record was used and should be unique:
275 checkUniqueUsed: function(objectId, uid, table) {
276 if (this.data.unique && this.data.unique[objectId]) {
277 var unique = this.data.unique[objectId];
278 var values = $H(unique.used).values();
279
280 // for select: only the uid is stored
281 if (unique['type'] == 'select') {
282 if (values.indexOf(uid) != -1) return true;
283
284 // for group/db: table and uid is stored in a assoc array
285 } else if (unique.type == 'groupdb') {
286 for (var i=values.length-1; i>=0; i--) {
287 // if the pair table:uid is already used:
288 if (values[i].table==table && values[i].uid==uid) return true;
289 }
290 }
291 }
292 return false;
293 },
294
295 setUniqueElement: function(objectId, table, uid, type, elName) {
296 var recordUid = this.parseFormElementName('none', elName, 1, 1);
297 // alert(objectId+'/'+table+'/'+uid+'/'+recordUid);
298 this.setUnique(objectId, recordUid, uid);
299 },
300
301 // this function is applied to a newly inserted record by AJAX
302 // it removes the used select items, that should be unique
303 setUnique: function(objectId, recordUid, selectedValue) {
304 if (this.data.unique && this.data.unique[objectId]) {
305 var unique = this.data.unique[objectId];
306
307 if (unique.type == 'select') {
308 // remove used items from each select-field of the child records
309 if (!(unique.selector && unique.max == -1)) {
310 var elName = this.parseFormElementName('full', objectId, 1)+'['+recordUid+']['+unique.field+']';
311 var formName = this.prependFormFieldNames+this.parseFormElementName('parts', objectId, 3, 1);
312
313 var fieldObj = document.getElementsByName(elName);
314 var values = $H(unique.used).values();
315
316 if (fieldObj.length) {
317 // remove all items from the new select-item which are already used in other children
318 for (var i=0; i<values.length; i++) this.removeSelectOption(fieldObj[0], values[i]);
319 // set the selected item automatically to the first of the remaining items if no selector is used
320 if (!unique.selector) {
321 selectedValue = fieldObj[0].options[0].value;
322 fieldObj[0].options[0].selected = true;
323 this.updateUnique(fieldObj[0], objectId, formName, recordUid);
324 this.handleChangedField(fieldObj[0], objectId+'['+recordUid+']');
325 }
326 if (typeof this.data.unique[objectId].used.length != 'undefined') {
327 this.data.unique[objectId].used = {};
328 }
329 this.data.unique[objectId].used[recordUid] = selectedValue;
330 }
331 }
332 } else if (unique.type == 'groupdb') {
333 // add the new record to the used items:
334 this.data.unique[objectId].used[recordUid] = {'table':unique.elTable, 'uid':selectedValue};
335 }
336
337 // remove used items from a selector-box
338 if (unique.selector == 'select' && selectedValue) {
339 var selector = $(objectId+'_selector');
340 this.removeSelectOption(selector, selectedValue);
341 this.data.unique[objectId]['used'][recordUid] = selectedValue;
342 }
343 }
344 },
345
346 domAddNewRecord: function(method, insertObject, objectPrefix, htmlData) {
347 if (this.isBelowMax(objectPrefix)) {
348 if (method == 'bottom')
349 new Insertion.Bottom(insertObject, htmlData);
350 else if (method == 'after')
351 new Insertion.After(insertObject, htmlData);
352 }
353 },
354
355 // Get script and link elements from head tag:
356 getDomHeadChildren: function(head) {
357 var headTags=[];
358 $$('head script', 'head link').each(function(tag) { headTags.push(tag); });
359 return headTags;
360 },
361
362 getDomHeadTag: function() {
363 if (document && document.head) {
364 return document.head;
365 } else {
366 var head = $$('head');
367 if (head.length) {
368 return head[0];
369 }
370 }
371 return false;
372 },
373
374 // Search whether elements exist in a given haystack:
375 searchInDomTags: function(haystack, needle) {
376 var result = false;
377 $A(haystack).each(function(element) {
378 if (element.nodeName.toUpperCase()==needle.name) {
379 var attributesCount = $H(needle.attributes).keys().length;
380 var attributesFound = 0;
381 $H(needle.attributes).each(function(attribute) {
382 if (element.getAttribute && element.getAttribute(attribute.key)==attribute.value) {
383 attributesFound++;
384 }
385 });
386 if (attributesFound==attributesCount) {
387 result = true;
388 return true;
389 }
390 }
391 });
392 return result;
393 },
394
395 // Create a new DOM element:
396 createNewDomElement: function(addTag) {
397 var element = document.createElement(addTag.name);
398 if (addTag.attributes) {
399 $H(addTag.attributes).each(function(attribute) {
400 element[attribute.key] = attribute.value;
401 });
402 }
403 return element;
404 },
405
406 changeSorting: function(objectId, direction) {
407 var objectName = this.prependFormFieldNames+this.parseFormElementName('parts', objectId, 3, 2);
408 var objectPrefix = this.parseFormElementName('full', objectId, 0, 1);
409 var formObj = document.getElementsByName(objectName);
410
411 if (formObj.length) {
412 // the uid of the calling object (last part in objectId)
413 var callingUid = this.parseFormElementName('none', objectId, 1);
414 var records = formObj[0].value.split(',');
415 var current = records.indexOf(callingUid);
416 var changed = false;
417
418 // move up
419 if (direction > 0 && current > 0) {
420 records[current] = records[current-1];
421 records[current-1] = callingUid;
422 changed = true;
423
424 // move down
425 } else if (direction < 0 && current < records.length-1) {
426 records[current] = records[current+1];
427 records[current+1] = callingUid;
428 changed = true;
429 }
430
431 if (changed) {
432 formObj[0].value = records.join(',');
433 var cAdj = direction > 0 ? 1 : 0; // adjustment
434 $(objectId+'_div').parentNode.insertBefore(
435 $(objectPrefix+'['+records[current-cAdj]+']_div'),
436 $(objectPrefix+'['+records[current+1-cAdj]+']_div')
437 );
438 this.redrawSortingButtons(objectPrefix, records);
439 }
440 }
441
442 return false;
443 },
444
445 dragAndDropSorting: function(element) {
446 var objectId = element.getAttribute('id').replace(/_records$/, '');
447 var objectName = inline.prependFormFieldNames+inline.parseFormElementName('parts', objectId, 3);
448 var formObj = document.getElementsByName(objectName);
449
450 if (formObj.length) {
451 var checked = new Array();
452 var order = Sortable.sequence(element);
453 var records = formObj[0].value.split(',');
454
455 // check if ordered uid is really part of the records
456 // virtually deleted items might still be there but ordering shouldn't saved at all on them
457 for (var i=0; i<order.length; i++) {
458 if (records.indexOf(order[i]) != -1) {
459 checked.push(order[i]);
460 }
461 }
462
463 formObj[0].value = checked.join(',');
464
465 if (inline.data.config && inline.data.config[objectId]) {
466 var table = inline.data.config[objectId].table;
467 inline.redrawSortingButtons(objectId+'['+table+']', checked);
468 }
469 }
470 },
471
472 createDragAndDropSorting: function(objectId) {
473 Sortable.create(
474 objectId,
475 {
476 format: /^[^_\-](?:[A-Za-z0-9\[\]\-\_]*)\[(.*)\]_div$/,
477 onUpdate: inline.dragAndDropSorting,
478 tag: 'div',
479 handle: 'sortableHandle',
480 overlap: 'vertical',
481 constraint: 'vertical'
482 }
483 );
484 },
485
486 destroyDragAndDropSorting: function(objectId) {
487 Sortable.destroy(objectId);
488 },
489
490 redrawSortingButtons: function(objectPrefix, records) {
491 var i;
492 var headerObj;
493 var sortingObj = new Array();
494
495 // if no records were passed, fetch them from form field
496 if (typeof records == 'undefined') {
497 records = new Array();
498 var objectName = this.prependFormFieldNames+this.parseFormElementName('parts', objectPrefix, 3, 1);
499 var formObj = document.getElementsByName(objectName);
500 if (formObj.length) records = formObj[0].value.split(',');
501 }
502
503 for (i=0; i<records.length; i++) {
504 if (!records[i].length) continue;
505
506 headerObj = $(objectPrefix+'['+records[i]+']_header');
507 sortingObj[0] = Element.select(headerObj, '.sortingUp');
508 sortingObj[1] = Element.select(headerObj, '.sortingDown');
509
510 if (sortingObj[0].length) {
511 sortingObj[0][0].style.visibility = (i == 0 ? 'hidden' : 'visible');
512 }
513 if (sortingObj[1].length) {
514 sortingObj[1][0].style.visibility = (i == records.length-1 ? 'hidden' : 'visible');
515 }
516 }
517 },
518
519 memorizeAddRecord: function(objectPrefix, newUid, afterUid, selectedValue) {
520 if (this.isBelowMax(objectPrefix)) {
521 var objectName = this.prependFormFieldNames+this.parseFormElementName('parts', objectPrefix, 3, 1);
522 var formObj = document.getElementsByName(objectName);
523
524 if (formObj.length) {
525 var records = new Array();
526 if (formObj[0].value.length) records = formObj[0].value.split(',');
527
528 if (afterUid) {
529 var newRecords = new Array();
530 for (var i=0; i<records.length; i++) {
531 if (records[i].length) newRecords.push(records[i]);
532 if (afterUid == records[i]) newRecords.push(newUid);
533 }
534 records = newRecords;
535 } else {
536 records.push(newUid);
537 }
538 formObj[0].value = records.join(',');
539 }
540
541 this.redrawSortingButtons(objectPrefix, records);
542
543 if (this.data.unique && this.data.unique[objectPrefix]) {
544 var unique = this.data.unique[objectPrefix];
545 this.setUnique(objectPrefix, newUid, selectedValue);
546 }
547 }
548
549 // if we reached the maximum off possible records after this action, hide the new buttons
550 if (!this.isBelowMax(objectPrefix)) {
551 var objectParent = this.parseFormElementName('full', objectPrefix, 0 , 1);
552 var md5 = this.getObjectMD5(objectParent);
553 this.hideElementsWithClassName('.inlineNewButton'+(md5 ? '.'+md5 : ''), objectParent);
554 }
555
556 if (TBE_EDITOR) TBE_EDITOR.fieldChanged_fName(objectName, formObj);
557 },
558
559 memorizeRemoveRecord: function(objectName, removeUid) {
560 var formObj = document.getElementsByName(objectName);
561 if (formObj.length) {
562 var parts = new Array();
563 if (formObj[0].value.length) {
564 parts = formObj[0].value.split(',');
565 parts = parts.without(removeUid);
566 formObj[0].value = parts.join(',');
567 if (TBE_EDITOR) TBE_EDITOR.fieldChanged_fName(objectName, formObj);
568 return parts.length;
569 }
570 }
571 return false;
572 },
573
574 updateUnique: function(srcElement, objectPrefix, formName, recordUid) {
575 if (this.data.unique && this.data.unique[objectPrefix]) {
576 var unique = this.data.unique[objectPrefix];
577 var oldValue = unique.used[recordUid];
578
579 if (unique.selector == 'select') {
580 var selector = $(objectPrefix+'_selector');
581 this.removeSelectOption(selector, srcElement.value);
582 if (typeof oldValue != 'undefined') this.readdSelectOption(selector, oldValue, unique);
583 }
584
585 if (!(unique.selector && unique.max == -1)) {
586 var formObj = document.getElementsByName(formName);
587 if (unique && formObj.length) {
588 var records = formObj[0].value.split(',');
589 var recordObj;
590 for (var i=0; i<records.length; i++) {
591 recordObj = document.getElementsByName(this.prependFormFieldNames+'['+unique.table+']['+records[i]+']['+unique.field+']');
592 if (recordObj.length && recordObj[0] != srcElement) {
593 this.removeSelectOption(recordObj[0], srcElement.value);
594 if (typeof oldValue != 'undefined') this.readdSelectOption(recordObj[0], oldValue, unique);
595 }
596 }
597 this.data.unique[objectPrefix].used[recordUid] = srcElement.value;
598 }
599 }
600 }
601 },
602
603 revertUnique: function(objectPrefix, elName, recordUid) {
604 if (this.data.unique && this.data.unique[objectPrefix]) {
605 var unique = this.data.unique[objectPrefix];
606 var fieldObj = elName ? document.getElementsByName(elName+'['+unique.field+']') : null;
607
608 if (unique.type == 'select') {
609 if (fieldObj && fieldObj.length) {
610 delete(this.data.unique[objectPrefix].used[recordUid])
611
612 if (unique.selector == 'select') {
613 if (!isNaN(fieldObj[0].value)) {
614 var selector = $(objectPrefix+'_selector');
615 this.readdSelectOption(selector, fieldObj[0].value, unique);
616 }
617 }
618
619 if (!(unique.selector && unique.max == -1)) {
620 var formName = this.prependFormFieldNames+this.parseFormElementName('parts', objectPrefix, 3, 1);
621 var formObj = document.getElementsByName(formName);
622 if (formObj.length) {
623 var records = formObj[0].value.split(',');
624 var recordObj;
625 // walk through all inline records on that level and get the select field
626 for (var i=0; i<records.length; i++) {
627 recordObj = document.getElementsByName(this.prependFormFieldNames+'['+unique.table+']['+records[i]+']['+unique.field+']');
628 if (recordObj.length) this.readdSelectOption(recordObj[0], fieldObj[0].value, unique);
629 }
630 }
631 }
632 }
633 } else if (unique.type == 'groupdb') {
634 // alert(objectPrefix+'/'+recordUid);
635 delete(this.data.unique[objectPrefix].used[recordUid])
636 }
637 }
638 },
639
640 enableDisableRecord: function(objectId) {
641 var elName = this.parseFormElementName('full', objectId, 2);
642 var imageObj = $(objectId+'_disabled');
643 var valueObj = document.getElementsByName(elName+'[hidden]');
644 var formObj = document.getElementsByName(elName+'[hidden]_0');
645 var imagePath = '';
646
647 if (valueObj && formObj) {
648 formObj[0].click();
649 imagePath = this.parsePath(imageObj.src);
650 imageObj.src = imagePath+(valueObj[0].value > 0 ? 'button_unhide.gif' : 'button_hide.gif');
651 }
652
653 return false;
654 },
655
656 deleteRecord: function(objectId, options) {
657 var i, j, inlineRecords, records, childObjectId, childTable;
658 var objectPrefix = this.parseFormElementName('full', objectId, 0 , 1);
659 var elName = this.parseFormElementName('full', objectId, 2);
660 var shortName = this.parseFormElementName('parts', objectId, 2);
661 var recordUid = this.parseFormElementName('none', objectId, 1);
662 var beforeDeleteIsBelowMax = this.isBelowMax(objectPrefix);
663
664 // revert the unique settings if available
665 this.revertUnique(objectPrefix, elName, recordUid);
666
667 // Remove from TBE_EDITOR (required fields, required range, etc.):
668 if (TBE_EDITOR && TBE_EDITOR.removeElement) {
669 var removeStack = [];
670 inlineRecords = Element.select(objectId+'_div', '.inlineRecord');
671 // Remove nested child records from TBE_EDITOR required/range checks:
672 for (i=inlineRecords.length-1; i>=0; i--) {
673 if (inlineRecords[i].value.length) {
674 records = inlineRecords[i].value.split(',');
675 childObjectId = this.data.map[inlineRecords[i].name];
676 childTable = this.data.config[childObjectId].table;
677 for (j=records.length-1; j>=0; j--) {
678 removeStack.push(this.prependFormFieldNames+'['+childTable+']['+records[j]+']');
679 }
680 }
681 }
682 removeStack.push(this.prependFormFieldNames+shortName);
683 TBE_EDITOR.removeElementArray(removeStack);
684 }
685
686 // If the record is new and was never saved before, just remove it from DOM:
687 if (this.isNewRecord(objectId) || options && options.forceDirectRemoval) {
688 this.fadeAndRemove(objectId+'_div');
689 // If the record already exists in storage, mark it to be deleted on clicking the save button:
690 } else {
691 document.getElementsByName('cmd'+shortName+'[delete]')[0].disabled = false;
692 new Effect.Fade(objectId+'_div');
693 }
694
695 var recordCount = this.memorizeRemoveRecord(
696 this.prependFormFieldNames+this.parseFormElementName('parts', objectId, 3, 2),
697 recordUid
698 );
699
700 if (recordCount <= 1) {
701 this.destroyDragAndDropSorting(this.parseFormElementName('full', objectId, 0 , 2)+'_records');
702 }
703 this.redrawSortingButtons(objectPrefix);
704
705 // if the NEW-button was hidden and now we can add again new children, show the button
706 if (!beforeDeleteIsBelowMax && this.isBelowMax(objectPrefix)) {
707 var objectParent = this.parseFormElementName('full', objectPrefix, 0 , 1);
708 var md5 = this.getObjectMD5(objectParent);
709 this.showElementsWithClassName('.inlineNewButton'+(md5 ? '.'+md5 : ''), objectParent);
710 }
711 return false;
712 },
713
714 parsePath: function(path) {
715 var backSlash = path.lastIndexOf('\\');
716 var normalSlash = path.lastIndexOf('/');
717
718 if (backSlash > 0)
719 path = path.substring(0,backSlash+1);
720 else if (normalSlash > 0)
721 path = path.substring(0,normalSlash+1);
722 else
723 path = '';
724
725 return path;
726 },
727
728 parseFormElementName: function(wrap, objectId, rightCount, skipRight) {
729 // remove left and right side "data[...|...]" -> '...|...'
730 objectId = objectId.substr(0, objectId.lastIndexOf(']')).substr(objectId.indexOf('[')+1);
731
732 if (!wrap) wrap = 'full';
733 if (!skipRight) skipRight = 0;
734
735 var elReturn;
736 var elParts = new Array();
737 var idParts = objectId.split('][');
738 for (var i=0; i<skipRight; i++) idParts.pop();
739
740 if (rightCount > 0) {
741 for (var i=0; i<rightCount; i++) elParts.unshift(idParts.pop());
742 } else {
743 for (var i=0; i<-rightCount; i++) idParts.shift();
744 elParts = idParts;
745 }
746
747 if (wrap == 'full') {
748 elReturn = this.prependFormFieldNames+'['+elParts.join('][')+']';
749 } else if (wrap == 'parts') {
750 elReturn = '['+elParts.join('][')+']';
751 } else if (wrap == 'none') {
752 elReturn = elParts.length > 1 ? elParts : elParts.join('');
753 }
754
755 return elReturn;
756 },
757
758 handleChangedField: function(formField, objectId) {
759 var formObj;
760 if (typeof formField == 'object') {
761 formObj = formField;
762 } else {
763 formObj = document.getElementsByName(formField);
764 if (formObj.length) formObj = formObj[0];
765 }
766
767 if (formObj != undefined) {
768 var value;
769 if (formObj.nodeName == 'SELECT') value = formObj.options[formObj.selectedIndex].text;
770 else value = formObj.value;
771 $(objectId+'_label').innerHTML = value.length ? value : this.noTitleString;
772 }
773 return true;
774 },
775
776 arrayAssocCount: function(object) {
777 var count = 0;
778 if (typeof object.length != 'undefined') {
779 count = object.length;
780 } else {
781 for (var i in object) count++;
782 }
783 return count;
784 },
785
786 isBelowMax: function(objectPrefix) {
787 var isBelowMax = true;
788 var objectName = this.prependFormFieldNames+this.parseFormElementName('parts', objectPrefix, 3, 1);
789 var formObj = document.getElementsByName(objectName);
790
791 if (this.data.config && this.data.config[objectPrefix] && formObj.length) {
792 var recordCount = formObj[0].value ? formObj[0].value.split(',').length : 0;
793 if (recordCount >= this.data.config[objectPrefix].max) isBelowMax = false;
794 }
795 if (isBelowMax && this.data.unique && this.data.unique[objectPrefix]) {
796 var unique = this.data.unique[objectPrefix];
797 if (this.arrayAssocCount(unique.used) >= unique.max && unique.max >= 0) isBelowMax = false;
798 }
799 return isBelowMax;
800 },
801
802 getOptionsHash: function(selectObj) {
803 var optionsHash = {};
804 for (var i=0; i<selectObj.options.length; i++) optionsHash[selectObj.options[i].value] = i;
805 return optionsHash;
806 },
807
808 removeSelectOption: function(selectObj, value) {
809 var optionsHash = this.getOptionsHash(selectObj);
810 if (optionsHash[value] != undefined) selectObj.options[optionsHash[value]] = null;
811 },
812
813 readdSelectOption: function(selectObj, value, unique) {
814 var index = null;
815 var optionsHash = this.getOptionsHash(selectObj);
816 var possibleValues = $H(unique.possible).keys();
817
818 for (var possibleValue in unique.possible) {
819 if (possibleValue == value) break;
820 if (optionsHash[possibleValue] != undefined) index = optionsHash[possibleValue];
821 }
822
823 if (index == null) index = 0;
824 else if (index < selectObj.options.length) index++;
825 // recreate the <option> tag
826 var readdOption = document.createElement('option');
827 readdOption.text = unique.possible[value];
828 readdOption.value = value;
829 // add the <option> at the right position
830 selectObj.add(readdOption, document.all ? index : selectObj.options[index]);
831 },
832
833 hideElementsWithClassName: function(selector, parentElement) {
834 this.setVisibilityOfElementsWithClassName('hide', selector, parentElement);
835 },
836
837 showElementsWithClassName: function(selector, parentElement) {
838 this.setVisibilityOfElementsWithClassName('show', selector, parentElement);
839 },
840
841 setVisibilityOfElementsWithClassName: function(action, selector, parentElement) {
842 var domObjects = Selector.findChildElements($(parentElement), [selector]);
843 if (action == 'hide') {
844 $A(domObjects).each(function(domObject) { new Effect.Fade(domObject); });
845 } else if (action == 'show') {
846 $A(domObjects).each(function(domObject) { new Effect.Appear(domObject); });
847 }
848 },
849
850 fadeOutFadeIn: function(objectId) {
851 var optIn = { duration:0.5, transition:Effect.Transitions.linear, from:0.50, to:1.00 };
852 var optOut = { duration:0.5, transition:Effect.Transitions.linear, from:1.00, to:0.50 };
853 optOut.afterFinish = function() { new Effect.Opacity(objectId, optIn); };
854 new Effect.Opacity(objectId, optOut);
855 },
856
857 isNewRecord: function(objectId) {
858 return $(objectId+'_div') && $(objectId+'_div').hasClassName('inlineIsNewRecord')
859 ? true
860 : false;
861 },
862
863 // Find and fix nested of inline and tab levels if a new element was created dynamically (it doesn't know about its nesting):
864 findContinuedNestedLevel: function(nested, objectId) {
865 if (this.data.nested && this.data.nested[objectId]) {
866 // Remove the first element from the new nested stack, it's just a hint:
867 nested.shift();
868 nested = this.data.nested[objectId].concat(nested);
869 return nested;
870 } else {
871 return nested;
872 }
873 },
874
875 getNumberOfRTE: function() {
876 var number = 0;
877 if (typeof RTEarea != 'undefined' && RTEarea.length > 0) {
878 number = RTEarea.length-1;
879 }
880 return number;
881 },
882
883 getObjectMD5: function(objectPrefix) {
884 var md5 = false;
885 if (this.data.config && this.data.config[objectPrefix] && this.data.config[objectPrefix].md5) {
886 md5 = this.data.config[objectPrefix].md5;
887 }
888 return md5
889 },
890
891 fadeAndRemove: function(element) {
892 if ($(element)) {
893 new Effect.Fade(element, { afterFinish: function() { Element.remove(element); } });
894 }
895 }
896 }
897
898 Object.extend(Array.prototype, {
899 diff: function(current) {
900 var diff = new Array();
901 if (this.length == current.length) {
902 for (var i=0; i<this.length; i++) {
903 if (this[i] !== current[i]) diff.push(i);
904 }
905 }
906 return diff;
907 }
908 });
909
910 /*]]>*/