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