Fixed bug #12327: IRRE - DOM object identifier use invalid characters
authorOliver Hader <oliver.hader@typo3.org>
Mon, 26 Oct 2009 23:18:47 +0000 (23:18 +0000)
committerOliver Hader <oliver.hader@typo3.org>
Mon, 26 Oct 2009 23:18:47 +0000 (23:18 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@6277 709f56b5-9817-0410-a4d7-c38de5d9e867

ChangeLog
t3lib/class.t3lib_tceforms.php
t3lib/class.t3lib_tceforms_inline.php
t3lib/jsfunc.inline.js
typo3/jsfunc.tbe_editor.js

index 75d02fe..3d7ed87 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2009-10-27  Oliver Hader  <oliver@typo3.org>
+
+       * Fixed bug #12327: IRRE - DOM object identifier use invalid characters
+
 2009-10-26  Rupert Germann  <rupi@gmx.li>
 
        * Fixed bug #11980: TCEforms suggest defaultreceiver returns array or boolean (thanks to Steffen Gebert)
index 994511e..d0b74b2 100644 (file)
@@ -894,7 +894,15 @@ class t3lib_TCEforms       {
                                        $PA['fieldChangeFunc']['alert']=$alertMsgOnChange;
                                                // if this is the child of an inline type and it is the field creating the label
                                        if ($this->inline->isInlineChildAndLabelField($table, $field)) {
-                                               $PA['fieldChangeFunc']['inline'] = "inline.handleChangedField('".$PA['itemFormElName']."','".$this->inline->inlineNames['object']."[$table][".$row['uid']."]');";
+                                               $inlineObjectId = implode(
+                                                       t3lib_TCEforms_inline::Structure_Separator,
+                                                       array(
+                                                               $this->inline->inlineNames['object'],
+                                                               $table,
+                                                               $row['uid']
+                                                       )
+                                               );
+                                               $PA['fieldChangeFunc']['inline'] = "inline.handleChangedField('" . $PA['itemFormElName'] . "','" . $inlineObjectId . "');";
                                        }
 
                                                // Based on the type of the item, call a render function:
index 2f1d3c9..ce9eeb4 100644 (file)
@@ -90,6 +90,9 @@
 
 
 class t3lib_TCEforms_inline {
+       const Structure_Separator = '-';
+       const Disposal_AttributeName = 'Disposal_AttributeName';
+       const Disposal_AttributeId = 'Disposal_AttributeId';
 
        /**
         * Reference to the calling TCEforms instance
@@ -214,9 +217,9 @@ class t3lib_TCEforms_inline {
                }
                        // add the current inline job to the structure stack
                $this->pushStructure($table, $row['uid'], $field, $config);
-                       // e.g. inline[<table>][<uid>][<field>]
+                       // e.g. data[<table>][<uid>][<field>]
                $nameForm = $this->inlineNames['form'];
-                       // e.g. inline[<pid>][<table1>][<uid1>][<field1>][<table2>][<uid2>][<field2>]
+                       // e.g. data-<pid>-<table1>-<uid1>-<field1>-<table2>-<uid2>-<field2>
                $nameObject = $this->inlineNames['object'];
                        // get the records related to this inline record
                $relatedRecords = $this->getRelatedRecords($table,$field,$row,$PA,$config);
@@ -231,7 +234,7 @@ class t3lib_TCEforms_inline {
                        'table' => $foreign_table,
                        'md5' => md5($nameObject),
                );
-               $this->inlineData['config'][$nameObject.'['.$foreign_table.']'] = array(
+               $this->inlineData['config'][$nameObject. self::Structure_Separator . $foreign_table] = array(
                        'min' => $minitems,
                        'max' => $maxitems,
                        'sortable' => $config['appearance']['useSortable'],
@@ -251,7 +254,7 @@ class t3lib_TCEforms_inline {
                        $uniqueIds = $this->getUniqueIds($relatedRecords['records'], $config, $selConfig['type']=='groupdb');
                        $possibleRecords = $this->getPossibleRecords($table,$field,$row,$config,'foreign_unique');
                        $uniqueMax = $config['appearance']['useCombination'] || $possibleRecords === false ? -1 : count($possibleRecords);
-                       $this->inlineData['unique'][$nameObject.'['.$foreign_table.']'] = array(
+                       $this->inlineData['unique'][$nameObject. self::Structure_Separator . $foreign_table] = array(
                                'max' => $uniqueMax,
                                'used' => $uniqueIds,
                                'type' => $selConfig['type'],
@@ -284,15 +287,15 @@ class t3lib_TCEforms_inline {
 
                        // Render the level links (create new record, localize all, synchronize):
                if ($config['appearance']['levelLinksPosition']!='none') {
-                       $levelLinks = $this->getLevelInteractionLink('newRecord', $nameObject.'['.$foreign_table.']', $config);
+                       $levelLinks = $this->getLevelInteractionLink('newRecord', $nameObject . self::Structure_Separator . $foreign_table, $config);
                        if ($language>0) {
                                        // Add the "Localize all records" link before all child records:
                                if (isset($config['appearance']['showAllLocalizationLink']) && $config['appearance']['showAllLocalizationLink']) {
-                                       $levelLinks.= $this->getLevelInteractionLink('localize', $nameObject.'['.$foreign_table.']', $config);
+                                       $levelLinks.= $this->getLevelInteractionLink('localize', $nameObject . self::Structure_Separator . $foreign_table, $config);
                                }
                                        // Add the "Synchronize with default language" link before all child records:
                                if (isset($config['appearance']['showSynchronizationLink']) && $config['appearance']['showSynchronizationLink']) {
-                                       $levelLinks.= $this->getLevelInteractionLink('synchronize', $nameObject.'['.$foreign_table.']', $config);
+                                       $levelLinks.= $this->getLevelInteractionLink('synchronize', $nameObject . self::Structure_Separator . $foreign_table, $config);
                                }
                        }
                }
@@ -364,7 +367,7 @@ class t3lib_TCEforms_inline {
                        $this->fObj->registerDefaultLanguageData($foreign_table, $rec);
                }
                        // Send a mapping information to the browser via JSON:
-                       // e.g. data[<curTable>][<curId>][<curField>] => data[<pid>][<parentTable>][<parentId>][<parentField>][<curTable>][<curId>][<curField>]
+                       // e.g. data[<curTable>][<curId>][<curField>] => data-<pid>-<parentTable>-<parentId>-<parentField>-<curTable>-<curId>-<curField>
                $this->inlineData['map'][$this->inlineNames['form']] = $this->inlineNames['object'];
 
                        // Set this variable if we handle a brand new unsaved record:
@@ -383,9 +386,9 @@ class t3lib_TCEforms_inline {
                        // Get the current naming scheme for DOM name/id attributes:
                $nameObject = $this->inlineNames['object'];
                $appendFormFieldNames = '['.$foreign_table.']['.$rec['uid'].']';
-               $formFieldNames = $nameObject.$appendFormFieldNames;
+               $objectId = $nameObject . self::Structure_Separator . $foreign_table . self::Structure_Separator . $rec['uid'];
                        // Put the current level also to the dynNestedStack of TCEforms:
-               $this->fObj->pushToDynNestedStack('inline', $this->inlineNames['object'].$appendFormFieldNames);
+               $this->fObj->pushToDynNestedStack('inline', $objectId);
 
                $header = $this->renderForeignRecordHeader($parentUid, $foreign_table, $rec, $config, $isVirtualRecord);
                if (!$isVirtualRecord) {
@@ -418,11 +421,11 @@ class t3lib_TCEforms_inline {
                }
 
                        // set the record container with data for output
-               $out = '<div id="'.$formFieldNames.'_header">'.$header.'</div>';
-               $out .= '<div id="'.$formFieldNames.'_fields"'.$appearanceStyleFields.'>'.$fields.$combination.'</div>';
+               $out = '<div id="' . $objectId . '_header">' . $header . '</div>';
+               $out .= '<div id="' . $objectId . '_fields"' . $appearanceStyleFields . '>' . $fields.$combination . '</div>';
                        // wrap the header, fields and combination part of a child record with a div container
-               $class = 'inlineDiv'.($this->fObj->clientInfo['BROWSER']=='msie' ? 'MSIE' : '').($isNewRecord ? ' inlineIsNewRecord' : '');
-               $out = '<div id="'.$formFieldNames.'_div" class="'.$class.'">' . $out . '</div>';
+               $class = 'inlineDiv'.($this->fObj->clientInfo['BROWSER']=='msie' ? 'MSIE' : '') . ($isNewRecord ? ' inlineIsNewRecord' : '');
+               $out = '<div id="' . $objectId . '_div" class="'.$class.'">' . $out . '</div>';
 
                        // Remove the current level also from the dynNestedStack of TCEforms:
                $this->fObj->popFromDynNestedStack();
@@ -468,9 +471,9 @@ class t3lib_TCEforms_inline {
         */
        function renderForeignRecordHeader($parentUid, $foreign_table, $rec, $config, $isVirtualRecord=false) {
                        // Init:
-               $formFieldNames = $this->inlineNames['object'].'['.$foreign_table.']['.$rec['uid'].']';
+               $objectId = $this->inlineNames['object'] . self::Structure_Separator . $foreign_table . self::Structure_Separator . $rec['uid'];
                $expandSingle = $config['appearance']['expandSingle'] ? 1 : 0;
-               $onClick = "return inline.expandCollapseRecord('".htmlspecialchars($formFieldNames)."', $expandSingle)";
+               $onClick = "return inline.expandCollapseRecord('" . htmlspecialchars($objectId) . "', $expandSingle)";
 
                        // Pre-Processing:
                $isOnSymmetricSide = t3lib_loadDBGroup::isOnSymmetricSide($parentUid, $config, $rec);
@@ -518,7 +521,7 @@ class t3lib_TCEforms_inline {
 
                $altText = t3lib_BEfunc::getRecordIconAltText($rec, $foreign_table);
                $iconImg = t3lib_iconWorks::getIconImage($foreign_table, $rec, $this->backPath, 'title="'.htmlspecialchars($altText).'" class="absmiddle"');
-               $label = '<span id="'.$formFieldNames.'_label">'.$recTitle.'</span>';
+               $label = '<span id="' . $objectId . '_label">' . $recTitle . '</span>';
                if (!$isVirtualRecord) {
                        $iconImg = $this->wrapWithAnchor($iconImg, '#', array('onclick' => $onClick));
                        $label = $this->wrapWithAnchor($label, '#', array('onclick' => $onClick, 'style' => 'display: block;'));
@@ -560,8 +563,8 @@ class t3lib_TCEforms_inline {
                $enableManualSorting = $tcaTableCtrl['sortby'] || $config['MM'] || (!$isOnSymmetricSide && $config['foreign_sortby']) || ($isOnSymmetricSide && $config['symmetric_sortby']) ? true : false;
 
                $nameObject = $this->inlineNames['object'];
-               $nameObjectFt = $nameObject.'['.$foreign_table.']';
-               $nameObjectFtId = $nameObjectFt.'['.$rec['uid'].']';
+               $nameObjectFt = $nameObject . self::Structure_Separator . $foreign_table;
+               $nameObjectFtId = $nameObjectFt . self::Structure_Separator . $rec['uid'];
 
                $calcPerms = $GLOBALS['BE_USER']->calcPerms(
                        t3lib_BEfunc::readPageAccess($rec['pid'], $GLOBALS['BE_USER']->getPagePermsClause(1))
@@ -809,9 +812,9 @@ class t3lib_TCEforms_inline {
                        $selector_itemListStyle = isset($config['itemListStyle']) ? ' style="'.htmlspecialchars($config['itemListStyle']).'"' : ' style="'.$this->fObj->defaultMultipleSelectorStyle.'"';
                        $size = intval($conf['size']);
                        $size = $conf['autoSizeMax'] ? t3lib_div::intInRange(count($itemArray)+1,t3lib_div::intInRange($size,1),$conf['autoSizeMax']) : $size;
-                       $onChange = "return inline.importNewRecord('".$this->inlineNames['object']."[".$conf['foreign_table']."]')";
+                       $onChange = "return inline.importNewRecord('" . $this->inlineNames['object']. self::Structure_Separator . $conf['foreign_table'] . "')";
                        $item = '
-                               <select id="'.$this->inlineNames['object'].'['.$conf['foreign_table'].']_selector"'.
+                               <select id="'.$this->inlineNames['object'] . self::Structure_Separator . $conf['foreign_table'] . '_selector"'.
                                                        $this->fObj->insertDefStyle('select').
                                                        ($size ? ' size="'.$size.'"' : '').
                                                        ' onchange="'.htmlspecialchars($onChange).'"'.
@@ -852,7 +855,7 @@ class t3lib_TCEforms_inline {
 
                $config = $PA['fieldConf']['config'];
                $allowed = $config['allowed'];
-               $objectPrefix = $this->inlineNames['object'].'['.$foreign_table.']';
+               $objectPrefix = $this->inlineNames['object'] . self::Structure_Separator . $foreign_table;
 
                $createNewRelationText = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.createNewRelation',1);
                $onClick = "setFormValueOpenBrowser('db','".('|||'.$allowed.'|'.$objectPrefix.'|inline.checkUniqueElement||inline.importElement')."'); return false;";
@@ -870,7 +873,7 @@ class t3lib_TCEforms_inline {
         * The possible keys for the parameter $type are 'newRecord', 'localize' and 'synchronize'.
         *
         * @param       string          $type: The link type, values are 'newRecord', 'localize' and 'synchronize'.
-        * @param       string          $objectPrefix: The "path" to the child record to create (e.g. 'data[parten_table][parent_uid][parent_field][child_table]')
+        * @param       string          $objectPrefix: The "path" to the child record to create (e.g. 'data-parentPageId-partenTable-parentUid-parentField-childTable]')
         * @param       array           $conf: TCA configuration of the parent(!) field
         * @return      string          The HTML code of the new link, wrapped in a div
         */
@@ -916,7 +919,7 @@ class t3lib_TCEforms_inline {
        /**
         * Creates a link/button to create new records
         *
-        * @param       string          $objectPrefix: The "path" to the child record to create (e.g. '[parten_table][parent_uid][parent_field][child_table]')
+        * @param       string          $objectPrefix: The "path" to the child record to create (e.g. 'data-parentPageId-partenTable-parentUid-parentField-childTable')
         * @param       array           $conf: TCA configuration of the parent(!) field
         * @return      string          The HTML code for the new record link
         * @deprecated  since TYPO3 4.2.0-beta1
@@ -1149,8 +1152,8 @@ class t3lib_TCEforms_inline {
                }
 
                        // the HTML-object-id's prefix of the dynamically created record
-               $objectPrefix = $this->inlineNames['object'].'['.$current['table'].']';
-               $objectId = $objectPrefix.'['.$record['uid'].']';
+               $objectPrefix = $this->inlineNames['object'] . self::Structure_Separator . $current['table'];
+               $objectId = $objectPrefix . self::Structure_Separator . $record['uid'];
 
                        // render the foreign record that should passed back to browser
                $item = $this->renderForeignRecord($parent['uid'], $record, $config);
@@ -1247,7 +1250,7 @@ class t3lib_TCEforms_inline {
                $jsonArrayScriptCall =& $jsonArray['scriptCall'];
 
                $nameObject = $this->inlineNames['object'];
-               $nameObjectForeignTable = $nameObject.'['.$current['table'].']';
+               $nameObjectForeignTable = $nameObject . self::Structure_Separator . $current['table'];
                        // Get the name of the field pointing to the original record:
                $transOrigPointerField = $GLOBALS['TCA'][$current['table']]['ctrl']['transOrigPointerField'];
                        // Get the name of the field used as foreign selector (if any):
@@ -1260,7 +1263,7 @@ class t3lib_TCEforms_inline {
                $localizedItems = array_diff($newItems, $oldItems);
                        // Set the items that should be removed in the forms view:
                foreach ($removedItems as $item) {
-                       $jsonArrayScriptCall[] = "inline.deleteRecord('".$nameObjectForeignTable.'['.$item.']'."', {forceDirectRemoval: true});";
+                       $jsonArrayScriptCall[] = "inline.deleteRecord('".$nameObjectForeignTable . self::Structure_Separator . $item . "', {forceDirectRemoval: true});";
                }
                        // Set the items that should be added in the forms view:
                foreach ($localizedItems as $item) {
@@ -1270,7 +1273,7 @@ class t3lib_TCEforms_inline {
                        $jsonArrayScriptCall[] = "inline.memorizeAddRecord('$nameObjectForeignTable', '".$item."', null, $selectedValue);";
                                // Remove possible virtual records in the form which showed that a child records could be localized:
                        if (isset($row[$transOrigPointerField]) && $row[$transOrigPointerField]) {
-                               $jsonArrayScriptCall[] = "inline.fadeAndRemove('".$nameObjectForeignTable.'['.$row[$transOrigPointerField].']_div'."');";
+                               $jsonArrayScriptCall[] = "inline.fadeAndRemove('" . $nameObjectForeignTable . self::Structure_Separator . $row[$transOrigPointerField] . '_div' . "');";
                        }
                }
                if ($data) {
@@ -1685,10 +1688,9 @@ class t3lib_TCEforms_inline {
                $current = $this->getStructureLevel(-1);
                        // if there are still more inline levels available
                if ($current !== false) {
-                       $lastItemName = $this->getStructureItemName($current);
                        $this->inlineNames = array(
-                               'form' => $this->prependFormFieldNames.$lastItemName,
-                               'object' => $this->prependNaming.'['.$this->inlineFirstPid.']'.$this->getStructurePath(),
+                               'form' => $this->prependFormFieldNames . $this->getStructureItemName($current, self::Disposal_AttributeName),
+                               'object' => $this->prependNaming . self::Structure_Separator . $this->inlineFirstPid . self::Structure_Separator . $this->getStructurePath(),
                        );
                        // if there are no more inline levels available
                } else {
@@ -1698,16 +1700,25 @@ class t3lib_TCEforms_inline {
 
 
        /**
-        * Create a name/id for usage in HTML output of a level of the structure stack.
+        * Create a name/id for usage in HTML output of a level of the structure stack to be used in form names.
         *
         * @param       array           $levelData: Array of a level of the structure stack (containing the keys table, uid and field)
         * @return      string          The name/id of that level, to be used for HTML output
         */
-       function getStructureItemName($levelData) {
+       function getStructureItemName($levelData, $disposal = self::Disposal_AttributeId) {
                if (is_array($levelData)) {
-                       $name = '['.$levelData['table'].']' .
-                                       '['.$levelData['uid'].']' .
-                                       (isset($levelData['field']) ? '['.$levelData['field'].']' : '');
+                       $parts = array($levelData['table'], $levelData['uid']);
+                       if (isset($levelData['field'])) {
+                               $parts[] = $levelData['field'];
+                       }
+
+                               // Use in name attributes:
+                       if ($disposal === self::Disposal_AttributeName) {
+                               $name = '[' . implode('][', $parts) . ']';
+                               // Use in id attributes:
+                       } else {
+                               $name = implode(self::Structure_Separator, $parts);
+                       }
                }
                return $name;
        }
@@ -1733,7 +1744,7 @@ class t3lib_TCEforms_inline {
 
        /**
         * Get the identifiers of a given depth of level, from the top of the stack to the bottom.
-        * An identifier consists looks like [<table>][<uid>][<field>].
+        * An identifier looks like "<table>-<uid>-<field>".
         *
         * @param       integer         $structureDepth: How much levels to output, beginning from the top of the stack
         * @return      string          The path of identifiers
@@ -1744,7 +1755,7 @@ class t3lib_TCEforms_inline {
 
                for ($i = 1; $i <= $structureDepth; $i++) {
                        $current = $this->getStructureLevel(-$i);
-                       $string = $this->getStructureItemName($current).$string;
+                       $string = $this->getStructureItemName($current, self::Disposal_AttributeId) . $string;
                }
 
                return $string;
@@ -1753,7 +1764,7 @@ class t3lib_TCEforms_inline {
 
        /**
         * Convert the DOM object-id of an inline container to an array.
-        * The object-id could look like 'data[inline][tx_mmftest_company][1][employees]'.
+        * The object-id could look like 'data-parentPageId-tx_mmftest_company-1-employees'.
         * The result is written to $this->inlineStructure.
         * There are two keys:
         *  - 'stable': Containing full qualified identifiers (table, uid and field)
@@ -1766,10 +1777,10 @@ class t3lib_TCEforms_inline {
        function parseStructureString($string, $loadConfig=true) {
                $unstable = array();
                $vector = array('table', 'uid', 'field');
-               $pattern = '/^'.$this->prependNaming.'\[(.+?)\]\[(.+)\]$/';
+               $pattern = '/^' . $this->prependNaming . self::Structure_Separator . '(.+?)' . self::Structure_Separator . '(.+)$/';
                if (preg_match($pattern, $string, $match)) {
                        $this->inlineFirstPid = $match[1];
-                       $parts = explode('][', $match[2]);
+                       $parts = explode(self::Structure_Separator, $match[2]);
                        $partsCnt = count($parts);
                        for ($i = 0; $i < $partsCnt; $i++) {
                                if ($i > 0 && $i % 3 == 0) {
index ca61fbc..b19c5cd 100644 (file)
@@ -30,6 +30,7 @@
 ***************************************************************/
 
 var inline = {
+       structureSeparator: '-',
        prependFormFieldNames: 'data',
        noTitleString: '[No title]',
        lockedAjaxMethod: {},
@@ -45,8 +46,8 @@ var inline = {
        setNoTitleString: function(value) { this.noTitleString = value; },
 
        expandCollapseRecord: function(objectId, expandSingle) {
-               var currentUid = this.parseFormElementName('none', objectId, 1);
-               var objectPrefix = this.parseFormElementName('full', objectId, 0, 1);
+               var currentUid = this.parseObjectId('none', objectId, 1);
+               var objectPrefix = this.parseObjectId('full', objectId, 0, 1);
 
                var currentState = '';
                var collapse = new Array();
@@ -74,7 +75,7 @@ var inline = {
 
        collapseAllRecords: function(objectId, objectPrefix, callingUid) {
                        // get the form field, where all records are stored
-               var objectName = this.prependFormFieldNames+this.parseFormElementName('parts', objectId, 3, 2);
+               var objectName = this.prependFormFieldNames+this.parseObjectId('parts', objectId, 3, 2, true);
                var formObj = document.getElementsByName(objectName);
                var collapse = [];
 
@@ -84,7 +85,7 @@ var inline = {
 
                        var records = formObj[0].value.split(',');
                        for (var i=0; i<records.length; i++) {
-                               recObjectId = objectPrefix+'['+records[i]+']';
+                               recObjectId = objectPrefix + this.structureSeparator + records[i];
                                if (records[i] != callingUid && Element.visible(recObjectId+'_fields')) {
                                        Element.hide(recObjectId+'_fields');
                                        if (this.isNewRecord(recObjectId)) this.updateExpandedCollapsedStateLocally(recObjectId, 0);
@@ -97,7 +98,7 @@ var inline = {
        },
 
        updateExpandedCollapsedStateLocally: function(objectId, value) {
-               var ucName = 'uc[inlineView]'+this.parseFormElementName('parts', objectId, 3, 2);
+               var ucName = 'uc[inlineView]'+this.parseObjectId('parts', objectId, 3, 2, true);
                var ucFormObj = document.getElementsByName(ucName);
                if (ucFormObj.length) {
                        ucFormObj[0].value = value;
@@ -107,7 +108,7 @@ var inline = {
        createNewRecord: function(objectId, recordUid) {
                if (this.isBelowMax(objectId)) {
                        if (recordUid) {
-                               objectId += '['+recordUid+']';
+                               objectId += this.structureSeparator + recordUid;
                        }
                        this.makeAjaxCall('createNewRecord', [this.getNumberOfRTE(), objectId], true);
                } else {
@@ -307,8 +308,7 @@ var inline = {
                        if (unique.type == 'select') {
                                        // remove used items from each select-field of the child records
                                if (!(unique.selector && unique.max == -1)) {
-                                       var elName = this.parseFormElementName('full', objectId, 1)+'['+recordUid+']['+unique.field+']';
-                                       var formName = this.prependFormFieldNames+this.parseFormElementName('parts', objectId, 3, 1);
+                                       var formName = this.prependFormFieldNames+this.parseObjectId('parts', objectId, 3, 1, true);
 
                                        var fieldObj = document.getElementsByName(elName);
                                        var values = $H(unique.used).values();
@@ -404,13 +404,13 @@ var inline = {
        },
 
        changeSorting: function(objectId, direction) {
-               var objectName = this.prependFormFieldNames+this.parseFormElementName('parts', objectId, 3, 2);
-               var objectPrefix = this.parseFormElementName('full', objectId, 0, 1);
+               var objectName = this.prependFormFieldNames+this.parseObjectId('parts', objectId, 3, 2, true);
+               var objectPrefix = this.parseObjectId('full', objectId, 0, 1);
                var formObj = document.getElementsByName(objectName);
 
                if (formObj.length) {
                                // the uid of the calling object (last part in objectId)
-                       var callingUid = this.parseFormElementName('none', objectId, 1);
+                       var callingUid = this.parseObjectId('none', objectId, 1);
                        var records = formObj[0].value.split(',');
                        var current = records.indexOf(callingUid);
                        var changed = false;
@@ -432,8 +432,8 @@ var inline = {
                                formObj[0].value = records.join(',');
                                var cAdj = direction > 0 ? 1 : 0; // adjustment
                                $(objectId+'_div').parentNode.insertBefore(
-                                       $(objectPrefix+'['+records[current-cAdj]+']_div'),
-                                       $(objectPrefix+'['+records[current+1-cAdj]+']_div')
+                                       $(objectPrefix + this.structureSeparator + records[current-cAdj] + '_div'),
+                                       $(objectPrefix + this.structureSeparator + records[current+1-cAdj] + '_div')
                                );
                                this.redrawSortingButtons(objectPrefix, records);
                        }
@@ -444,7 +444,7 @@ var inline = {
 
        dragAndDropSorting: function(element) {
                var objectId = element.getAttribute('id').replace(/_records$/, '');
-               var objectName = inline.prependFormFieldNames+inline.parseFormElementName('parts', objectId, 3);
+               var objectName = inline.prependFormFieldNames+inline.parseObjectId('parts', objectId, 3, 0, true);
                var formObj = document.getElementsByName(objectName);
 
                if (formObj.length) {
@@ -464,7 +464,7 @@ var inline = {
 
                        if (inline.data.config && inline.data.config[objectId]) {
                                var table = inline.data.config[objectId].table;
-                               inline.redrawSortingButtons(objectId+'['+table+']', checked);
+                               inline.redrawSortingButtons(objectId + inline.structureSeparator + table, checked);
                        }
                }
        },
@@ -474,7 +474,7 @@ var inline = {
                Sortable.create(
                        objectId,
                        {
-                               format: /^[^_\-](?:[A-Za-z0-9\[\]\-\_]*)\[(.*)\]_div$/,
+                               format: /^[^_\-](?:[A-Za-z0-9\-\_]*)-(.*)_div$/,
                                onUpdate: inline.dragAndDropSorting,
                                tag: 'div',
                                handle: 'sortableHandle',
@@ -496,7 +496,7 @@ var inline = {
                        // if no records were passed, fetch them from form field
                if (typeof records == 'undefined') {
                        records = new Array();
-                       var objectName = this.prependFormFieldNames+this.parseFormElementName('parts', objectPrefix, 3, 1);
+                       var objectName = this.prependFormFieldNames+this.parseObjectId('parts', objectPrefix, 3, 1, true);
                        var formObj = document.getElementsByName(objectName);
                        if (formObj.length) records = formObj[0].value.split(',');
                }
@@ -504,7 +504,7 @@ var inline = {
                for (i=0; i<records.length; i++) {
                        if (!records[i].length) continue;
 
-                       headerObj = $(objectPrefix+'['+records[i]+']_header');
+                       headerObj = $(objectPrefix + this.structureSeparator + records[i] + '_header');
                        sortingObj[0] = Element.select(headerObj, '.sortingUp');
                        sortingObj[1] = Element.select(headerObj, '.sortingDown');
 
@@ -519,7 +519,7 @@ var inline = {
 
        memorizeAddRecord: function(objectPrefix, newUid, afterUid, selectedValue) {
                if (this.isBelowMax(objectPrefix)) {
-                       var objectName = this.prependFormFieldNames+this.parseFormElementName('parts', objectPrefix, 3, 1);
+                       var objectName = this.prependFormFieldNames+this.parseObjectId('parts', objectPrefix, 3, 1, true);
                        var formObj = document.getElementsByName(objectName);
 
                        if (formObj.length) {
@@ -549,7 +549,7 @@ var inline = {
 
                        // if we reached the maximum off possible records after this action, hide the new buttons
                if (!this.isBelowMax(objectPrefix)) {
-                       var objectParent = this.parseFormElementName('full', objectPrefix, 0 , 1);
+                       var objectParent = this.parseObjectId('full', objectPrefix, 0 , 1);
                        var md5 = this.getObjectMD5(objectParent);
                        this.hideElementsWithClassName('.inlineNewButton'+(md5 ? '.'+md5 : ''), objectParent);
                }
@@ -618,7 +618,7 @@ var inline = {
                                        }
 
                                        if (!(unique.selector && unique.max == -1)) {
-                                               var formName = this.prependFormFieldNames+this.parseFormElementName('parts', objectPrefix, 3, 1);
+                                               var formName = this.prependFormFieldNames+this.parseObjectId('parts', objectPrefix, 3, 1, true);
                                                var formObj = document.getElementsByName(formName);
                                                if (formObj.length) {
                                                        var records = formObj[0].value.split(',');
@@ -639,7 +639,7 @@ var inline = {
        },
 
        enableDisableRecord: function(objectId) {
-               var elName = this.parseFormElementName('full', objectId, 2);
+               var elName = this.parseObjectId('full', objectId, 2, 0, true);
                var imageObj = $(objectId+'_disabled');
                var valueObj = document.getElementsByName(elName+'[hidden]');
                var formObj = document.getElementsByName(elName+'[hidden]_0');
@@ -656,10 +656,10 @@ var inline = {
 
        deleteRecord: function(objectId, options) {
                var i, j, inlineRecords, records, childObjectId, childTable;
-               var objectPrefix = this.parseFormElementName('full', objectId, 0 , 1);
-               var elName = this.parseFormElementName('full', objectId, 2);
-               var shortName = this.parseFormElementName('parts', objectId, 2);
-               var recordUid = this.parseFormElementName('none', objectId, 1);
+               var objectPrefix = this.parseObjectId('full', objectId, 0 , 1);
+               var elName = this.parseObjectId('full', objectId, 2, 0, true);
+               var shortName = this.parseObjectId('parts', objectId, 2, 0, true);
+               var recordUid = this.parseObjectId('none', objectId, 1);
                var beforeDeleteIsBelowMax = this.isBelowMax(objectPrefix);
 
                        // revert the unique settings if available
@@ -694,18 +694,18 @@ var inline = {
                }
 
                var recordCount = this.memorizeRemoveRecord(
-                       this.prependFormFieldNames+this.parseFormElementName('parts', objectId, 3, 2),
+                       this.prependFormFieldNames+this.parseObjectId('parts', objectId, 3, 2, true),
                        recordUid
                );
 
                if (recordCount <= 1) {
-                       this.destroyDragAndDropSorting(this.parseFormElementName('full', objectId, 0 , 2)+'_records');
+                       this.destroyDragAndDropSorting(this.parseObjectId('full', objectId, 0 , 2)+'_records');
                }
                this.redrawSortingButtons(objectPrefix);
 
                        // if the NEW-button was hidden and now we can add again new children, show the button
                if (!beforeDeleteIsBelowMax && this.isBelowMax(objectPrefix)) {
-                       var objectParent = this.parseFormElementName('full', objectPrefix, 0 , 1);
+                       var objectParent = this.parseObjectId('full', objectPrefix, 0 , 1);
                        var md5 = this.getObjectMD5(objectParent);
                        this.showElementsWithClassName('.inlineNewButton'+(md5 ? '.'+md5 : ''), objectParent);
                }
@@ -726,16 +726,13 @@ var inline = {
                return path;
        },
 
-       parseFormElementName: function(wrap, objectId, rightCount, skipRight) {
-                       // remove left and right side "data[...|...]" -> '...|...'
-               objectId = objectId.substr(0, objectId.lastIndexOf(']')).substr(objectId.indexOf('[')+1);
+       parseFormElementName: function(wrap, formElementName, rightCount, skipRight) {
+               var idParts = this.splitFormElementName(formElementName);
 
                if (!wrap) wrap = 'full';
                if (!skipRight) skipRight = 0;
 
-               var elReturn;
                var elParts = new Array();
-               var idParts = objectId.split('][');
                for (var i=0; i<skipRight; i++) idParts.pop();
 
                if (rightCount > 0) {
@@ -745,12 +742,74 @@ var inline = {
                        elParts = idParts;
                }
 
+               var elReturn = this.constructFormElementName(wrap, elParts);
+
+               return elReturn;
+       },
+
+       splitFormElementName: function(formElementName) {
+               // remove left and right side "data[...|...]" -> '...|...'
+               formElementName = formElementName.substr(0, formElementName.lastIndexOf(']')).substr(formElementName.indexOf('[')+1);
+               var parts = objectId.split('][');
+
+               return parts;
+       },
+
+       splitObjectId: function(objectId) {
+               objectId = objectId.substr(objectId.indexOf(this.structureSeparator)+1);
+               var parts = objectId.split(this.structureSeparator);
+
+               return parts;
+       },
+
+       constructFormElementName: function(wrap, parts) {
+               var elReturn;
+
                if (wrap == 'full') {
-                       elReturn = this.prependFormFieldNames+'['+elParts.join('][')+']';
+                       elReturn = this.prependFormFieldNames+'['+parts.join('][')+']';
                } else if (wrap == 'parts') {
-                       elReturn = '['+elParts.join('][')+']';
+                       elReturn = '['+parts.join('][')+']';
                } else if (wrap == 'none') {
-                       elReturn = elParts.length > 1 ? elParts : elParts.join('');
+                       elReturn = parts.length > 1 ? parts : parts.join('');
+               }
+
+               return elReturn;
+       },
+
+       constructObjectId: function(wrap, parts) {
+               var elReturn;
+
+               if (wrap == 'full') {
+                       elReturn = this.prependFormFieldNames+this.structureSeparator+parts.join(this.structureSeparator);
+               } else if (wrap == 'parts') {
+                       elReturn = this.structureSeparator+parts.join(this.structureSeparator);
+               } else if (wrap == 'none') {
+                       elReturn = parts.length > 1 ? parts : parts.join('');
+               }
+
+               return elReturn;
+       },
+
+       parseObjectId: function(wrap, objectId, rightCount, skipRight, returnAsFormElementName) {
+               var idParts = this.splitObjectId(objectId);
+
+               if (!wrap) wrap = 'full';
+               if (!skipRight) skipRight = 0;
+
+               var elParts = new Array();
+               for (var i=0; i<skipRight; i++) idParts.pop();
+
+               if (rightCount > 0) {
+                       for (var i=0; i<rightCount; i++) elParts.unshift(idParts.pop());
+               } else {
+                       for (var i=0; i<-rightCount; i++) idParts.shift();
+                       elParts = idParts;
+               }
+
+               if (returnAsFormElementName) {
+                       var elReturn = this.constructFormElementName(wrap, elParts);
+               } else {
+                       var elReturn = this.constructObjectId(wrap, elParts);
                }
 
                return elReturn;
@@ -786,7 +845,7 @@ var inline = {
 
        isBelowMax: function(objectPrefix) {
                var isBelowMax = true;
-               var objectName = this.prependFormFieldNames+this.parseFormElementName('parts', objectPrefix, 3, 1);
+               var objectName = this.prependFormFieldNames+this.parseObjectId('parts', objectPrefix, 3, 1, true);
                var formObj = document.getElementsByName(objectName);
 
                if (this.data.config && this.data.config[objectPrefix] && formObj.length) {
index c406304..8ccef9a 100644 (file)
@@ -36,7 +36,7 @@
 var TBE_EDITOR = {
        /* Example:
                elements: {
-                       'data[table][uid]': {
+                       'data-parentPid-table-uid': {
                                'field': {
                                        'range':                [0, 100],
                                        'rangeImg':             '',