[!!!][TASK] Holding out for a t3editor 35/39635/28
authorAndreas Fernandez <a.fernandez@scripting-base.de>
Wed, 20 May 2015 19:18:36 +0000 (21:18 +0200)
committerWouter Wolters <typo3@wouterwolters.nl>
Wed, 15 Jul 2015 11:41:36 +0000 (13:41 +0200)
EXT:t3editor is ported from Prototype to jQuery, thus being an AMD
module. The checkbox for toggling the editor is removed, this
will be configurable per user basis in a separate patch.

This patch also refactors the plugins required for TypoScript parsing
and code completion.

Resolves: #68010
Releases: master
Change-Id: I22cfc910dfb7eb1fc71584030787502fefb51eb9
Reviewed-on: http://review.typo3.org/39635
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Georg Ringer <georg.ringer@gmail.com>
Tested-by: Georg Ringer <georg.ringer@gmail.com>
Reviewed-by: Stefan Froemken <froemken@gmail.com>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
18 files changed:
typo3/contrib/codemirror/js/editor.js
typo3/sysext/core/Documentation/Changelog/master/Breaking-68010-T3Editor-EventCallbacksForCodecompletionHaveChanged.rst [new file with mode: 0644]
typo3/sysext/core/Documentation/Changelog/master/Breaking-68010-T3Editor-PluginRegistrationForCodecompletionHasChanged.rst [new file with mode: 0644]
typo3/sysext/t3editor/Classes/Hook/FileEditHook.php
typo3/sysext/t3editor/Classes/T3editor.php
typo3/sysext/t3editor/Resources/Public/JavaScript/FileEdit.js
typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/CompletionResult.js [new file with mode: 0644]
typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/DescriptionPlugin.js [new file with mode: 0644]
typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/TsCodeCompletion.js [new file with mode: 0644]
typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/TsParser.js [new file with mode: 0644]
typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/TsRef.js [new file with mode: 0644]
typo3/sysext/t3editor/Resources/Public/JavaScript/T3editor.js
typo3/sysext/t3editor/res/jslib/t3editor.js [deleted file]
typo3/sysext/t3editor/res/jslib/ts_codecompletion/completionresult.js [deleted file]
typo3/sysext/t3editor/res/jslib/ts_codecompletion/descriptionPlugin.js [deleted file]
typo3/sysext/t3editor/res/jslib/ts_codecompletion/tscodecompletion.js [deleted file]
typo3/sysext/t3editor/res/jslib/ts_codecompletion/tsparser.js [deleted file]
typo3/sysext/t3editor/res/jslib/ts_codecompletion/tsref.js [deleted file]

index f5092ce..e1788b7 100644 (file)
@@ -904,7 +904,7 @@ var Editor = (function(){
           event.stop();
         }
         else if (code == 83 && this.options.saveFunction) { // S
-          this.options.saveFunction();
+          this.options.saveFunction(this);
           event.stop();
         }
         else if (code == 86 && !mac) { // V
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-68010-T3Editor-EventCallbacksForCodecompletionHaveChanged.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-68010-T3Editor-EventCallbacksForCodecompletionHaveChanged.rst
new file mode 100644 (file)
index 0000000..1878269
--- /dev/null
@@ -0,0 +1,34 @@
+=============================================================================
+Breaking: #68010 - T3Editor - Event callbacks for codecompletion have changed
+=============================================================================
+
+Description
+===========
+
+Due to the rewrite of T3Editor to jQuery, the event callbacks for codecompletion have changed.
+
+
+Impact
+======
+
+Plugins for codecompletion written in Prototype will not work anymore.
+
+
+Affected Installations
+======================
+
+Every third-party extension providing a T3Editor plugin extending the codecompletion.
+
+
+Migration
+=========
+
+Port the plugin to an AMD module. The event callbacks are now part of the module object and not a standalone function anymore.
+
+Example code:
+
+.. code-block:: javascript
+
+       CoolPlugin.afterKeyDown = function(currWordObj, compResult) {
+               CoolPlugin.somethingFunky(currWordObj, compResult);
+       };
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-68010-T3Editor-PluginRegistrationForCodecompletionHasChanged.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-68010-T3Editor-PluginRegistrationForCodecompletionHasChanged.rst
new file mode 100644 (file)
index 0000000..42d9168
--- /dev/null
@@ -0,0 +1,50 @@
+================================================================================
+Breaking: #68010 - T3Editor - Plugin registration for codecompletion has changed
+================================================================================
+
+Description
+===========
+
+Due to the rewrite of T3Editor to jQuery, the plugin registration for codecompletion has changed.
+
+
+Impact
+======
+
+Plugins for codecompletion written in Prototype will not work anymore.
+
+
+Affected Installations
+======================
+
+Every third-party extension providing a T3Editor plugin extending the codecompletion.
+
+
+Migration
+=========
+
+Port the plugin to an AMD module. The plugin must have an ``init`` method with a configuration object as only parameter. Every parameter that was passed to the old Prototype function must be in that configuration object. Please see the example code below or consult :file:`EXT:t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/DescriptionPlugin.js`.
+
+Example code:
+
+.. code-block:: javascript
+
+       define('Awesome/Extension/Plugins/CodeCompletion/CoolPlugin', [
+               'jquery',
+               'TYPO3/CMS/T3editor/Plugins/CodeCompletion/TsRef',
+               'TYPO3/CMS/T3editor/Plugins/CodeCompletion/TsParser'
+       ], function ($, TsRef, TsParser) {
+               var CoolPlugin = {
+                       codeCompleteBox: null,
+                       codemirror: null
+               };
+
+               CoolPlugin.init = function(configuration) {
+                       DescriptionPlugin.codeCompleteBox = configuration.codeCompleteBox;
+                       DescriptionPlugin.codemirror = configuration.codemirror;
+
+                       DescriptionPlugin.codeCompleteBox.parent().append($('<div />', {class: 'foomatic}));
+               };
+
+               return CoolPlugin;
+       });
index 3747706..d475baf 100644 (file)
@@ -55,7 +55,6 @@ class FileEditHook {
                if (!$t3editor->getMode()) {
                        return;
                }
-               $parameters['content'] = str_replace('<!--###POSTJSMARKER###-->', '<!--###POSTJSMARKER###-->' . $t3editor->getModeSpecificJavascriptCode(), $parameters['content']);
        }
 
        /**
index 8a6e8de..c01d6ce 100644 (file)
@@ -30,6 +30,7 @@ class T3editor implements \TYPO3\CMS\Core\SingletonInterface {
        const MODE_PHP = 'php';
        const MODE_SPARQL = 'sparql';
        const MODE_MIXED = 'mixed';
+
        /**
         * @var string
         */
@@ -48,6 +49,27 @@ class T3editor implements \TYPO3\CMS\Core\SingletonInterface {
        protected $editorCounter = 0;
 
        /**
+        * Relative path to EXT:t3editor
+        *
+        * @var string
+        */
+       protected $relExtPath = '';
+
+       /**
+        * Relative directory to codemirror
+        *
+        * @var string
+        */
+       protected $codemirrorPath = 'contrib/codemirror/js/';
+
+       /**
+        * RequireJS modules loaded for code completion
+        *
+        * @var array
+        */
+       protected $codeCompletionComponents = array('TsRef', 'CompletionResult', 'TsParser', 'TsCodeCompletion');
+
+       /**
         * sets the type of code to edit (::MODE_TYPOSCRIPT, ::MODE_JAVASCRIPT)
         *
         * @param $mode string Expects one of the predefined constants
@@ -89,9 +111,7 @@ class T3editor implements \TYPO3\CMS\Core\SingletonInterface {
        public function setModeByType($type) {
                switch ($type) {
                        case 'html':
-
                        case 'htm':
-
                        case 'tmpl':
                                $mode = self::MODE_HTML;
                                break;
@@ -99,7 +119,6 @@ class T3editor implements \TYPO3\CMS\Core\SingletonInterface {
                                $mode = self::MODE_JAVASCRIPT;
                                break;
                        case 'xml':
-
                        case 'svg':
                                $mode = self::MODE_XML;
                                break;
@@ -113,9 +132,7 @@ class T3editor implements \TYPO3\CMS\Core\SingletonInterface {
                                $mode = self::MODE_SPARQL;
                                break;
                        case 'php':
-
                        case 'phpsh':
-
                        case 'inc':
                                $mode = self::MODE_PHP;
                                break;
@@ -150,60 +167,47 @@ class T3editor implements \TYPO3\CMS\Core\SingletonInterface {
                $GLOBALS['LANG']->includeLLFile('EXT:t3editor/locallang.xlf');
                // Disable pmktextarea to avoid conflicts (thanks Peter Klein for this suggestion)
                $GLOBALS['BE_USER']->uc['disablePMKTextarea'] = 1;
+
+               $this->relExtPath = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath('t3editor');
        }
 
        /**
         * Retrieves JavaScript code (header part) for editor
         *
         * @param \TYPO3\CMS\Backend\Template\DocumentTemplate $doc
-        * @return string JavaScript code
+        * @return string
         */
        public function getJavascriptCode($doc) {
-               $content = '';
-               $path_t3e = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath('t3editor');
-               $path_codemirror = 'contrib/codemirror/js/';
-               // Include needed javascript-frameworks
+               /** @var $pageRenderer \TYPO3\CMS\Core\Page\PageRenderer */
                $pageRenderer = $this->getPageRenderer();
+
+               // Include needed javascript-frameworks
                $pageRenderer->loadPrototype();
                $pageRenderer->loadScriptaculous();
+
                // Include editor-css
-               $content .= '<link href="' . GeneralUtility::createVersionNumberedFilename(($GLOBALS['BACK_PATH'] . \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath('t3editor') . 'res/css/t3editor.css')) . '" type="text/css" rel="stylesheet" />';
+               $cssFile = GeneralUtility::createVersionNumberedFilename($GLOBALS['BACK_PATH'] . $this->relExtPath . 'res/css/t3editor.css');
+               $doc->addStyleSheet('t3editor', $cssFile);
+
                // Include editor-js-lib
-               $doc->loadJavascriptLib($path_codemirror . 'codemirror.js');
-               $doc->loadJavascriptLib($path_t3e . 'res/jslib/t3editor.js');
+               $doc->loadJavascriptLib($this->codemirrorPath . 'codemirror.js');
+               $this->loadTypoScriptCodeCompletion($doc);
                $pageRenderer->loadRequireJsModule('TYPO3/CMS/T3editor/T3editor');
-
-               $content .= GeneralUtility::wrapJS(
-                       'T3editor = T3editor || {};' .
-                       'T3editor.lang = ' . json_encode($GLOBALS['LANG']->getLabelsWithPrefix('js.', 'label_')) . ';' . LF .
-                       'T3editor.PATH_t3e = "' . $GLOBALS['BACK_PATH'] . $path_t3e . '"; ' . LF .
-                       'T3editor.PATH_codemirror = "' . $GLOBALS['BACK_PATH'] . $path_codemirror . '"; ' . LF .
-                       'T3editor.template = ' . $this->getPreparedTemplate() . ';' . LF .
-                       'T3editor.ajaxSavetype = "' . $this->ajaxSaveType . '";' . LF
-               );
-               $content .= $this->getModeSpecificJavascriptCode();
-               return $content;
+               return '';
        }
 
        /**
-        * Get mode specific JavaScript code
+        * Load additional code completion for TypoScript
         *
-        * @return string
+        * @param \TYPO3\CMS\Backend\Template\DocumentTemplate $doc
         */
-       public function getModeSpecificJavascriptCode() {
-               if (empty($this->mode)) {
-                       return '';
-               }
-               $path_t3e = $GLOBALS['BACK_PATH'] . \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath('t3editor');
-               $content = '';
+       protected function loadTypoScriptCodeCompletion($doc) {
                if ($this->mode === self::MODE_TYPOSCRIPT) {
-                       $content .= '<script type="text/javascript" src="' . $path_t3e . 'res/jslib/ts_codecompletion/tsref.js' . '"></script>';
-                       $content .= '<script type="text/javascript" src="' . $path_t3e . 'res/jslib/ts_codecompletion/completionresult.js' . '"></script>';
-                       $content .= '<script type="text/javascript" src="' . $path_t3e . 'res/jslib/ts_codecompletion/tsparser.js' . '"></script>';
-                       $content .= '<script type="text/javascript" src="' . $path_t3e . 'res/jslib/ts_codecompletion/tscodecompletion.js' . '"></script>';
+                       $pageRenderer = $doc->getPageRenderer();
+                       foreach ($this->codeCompletionComponents as $codeCompletionComponent) {
+                               $pageRenderer->loadRequireJsModule('TYPO3/CMS/T3editor/Plugins/CodeCompletion/' . $codeCompletionComponent);
+                       }
                }
-               $content .= GeneralUtility::wrapJS('T3editor.parserfile = ' . $this->getParserfileByMode($this->mode) . ';' . LF . 'T3editor.stylesheet = ' . $this->getStylesheetByMode($this->mode) . ';');
-               return $content;
        }
 
        /**
@@ -213,9 +217,8 @@ class T3editor implements \TYPO3\CMS\Core\SingletonInterface {
         */
        protected function getPreparedTemplate() {
                $T3editor_template = GeneralUtility::getUrl(GeneralUtility::getFileAbsFileName('EXT:t3editor/res/templates/t3editor.html'));
-               $T3editor_template = addslashes($T3editor_template);
-               $T3editor_template = str_replace(array(CR, LF), array('', '\' + \''), $T3editor_template);
-               return '\'' . $T3editor_template . '\'';
+               $T3editor_template = str_replace(array(CR, LF), '', $T3editor_template);
+               return $T3editor_template;
        }
 
        /**
@@ -227,31 +230,30 @@ class T3editor implements \TYPO3\CMS\Core\SingletonInterface {
        protected function getParserfileByMode($mode) {
                switch ($mode) {
                        case self::MODE_TYPOSCRIPT:
-                               $relPath = ($GLOBALS['BACK_PATH'] ? $GLOBALS['BACK_PATH'] : '../../../') . \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath('t3editor') . 'res/jslib/parse_typoscript/';
-                               $parserfile = '["' . $relPath . 'tokenizetyposcript.js", "' . $relPath . 'parsetyposcript.js"]';
+                               $relPath = ($GLOBALS['BACK_PATH'] ? $GLOBALS['BACK_PATH'] : '../../../') . $this->relExtPath . 'res/jslib/parse_typoscript/';
+                               $parserfile = array($relPath . 'tokenizetyposcript.js', $relPath . 'parsetyposcript.js');
                                break;
                        case self::MODE_JAVASCRIPT:
-                               $parserfile = '["tokenizejavascript.js", "parsejavascript.js"]';
+                               $parserfile = array('tokenizetyposcript.js', 'parsejavascript.js');
                                break;
                        case self::MODE_CSS:
-                               $parserfile = '"parsecss.js"';
+                               $parserfile = 'parsecss.js';
                                break;
                        case self::MODE_XML:
-                               $parserfile = '"parsexml.js"';
+                               $parserfile = 'parsexml.js';
                                break;
                        case self::MODE_SPARQL:
-                               $parserfile = '"parsesparql.js"';
+                               $parserfile = 'parsesparql.js';
                                break;
                        case self::MODE_HTML:
-                               $parserfile = '["tokenizejavascript.js", "parsejavascript.js", "parsecss.js", "parsexml.js", "parsehtmlmixed.js"]';
+                               $parserfile = array('tokenizejavascript.js', 'parsejavascript.js', 'parsecss.js', 'parsexml.js', 'parsehtmlmixed.js');
                                break;
                        case self::MODE_PHP:
-
                        case self::MODE_MIXED:
-                               $parserfile = '[' . '"tokenizejavascript.js", ' . '"parsejavascript.js", ' . '"parsecss.js", ' . '"parsexml.js", ' . '"../contrib/php/js/tokenizephp.js", ' . '"../contrib/php/js/parsephp.js", ' . '"../contrib/php/js/parsephphtmlmixed.js"' . ']';
+                               $parserfile = array('tokenizejavascript.js', 'parsejavascript.js', 'parsecss.js', 'parsexml.js', '../contrib/php/js/tokenizephp.js', '../contrib/php/js/parsephp.js', '../contrib/php/js/parsephphtmlmixed.js');
                                break;
                }
-               return $parserfile;
+               return json_encode($parserfile);
        }
 
        /**
@@ -263,34 +265,34 @@ class T3editor implements \TYPO3\CMS\Core\SingletonInterface {
        protected function getStylesheetByMode($mode) {
                switch ($mode) {
                        case self::MODE_TYPOSCRIPT:
-                               $stylesheet = 'T3editor.PATH_t3e + "res/css/typoscriptcolors.css"';
+                               $stylesheet = array($this->relExtPath . 'res/css/typoscriptcolors.css');
                                break;
                        case self::MODE_JAVASCRIPT:
-                               $stylesheet = 'T3editor.PATH_codemirror + "../css/jscolors.css"';
+                               $stylesheet = array($this->codemirrorPath . '../css/jscolors.css');
                                break;
                        case self::MODE_CSS:
-                               $stylesheet = 'T3editor.PATH_codemirror + "../css/csscolors.css"';
+                               $stylesheet = array($this->codemirrorPath . '../css/csscolors.css');
                                break;
                        case self::MODE_XML:
-                               $stylesheet = 'T3editor.PATH_codemirror + "../css/xmlcolors.css"';
+                               $stylesheet = array($this->codemirrorPath . '../css/xmlcolors.css');
                                break;
                        case self::MODE_HTML:
-                               $stylesheet = 'T3editor.PATH_codemirror + "../css/xmlcolors.css", ' . 'T3editor.PATH_codemirror + "../css/jscolors.css", ' . 'T3editor.PATH_codemirror + "../css/csscolors.css"';
+                               $stylesheet = array($this->codemirrorPath . '../css/xmlcolors.css', $this->codemirrorPath . '../css/jscolors.css', $this->codemirrorPath . '../css/csscolors.css');
                                break;
                        case self::MODE_SPARQL:
-                               $stylesheet = 'T3editor.PATH_codemirror + "../css/sparqlcolors.css"';
+                               $stylesheet = array($this->codemirrorPath . '../css/sparqlcolors.css');
                                break;
                        case self::MODE_PHP:
-                               $stylesheet = 'T3editor.PATH_codemirror + "../contrib/php/css/phpcolors.css"';
+                               $stylesheet = array($this->codemirrorPath . '../contrib/php/css/phpcolors.css');
                                break;
                        case self::MODE_MIXED:
-                               $stylesheet = 'T3editor.PATH_codemirror + "../css/xmlcolors.css", ' . 'T3editor.PATH_codemirror + "../css/jscolors.css", ' . 'T3editor.PATH_codemirror + "../css/csscolors.css", ' . 'T3editor.PATH_codemirror + "../contrib/php/css/phpcolors.css"';
+                               $stylesheet = array($this->codemirrorPath . '../css/xmlcolors.css', $this->codemirrorPath . '../css/jscolors.css', $this->codemirrorPath . '../css/csscolors.css', $this->codemirrorPath . '../contrib/php/css/phpcolors.css');
                                break;
+                       default:
+                               $stylesheet = array();
                }
-               if ($stylesheet != '') {
-                       $stylesheet = '' . $stylesheet . ', ';
-               }
-               return '[' . $stylesheet . 'T3editor.PATH_t3e + "res/css/t3editor_inner.css"]';
+               $stylesheet[] = $this->relExtPath . 'res/css/t3editor_inner.css';
+               return json_encode($stylesheet);
        }
 
        /**
@@ -306,20 +308,38 @@ class T3editor implements \TYPO3\CMS\Core\SingletonInterface {
         */
        public function getCodeEditor($name, $class = '', $content = '', $additionalParams = '', $alt = '', array $hiddenfields = array()) {
                $code = '';
-               $this->editorCounter++;
                $class .= ' t3editor';
                $alt = htmlspecialchars($alt);
                if (!empty($alt)) {
                        $alt = ' alt="' . $alt . '"';
                }
-               $code .= '<div>' . '<textarea id="t3editor_' . $this->editorCounter . '" ' . 'name="' . $name . '" ' . 'class="' . $class . '" ' . $additionalParams . ' ' . $alt . '>' . htmlspecialchars($content) . '</textarea></div>';
-               $checked = $GLOBALS['BE_USER']->uc['disableT3Editor'] ? 'checked="checked"' : '';
-               $code .= '<div class="checkbox"><label for="t3editor_disableEditor_' . $this->editorCounter . '_checkbox"><input type="checkbox" class="checkbox t3editor_disableEditor" onclick="T3editor.toggleEditor(this);" name="t3editor_disableEditor" value="true" id="t3editor_disableEditor_' . $this->editorCounter . '_checkbox" ' . $checked . ' />' . $GLOBALS['LANG']->getLL('deactivate') . '</label></div>';
+               $code .=
+                       '<div class="t3editor">'
+                               . '<div class="t3e_wrap">'
+                                       . $this->getPreparedTemplate()
+                               . '</div>'
+                               . '<textarea '
+                                       . 'id="t3editor_' . $this->editorCounter . '" '
+                                       . 'name="' . $name . '" '
+                                       . 'class="' . $class . '" '
+                                       . $additionalParams . ' '
+                                       . $alt
+                                       . ' data-labels="' . htmlspecialchars(json_encode($GLOBALS['LANG']->getLabelsWithPrefix('js.', 'label_'))) . '"'
+                                       . ' data-instance-number="' . $this->editorCounter . '"'
+                                       . ' data-editor-path="' . htmlspecialchars($GLOBALS['BACK_PATH'] . $this->relExtPath) . '"'
+                                       . ' data-codemirror-path="' . htmlspecialchars($GLOBALS['BACK_PATH'] . $this->codemirrorPath) . '"'
+                                       . ' data-ajaxsavetype="' . htmlspecialchars($this->ajaxSaveType) . '"'
+                                       . ' data-parserfile="' . htmlspecialchars($this->getParserfileByMode($this->mode)) . '"'
+                                       . ' data-stylesheet="' . htmlspecialchars($this->getStylesheetByMode($this->mode)) . '"'
+                                       . '>' . htmlspecialchars($content)
+                               . '</textarea>'
+                       . '</div>';
                if (!empty($hiddenfields)) {
                        foreach ($hiddenfields as $name => $value) {
                                $code .= '<input type="hidden" ' . 'name="' . $name . '" ' . 'value="' . $value . '" />';
                        }
                }
+               $this->editorCounter++;
                return $code;
        }
 
index 40234d6..60bb426 100644 (file)
@@ -14,7 +14,7 @@
 /**
  * File edit for ext:t3editor
  */
-define('TYPO3/CMS/T3editor/FileEdit', ['jquery'], function ($) {
+define('TYPO3/CMS/T3editor/FileEdit', ['jquery', 'TYPO3/CMS/T3editor/T3editor'], function ($, T3editor) {
 
        $(document).ready(function() {
                $('.t3-icon-document-save, .t3-icon-document-save-close').each(function() {
@@ -29,16 +29,9 @@ define('TYPO3/CMS/T3editor/FileEdit', ['jquery'], function ($) {
                                                return false;
                                        }
                                        if ($(this).children('span').hasClass('t3-icon-document-save')) {
-                                               if (!T3editor.instances[0].disabled) {
-                                                       T3editor.instances[0].saveFunctionEvent();
-                                               } else {
-                                                       document.editform.submit();
-                                               }
+                                               T3editor.saveFunction(T3editor.instances[0]);
                                        } else {
-                                               if (!T3editor.instances[0].disabled) {
-                                                       T3editor.instances[0].updateTextareaEvent();
-                                               }
-                                               document.editform.submit();
+                                               T3editor.updateTextarea(T3editor.instances[0]);
                                        }
                                        return false;
                                });
diff --git a/typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/CompletionResult.js b/typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/CompletionResult.js
new file mode 100644 (file)
index 0000000..480a4e5
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Contains the CompletionResult class
+ */
+define('TYPO3/CMS/T3editor/Plugins/CodeCompletion/CompletionResult', ['jquery'], function ($) {
+       var CompletionResult = {
+               tsRef: null,
+               tsTreeNode: null
+       };
+
+       CompletionResult.init = function(config) {
+               CompletionResult.tsRef = config.tsRef;
+               CompletionResult.tsTreeNode = config.tsTreeNode;
+
+               return CompletionResult;
+       };
+
+       /**
+        * returns the type of the currentTsTreeNode
+        */
+       CompletionResult.getType = function() {
+               var val = CompletionResult.tsTreeNode.getValue();
+               if (CompletionResult.tsRef.isType(val)) {
+                       return CompletionResult.tsRef.getType(val);
+               }
+               return null;
+       };
+
+       /**
+        * returns a list of possible path completions (proposals), which is:
+        * a list of the children of the current TsTreeNode (= userdefined properties)
+        * and a list of properties allowed for the current object in the TsRef
+        * remove all words from list that don't start with the string in filter
+        *
+        * @param {String} filter beginning of the words contained in the proposal list
+        * @return {Array} an Array of Proposals
+        */
+       CompletionResult.getFilteredProposals = function(filter) {
+               var defined = [],
+                       propArr = [],
+                       childNodes = CompletionResult.tsTreeNode.getChildNodes(),
+                       value = CompletionResult.tsTreeNode.getValue();
+
+               // first get the childNodes of the Node (=properties defined by the user)
+               for (key in childNodes) {
+                       if (typeof childNodes[key].value !== 'undefined' && childNodes[key].value !== null) {
+                               var propObj = {};
+                               propObj.word = key;
+                               if (CompletionResult.tsRef.typeHasProperty(value, childNodes[key].name)) {
+                                       CompletionResult.tsRef.cssClass = 'definedTSREFProperty';
+                                       propObj.type = childNodes[key].value;
+                               } else {
+                                       propObj.cssClass = 'userProperty';
+                                       if (CompletionResult.tsRef.isType(childNodes[key].value)) {
+                                               propObj.type = childNodes[key].value;
+                                       } else {
+                                               propObj.type = '';
+                                       }
+                               }
+                               propArr.push(propObj);
+                               defined[key] = true;
+                       }
+               }
+
+               // then get the tsref properties
+               var props = CompletionResult.tsRef.getPropertiesFromTypeId(CompletionResult.tsTreeNode.getValue());
+               for (key in props) {
+                       // show just the TSREF properties - no properties of the array-prototype and no properties which have been defined by the user
+                       if (typeof props[key].value !== 'undefined' && defined[key] !== true) {
+                               var propObj = {};
+                               propObj.word = key;
+                               propObj.cssClass = 'undefinedTSREFProperty';
+                               propObj.type = props[key].value;
+                               propArr.push(propObj);
+                       }
+               }
+
+               var result = [],
+                       wordBeginning = '';
+
+               for (var i = 0; i < propArr.length; i++) {
+                       if (filter.length === 0) {
+                               result.push(propArr[i]);
+                               continue;
+                       }
+                       wordBeginning = propArr[i].word.substring(0, filter.length);
+                       if (wordBeginning.toLowerCase() === filter.toLowerCase()) {
+                               result.push(propArr[i]);
+                       }
+               }
+               return result;
+       };
+
+       return CompletionResult;
+});
\ No newline at end of file
diff --git a/typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/DescriptionPlugin.js b/typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/DescriptionPlugin.js
new file mode 100644 (file)
index 0000000..6f29250
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Descriptionbox plugin for the t3editor-codecompletion which displays the datatype
+ * and the desciption for each property displayed in the completionbox
+ **/
+define('TYPO3/CMS/T3editor/Plugins/CodeCompletion/DescriptionPlugin', [
+       'jquery',
+       'TYPO3/CMS/T3editor/Plugins/CodeCompletion/TsRef',
+       'TYPO3/CMS/T3editor/Plugins/CodeCompletion/TsParser'
+], function ($, TsRef, TsParser) {
+       var DescriptionPlugin = {
+               codeCompleteBox: null,
+               codemirror: null,
+               $descriptionBox: $('<div />', {class: 't3e_descriptionBox'}).hide()
+       };
+
+       DescriptionPlugin.init = function(pluginContext) {
+               DescriptionPlugin.codeCompleteBox = pluginContext.codeCompleteBox;
+               DescriptionPlugin.codemirror = pluginContext.codemirror;
+
+               DescriptionPlugin.codeCompleteBox.parent().append(DescriptionPlugin.$descriptionBox);
+       };
+
+       DescriptionPlugin.afterMouseOver = function(currWordObj, compResult) {
+               DescriptionPlugin.refreshBox(currWordObj, compResult);
+       };
+
+       DescriptionPlugin.afterKeyDown = function(currWordObj, compResult) {
+               DescriptionPlugin.refreshBox(currWordObj, compResult);
+       };
+
+       DescriptionPlugin.afterKeyUp = function(currWordObj, compResult) {
+               DescriptionPlugin.refreshBox(currWordObj, compResult);
+       };
+
+       DescriptionPlugin.afterCCRefresh = function(currWordObj, compResult) {
+               DescriptionPlugin.refreshBox(currWordObj, compResult);
+       };
+
+       DescriptionPlugin.descriptionLoaded = function(desc) {
+               $('#TSREF_description').html(desc);
+       };
+
+       DescriptionPlugin.endCodeCompletion = function() {
+               DescriptionPlugin.$descriptionBox.hide();
+       };
+
+       DescriptionPlugin.refreshBox = function(proposalObj, compResult) {
+               var type = compResult.getType();
+
+               if (type && type.properties[proposalObj.word]) {
+                       // first a container has to be built
+                       var html =
+                               '<div class="TSREF_type_label">Object-type: </div>'
+                               + '<div class="TSREF_type">'+type.typeId+'</div>'
+                               + '<div class="TSREF_type_label">Property-type: </div>'
+                               + '<div class="TSREF_type">'+type.properties[proposalObj.word].value
+                               + '</div><br />'
+                               + '<div class="TSREF_description_label">TSREF-description:</div>'
+                               + '<div id="TSREF_description"><span class="fa fa-spin fa-spinner" title="one moment please..."></span></div>';
+                       DescriptionPlugin.$descriptionBox.html(html);
+
+                       var prop = type.properties[proposalObj.word];
+
+                       // if there is another request for a description in the queue -> cancel it
+                       window.clearTimeout(this.lastTimeoutId);
+                       // add a request for a description onto the queue, but wait for 0.5 seconds
+                       // (look if user really wants to see the description of this property, if not -> don't load it)
+                       this.lastTimeoutId = setTimeout(function() {
+                               prop.getDescription(DescriptionPlugin.descriptionLoaded)
+                       }, 500);
+                       DescriptionPlugin.$descriptionBox.show();
+               } else if (proposalObj.type) {
+                       var html =
+                               '<div class="TSREF_type_label">TSREF-type: </div>'
+                               + '<div class="TSREF_type">'+proposalObj.type
+                               + '</div><br />';
+                       DescriptionPlugin.$descriptionBox.html(html);
+                       DescriptionPlugin.$descriptionBox.show();
+               } else {
+                       DescriptionPlugin.$descriptionBox.html('');
+                       DescriptionPlugin.$descriptionBox.hide();
+               }
+
+               DescriptionPlugin.$descriptionBox.scrollTop(0);
+               DescriptionPlugin.$descriptionBox.css({
+                       overflowY: 'scroll'
+               });
+               DescriptionPlugin.$descriptionBox.addClass('descriptionBox');
+
+               var addX = 18,
+                       leftOffset = parseInt(DescriptionPlugin.codeCompleteBox.css('left')) + parseInt(DescriptionPlugin.codeCompleteBox.width()) + addX;
+
+               DescriptionPlugin.$descriptionBox.css({
+                       top: DescriptionPlugin.codeCompleteBox.css('top'),
+                       left: leftOffset + 'px'
+               });
+
+               DescriptionPlugin.$descriptionBox.show();
+       };
+
+       return DescriptionPlugin;
+});
\ No newline at end of file
diff --git a/typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/TsCodeCompletion.js b/typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/TsCodeCompletion.js
new file mode 100644 (file)
index 0000000..737650d
--- /dev/null
@@ -0,0 +1,579 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Contains the TsCodeCompletion class
+ */
+define('TYPO3/CMS/T3editor/Plugins/CodeCompletion/TsCodeCompletion', [
+       'jquery',
+       'TYPO3/CMS/T3editor/Plugins/CodeCompletion/TsRef',
+       'TYPO3/CMS/T3editor/Plugins/CodeCompletion/TsParser',
+       'TYPO3/CMS/T3editor/Plugins/CodeCompletion/CompletionResult'
+], function ($, TsRef, TsParser, CompletionResult) {
+       var TsCodeCompletion = {
+               tsRef: TsRef,
+               outerDiv: null,
+               options: {
+                       ccWords : 10
+               },
+               index: 0,
+               currWord: 0,
+               cc_up: null,
+               cc_down: null,
+               mousePos: {
+                       x:0,
+                       y:0
+               },
+               proposals: null,
+               compResult: null,
+               cc: 0,
+               linefeedsPrepared: false,
+               currentCursorPosition: null,
+               extTsObjTree: {},
+               codemirror: null,
+               parser: null,
+               plugins: [
+                       'TYPO3/CMS/T3editor/Plugins/CodeCompletion/DescriptionPlugin'
+               ],
+               $codeCompleteBox: $('<div />', {class: 't3e_codeCompleteBox'}).hide()
+       };
+
+       /**
+        * All external templates along the rootline have to be loaded,
+        * this function retrieves the JSON code by comitting a AJAX request
+        */
+       TsCodeCompletion.loadExtTemplatesAsync = function() {
+               var id = TsCodeCompletion.getGetVar('id');
+               if (id === '') {
+                       return;
+               }
+               $.ajax({
+                       url: TYPO3.settings.ajaxUrls['CodeCompletion::loadTemplates'],
+                       data: {
+                               pageId: id
+                       },
+                       success: function(response) {
+                               TsCodeCompletion.extTsObjTree.c = response;
+                               TsCodeCompletion.resolveExtReferencesRec(TsCodeCompletion.extTsObjTree.c);
+                       }
+               });
+       };
+
+       /**
+        * Load registered plugins
+        */
+       TsCodeCompletion.loadPluginArray = function() {
+               $.ajax({
+                       url: TYPO3.settings.ajaxUrls['T3Editor::getPlugins'],
+                       success: function(response) {
+                               TsCodeCompletion.plugins = $.merge(TsCodeCompletion.plugins, response);
+                               // register an internal plugin
+                               TsCodeCompletion.loadPlugins();
+                       }
+               });
+       };
+
+       /**
+        * Load and initialize the plugins fetched by TsCodeCompletion.loadPluginArray()
+        */
+       TsCodeCompletion.loadPlugins = function() {
+               for (var i = 0; i < TsCodeCompletion.plugins.length; i++) {
+                       require([TsCodeCompletion.plugins[i]], function(plugin) {
+                               if (typeof plugin.init === 'function') {
+                                       plugin.init({
+                                               tsRef: TsCodeCompletion.tsRef,
+                                               codeCompleteBox: TsCodeCompletion.$codeCompleteBox,
+                                               codemirror: TsCodeCompletion.codemirror
+                                       });
+                               } else {
+                                       console.warn('Cannot initialize plugin ' + TsCodeCompletion.plugins[i] + ', missing "init" method');
+                               }
+                       });
+               }
+       };
+
+       /**
+        * Get the value of a given GET parameter
+        *
+        * @param name
+        * @return {String}
+        */
+       TsCodeCompletion.getGetVar = function(name) {
+               var get_string = document.location.search,
+                       return_value = '',
+                       value;
+
+               do { //This loop is made to catch all instances of any get variable.
+                       var name_index = get_string.indexOf(name + '=');
+                       if (name_index !== -1) {
+                               get_string = get_string.substr(name_index + name.length + 1, get_string.length - name_index);
+                               var end_of_value = get_string.indexOf('&');
+                               if (end_of_value !== -1) {
+                                       value = get_string.substr(0, end_of_value);
+                               } else {
+                                       value = get_string;
+                               }
+
+                               if (return_value === '' || value === '') {
+                                       return_value += value;
+                               } else {
+                                       return_value += ', ' + value;
+                               }
+                       }
+               } while (name_index !== -1);
+
+               // Restores all the blank spaces.
+               var space = return_value.indexOf('+');
+               while (space !== -1) {
+                       return_value = return_value.substr(0, space) + ' ' + return_value.substr(space + 1, return_value.length);
+                       space = return_value.indexOf('+');
+               }
+
+               return return_value;
+       };
+
+       /**
+        * Since the references are not resolved server side we have to do it client-side
+        * Benefit: less loading time due to less data which has to be transmitted
+        */
+       TsCodeCompletion.resolveExtReferencesRec = function(childNodes) {
+               for (var key in childNodes) {
+                       var childNode;
+                       // if the childnode has a value and there is a part of a reference operator ('<')
+                       // and it does not look like a html tag ('>')
+                       if (childNodes[key].v && childNodes[key].v[0] === '<' && childNodes[key].v.indexOf('>') === -1 ) {
+                               var path = $.trim(childNodes[key].v.replace(/</, ''));
+                               // if there are still whitespaces it's no path
+                               if (path.indexOf(' ') === -1) {
+                                       childNode = TsCodeCompletion.getExtChildNode(path);
+                                       // if the node was found - reference it
+                                       if (childNode !== null) {
+                                               childNodes[key] = childNode;
+                                       }
+                               }
+                       }
+                       // if there was no reference-resolving then we go deeper into the tree
+                       if (!childNode && childNodes[key].c) {
+                               TsCodeCompletion.resolveExtReferencesRec(childNodes[key].c);
+                       }
+               }
+       };
+
+       /**
+        * Get the child node of given path
+        *
+        * @param path
+        * @returns {Object}
+        */
+       TsCodeCompletion.getExtChildNode = function(path) {
+               var extTree = TsCodeCompletion.extTsObjTree,
+                       path = path.split('.'),
+                       pathSeg;
+
+               for (var i = 0; i < path.length; i++) {
+                       pathSeg = path[i];
+                       if (typeof extTree.c === 'undefined' || typeof extTree.c[pathSeg] === 'undefined') {
+                               return null;
+                       }
+                       extTree = extTree.c[pathSeg];
+               }
+               return extTree;
+       };
+
+       /**
+        * Replaces editor functions insertNewlineAtCursor and indentAtCursor
+        * with modified ones that only execute when codecompletion box is not shown
+        * @todo check if this works correctly after updating the codemirror base
+        */
+       TsCodeCompletion.prepareLinefeeds = function() {
+               TsCodeCompletion.codemirror.win.select.insertNewlineAtCursor_original = TsCodeCompletion.codemirror.win.select.insertNewlineAtCursor;
+               TsCodeCompletion.codemirror.win.select.insertNewlineAtCursor = function(window) {
+                       if (TsCodeCompletion.cc === 0) {
+                               TsCodeCompletion.codemirror.win.select.insertNewlineAtCursor_original(window);
+                       }
+               };
+               TsCodeCompletion.codemirror.editor.indentAtCursor_original = TsCodeCompletion.codemirror.editor.indentAtCursor;
+               TsCodeCompletion.codemirror.editor.indentAtCursor = function() {
+                       if (TsCodeCompletion.cc === 0) {
+                               TsCodeCompletion.codemirror.editor.indentAtCursor_original();
+                       }
+               };
+               TsCodeCompletion.linefeedsPrepared = true;
+       };
+
+       TsCodeCompletion.click = function() {
+               TsCodeCompletion.endAutoCompletion();
+       };
+
+       TsCodeCompletion.getFilter = function(cursorNode) {
+               if (cursorNode.currentText) {
+                       var filter = cursorNode.currentText.replace('.', '');
+                       return filter.replace(/\s/g, '');
+               }
+               return '';
+       };
+
+       TsCodeCompletion.getCursorNode = function() {
+               var cursorNode = TsCodeCompletion.codemirror.win.select.selectionTopNode(TsCodeCompletion.codemirror.win.document.body, false);
+               // cursorNode is null if the cursor is positioned at the beginning of the first line
+               if (cursorNode === null) {
+                       cursorNode = TsCodeCompletion.codemirror.editor.container.firstChild;
+               } else if (cursorNode.tagName === 'BR') {
+                       // if cursor is at the end of the line -> jump to beginning of the next line
+                       cursorNode = cursorNode.nextSibling;
+               }
+               return cursorNode;
+       };
+
+       TsCodeCompletion.getCurrentLine = function(cursor) {
+               var line = '',
+                       currentNode = cursor.start.node.parentNode;
+
+               while (currentNode.tagName !== 'BR') {
+                       if (currentNode.hasChildNodes()
+                               && currentNode.firstChild.nodeType === 3
+                               && currentNode.currentText.length > 0)
+                       {
+                               line = currentNode.currentText + line;
+                       }
+                       if (typeof currentNode.previousSibling === 'undefined') {
+                               break;
+                       } else {
+                               currentNode = currentNode.previousSibling;
+                       }
+               }
+               return line;
+       };
+
+       /**
+        * Eventhandler function executed after keystroke release
+        * triggers CC on pressed dot and typing on
+        *
+        * @param event fired prototype event object
+        * @type void
+        */
+       TsCodeCompletion.keyUp = function(e) {
+               // 190 = .
+               if (e.which === 190) {
+                       TsCodeCompletion.refreshCodeCompletion();
+               } else if (TsCodeCompletion.cc === 1) {
+                       // 38 = KEYUP, 40 = KEYDOWN
+                       if (e.which !== 40 && e.which !== 38) {
+                               if (e.which === 13) {
+                                       // return
+                                       e.preventDefault();
+                                       if (TsCodeCompletion.currWord !== -1) {
+                                               TsCodeCompletion.insertCurrWordAtCursor();
+                                       }
+                                       TsCodeCompletion.endAutoCompletion();
+                               } else {
+                                       TsCodeCompletion.refreshCodeCompletion();
+                               }
+                       }
+               }
+       };
+
+       /**
+        * Highlights entry in codecomplete box by id
+        *
+        * @param {Integer} id
+        */
+       TsCodeCompletion.mouseOver = function(id) {
+               TsCodeCompletion.highlightCurrWord(id);
+               for (var i = 0; i < TsCodeCompletion.plugins.length; i++) {
+                       require([TsCodeCompletion.plugins[i]], function(plugin) {
+                               if (typeof plugin.afterMouseOver === 'function') {
+                                       plugin.afterMouseOver(TsCodeCompletion.proposals[TsCodeCompletion.currWord], TsCodeCompletion.compResult);
+                               }
+                       });
+               }
+       };
+
+       /**
+        * Eventhandler function executed after keystroke release
+        * triggers CC on pressed dot and typing on
+        *
+        * @param event fired prototype event object
+        */
+       TsCodeCompletion.keyDown = function(e) {
+               if (!TsCodeCompletion.linefeedsPrepared) {
+                       TsCodeCompletion.prepareLinefeeds();
+               }
+               if (TsCodeCompletion.cc === 1) {
+                       if (e.which === 38) {
+                               // Arrow up: move up cursor in codecomplete box
+                               e.preventDefault();
+                               TsCodeCompletion.codeCompleteBoxMoveUpCursor();
+                               for (var i = 0; i < TsCodeCompletion.plugins.length; i++) {
+                                       require([TsCodeCompletion.plugins[i]], function(plugin) {
+                                               if (typeof plugin.afterKeyUp === 'function') {
+                                                       plugin.afterKeyUp(TsCodeCompletion.proposals[TsCodeCompletion.currWord], TsCodeCompletion.compResult);
+                                               }
+                                       });
+                               }
+                       } else if (e.which === 40) {
+                               // Arrow down: move down cursor in codecomplete box
+                               e.preventDefault();
+                               TsCodeCompletion.codeCompleteBoxMoveDownCursor();
+                               for (var i = 0; i < TsCodeCompletion.plugins.length; i++) {
+                                       require([TsCodeCompletion.plugins[i]], function(plugin) {
+                                               if (typeof plugin.afterKeyDown === 'function') {
+                                                       plugin.afterKeyDown(TsCodeCompletion.proposals[TsCodeCompletion.currWord], TsCodeCompletion.compResult);
+                                               }
+                                       });
+                               }
+                       } else if (e.which === 27 || e.which === 37 || e.which === 39) {
+                               // Esc, Arrow Left, Arrow Right: if codecomplete box is showing, hide it
+                               TsCodeCompletion.endAutoCompletion();
+                       } else if (e.which === 32 && (!e.ctrlKey || !e.metaKey)) {
+                               // space
+                               TsCodeCompletion.endAutoCompletion();
+                       } else if (e.which === 32 && (e.ctrlKey || e.metaKey)) {
+                               // CTRL + space
+                               TsCodeCompletion.refreshCodeCompletion();
+                       } else if (e.which === 8) {
+                               // backspace
+                               var cursorNode = TsCodeCompletion.codemirror.win.select.selectionTopNode(TsCodeCompletion.codemirror.win.document.body, false);
+                               if (cursorNode.innerHTML === '.') {
+                                       // force full refresh at keyUp
+                                       TsCodeCompletion.compResult = null;
+                               }
+                       }
+               } else { // if autocompletion is deactivated and ctrl+space is pressed
+                       if (e.which === 32 && (e.ctrlKey || e.metaKey)) {
+                               e.preventDefault();
+                               TsCodeCompletion.refreshCodeCompletion();
+                       }
+               }
+       };
+
+       /**
+        * Refreshes the code completion list based on the cursor's position
+        */
+       TsCodeCompletion.refreshCodeCompletion = function() {
+               // init vars for up/down moving in word list
+               TsCodeCompletion.cc_up = 0;
+               TsCodeCompletion.cc_down = TsCodeCompletion.options.ccWords-1;
+
+               // clear the last completion wordposition
+               TsCodeCompletion.currWord = -1;
+               TsCodeCompletion.codemirror.editor.highlightAtCursor();
+
+               // retrieves the node right to the cursor
+               TsCodeCompletion.currentCursorPosition = TsCodeCompletion.codemirror.win.select.markSelection(TsCodeCompletion.codemirror);
+               var cursorNode = TsCodeCompletion.getCursorNode();
+
+               // the cursornode has to be stored cause inserted breaks have to be deleted after pressing enter if the codecompletion is active
+               var filter = TsCodeCompletion.getFilter(cursorNode);
+
+               if (TsCodeCompletion.compResult === null || cursorNode.innerHTML === '.') {
+                       // TODO: implement cases: operatorCompletion reference/copy path completion (formerly found in getCompletionResults())
+                       var currentTsTreeNode = TsCodeCompletion.parser.buildTsObjTree(TsCodeCompletion.codemirror.editor.container.firstChild, cursorNode);
+                       TsCodeCompletion.compResult = CompletionResult.init({
+                               tsRef: TsRef,
+                               tsTreeNode: currentTsTreeNode
+                       });
+               }
+
+               TsCodeCompletion.proposals = TsCodeCompletion.compResult.getFilteredProposals(filter);
+
+               // if proposals are found - show box
+               if (TsCodeCompletion.proposals.length > 0) {
+                       // TODO: Drop instance dependency
+                       var index = 0;
+                       // make UL list of completation proposals
+                       var $ul = $('<ul />');
+                       for (var i = 0; i < TsCodeCompletion.proposals.length; i++) {
+                               var $li = $('<li />', {id: 'cc_word_' + i}).data('item', i).append(
+                                               $('<span />', {class: 'word_' + TsCodeCompletion.proposals[i].cssClass}).text(TsCodeCompletion.proposals[i].word)
+                                       ).on('click', function() {
+                                               TsCodeCompletion.insertCurrWordAtCursor($(this).data('item'));
+                                               TsCodeCompletion.endAutoCompletion();
+                                       }).on('mouseover', function() {
+                                               TsCodeCompletion.mouseOver($(this).data('item'));
+                                       });
+
+                               $ul.append($li);
+                       }
+
+                       // put HTML and show box
+                       TsCodeCompletion.$codeCompleteBox.html($ul);
+                       TsCodeCompletion.$codeCompleteBox.show();
+                       TsCodeCompletion.$codeCompleteBox.scrollTop(0);
+
+                       // init styles
+                       TsCodeCompletion.$codeCompleteBox.css({
+                               overflow: 'scroll',
+                               height: (TsCodeCompletion.options.ccWords * $('#cc_word_0').height()) + 'px'
+                       });
+
+                       var wrapOffset = $('.t3e_iframe_wrap', document).offset(),
+                               $cursorNode = $(cursorNode),
+                               nodeOffset = $cursorNode.offset();
+
+                       var leftpos = Math.round(wrapOffset.left + nodeOffset.left + cursorNode.offsetWidth) + 'px',
+                               toppos = Math.round($cursorNode.position().top + cursorNode.offsetHeight - $cursorNode.scrollTop()) + 'px';
+
+                       TsCodeCompletion.$codeCompleteBox.css({
+                               left: leftpos,
+                               top: toppos
+                       });
+
+                       // set flag to 1 - needed for continue typing word.
+                       TsCodeCompletion.cc = 1;
+
+                       // highlight first word in list
+                       TsCodeCompletion.highlightCurrWord(0);
+                       for (var i = 0; i < TsCodeCompletion.plugins.length; i++) {
+                               require([TsCodeCompletion.plugins[i]], function(plugin) {
+                                       if (typeof plugin.afterCCRefresh === 'function') {
+                                               plugin.afterCCRefresh(TsCodeCompletion.proposals[TsCodeCompletion.currWord], TsCodeCompletion.compResult);
+                                       }
+                               });
+                       }
+               } else {
+                       TsCodeCompletion.endAutoCompletion();
+               }
+       };
+
+       /**
+        * Stop code completion and call hooks
+        */
+       TsCodeCompletion.endAutoCompletion = function() {
+               TsCodeCompletion.cc = 0;
+               TsCodeCompletion.$codeCompleteBox.hide();
+               // force full refresh
+               TsCodeCompletion.compResult = null;
+               for (var i = 0; i < TsCodeCompletion.plugins.length; i++) {
+                       require([TsCodeCompletion.plugins[i]], function(plugin) {
+                               if (typeof plugin.endCodeCompletion === 'function') {
+                                       plugin.endCodeCompletion();
+                               }
+                       });
+               }
+       };
+
+       /**
+        * Move cursor in autocomplete box up
+        */
+       TsCodeCompletion.codeCompleteBoxMoveUpCursor = function() {
+               var id;
+               // if previous position was first or position not initialized - then move cursor to last word, else decrease position
+               if (TsCodeCompletion.currWord === 0 || TsCodeCompletion.currWord === -1) {
+                       id = TsCodeCompletion.proposals.length - 1;
+               } else {
+                       id = TsCodeCompletion.currWord - 1;
+               }
+               // hightlight new cursor position
+               TsCodeCompletion.highlightCurrWord(id);
+               // update id of first and last showing proposals and scroll box
+               if (TsCodeCompletion.currWord < TsCodeCompletion.cc_up || TsCodeCompletion.currWord === (TsCodeCompletion.proposals.length - 1)) {
+                       TsCodeCompletion.cc_up = TsCodeCompletion.currWord;
+                       TsCodeCompletion.cc_down = TsCodeCompletion.currWord + (TsCodeCompletion.options.ccWords - 1);
+                       if (TsCodeCompletion.cc_up === (TsCodeCompletion.proposals.length - 1)) {
+                               TsCodeCompletion.cc_down = TsCodeCompletion.proposals.length - 1;
+                               TsCodeCompletion.cc_up = TsCodeCompletion.cc_down - (TsCodeCompletion.options.ccWords - 1);
+                       }
+                       TsCodeCompletion.$codeCompleteBox.scrollTop = TsCodeCompletion.cc_up * 16;
+               }
+       };
+
+       /**
+        * Move cursor in codecomplete box down
+        */
+       TsCodeCompletion.codeCompleteBoxMoveDownCursor = function() {
+               var id;
+               // if previous position was last word in list - then move cursor to first word if not than      position ++
+               if (TsCodeCompletion.currWord === TsCodeCompletion.proposals.length - 1) {
+                       id = 0;
+               } else {
+                       id = TsCodeCompletion.currWord + 1;
+               }
+               // highlight new cursor position
+               TsCodeCompletion.highlightCurrWord(id);
+
+               // update id of first and last showing proposals and scroll box
+               if (TsCodeCompletion.currWord > TsCodeCompletion.cc_down || TsCodeCompletion.currWord === 0) {
+                       TsCodeCompletion.cc_down = TsCodeCompletion.currWord;
+                       TsCodeCompletion.cc_up = TsCodeCompletion.currWord - (TsCodeCompletion.options.ccWords - 1);
+                       if (TsCodeCompletion.cc_down == 0) {
+                               TsCodeCompletion.cc_up = 0;
+                               TsCodeCompletion.cc_down = TsCodeCompletion.options.ccWords - 1;
+                       }
+                       TsCodeCompletion.$codeCompleteBox.scrollTop = TsCodeCompletion.cc_up * 16;
+               }
+       };
+
+       /**
+        * Highlight the active word in the code completion list
+        *
+        * @param {Integer} id
+        */
+       TsCodeCompletion.highlightCurrWord = function(id) {
+               if (TsCodeCompletion.currWord !== -1) {
+                       $('#cc_word_' + TsCodeCompletion.currWord).removeClass('active');
+               }
+               $('#cc_word_' + id).addClass('active');
+               TsCodeCompletion.currWord = id;
+       };
+
+       /**
+        * Insert selected word into text from codecompletebox
+        */
+       TsCodeCompletion.insertCurrWordAtCursor = function() {
+               var word = TsCodeCompletion.proposals[TsCodeCompletion.currWord].word;
+               // tokenize current line
+               TsCodeCompletion.codemirror.editor.highlightAtCursor();
+               var select = TsCodeCompletion.codemirror.win.select;
+               var cursorNode = TsCodeCompletion.getCursorNode();
+
+               if (cursorNode.currentText
+                       && cursorNode.currentText !== '.'
+                       && $.trim(cursorNode.currentText) !== '' ) {
+                       // if there is some typed text already, left to the "." -> simply replace node content with the word
+                       cursorNode.innerHTML = word;
+                       cursorNode.currentText = word;
+                       select.setCursorPos(TsCodeCompletion.codemirror.editor.container, {node: cursorNode, offset: 0});
+               } else { // if there is no text there, insert the word at the cursor position
+                       TsCodeCompletion.codemirror.replaceSelection(word);
+               }
+       };
+
+       /**
+        * Save the mouse position
+        *
+        * @param {Event} e
+        */
+       TsCodeCompletion.saveMousePos = function(e) {
+               TsCodeCompletion.mousePos.x = e.clientX;
+               TsCodeCompletion.mousePos.y = e.clientY;
+       };
+
+       $(document).on('t3editor:init', function(e, codemirror, $outerDiv) {
+               $outerDiv.append(TsCodeCompletion.$codeCompleteBox);
+               TsCodeCompletion.outerDiv = $outerDiv;
+               TsCodeCompletion.codemirror = codemirror;
+
+               TsCodeCompletion.parser = TsParser.init(TsCodeCompletion.tsRef, TsCodeCompletion.extTsObjTree);
+               TsCodeCompletion.tsRef.loadTsrefAsync();
+
+               $(TsCodeCompletion.codemirror.win).on('mousemove', TsCodeCompletion.saveMousePos);
+               $(TsCodeCompletion.codemirror.win).on('keydown', TsCodeCompletion.keyDown);
+               $(TsCodeCompletion.codemirror.win).on('keyup', TsCodeCompletion.keyUp);
+
+               TsCodeCompletion.loadExtTemplatesAsync();
+               TsCodeCompletion.loadPluginArray();
+       });
+
+       return TsCodeCompletion;
+});
\ No newline at end of file
diff --git a/typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/TsParser.js b/typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/TsParser.js
new file mode 100644 (file)
index 0000000..d2960c0
--- /dev/null
@@ -0,0 +1,503 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Contains the TsCodeCompletion class
+ */
+define('TYPO3/CMS/T3editor/Plugins/CodeCompletion/TsParser', [
+       'jquery', 'TYPO3/CMS/T3editor/Plugins/CodeCompletion/TsRef'
+], function ($) {
+       var TsParser = {
+               typeId: null,
+               properties: null,
+               typeTree: [],
+               doc: null,
+               tsRef: null,
+               extTsObjTree: [],
+               tsTree: null
+       };
+
+       TsParser.init = function(tsRef, extTsObjTree) {
+               TsParser.tsRef = tsRef;
+               TsParser.extTsObjTree = extTsObjTree;
+               TsParser.tsTree = new TsParser.treeNode('_L_');
+
+               return TsParser;
+       };
+
+       TsParser.treeNode = function(nodeName) {
+               this.name = nodeName;
+               this.childNodes = [];
+               this.extPath = '';
+               this.value = '';
+               this.isExternal = false;
+
+               /**
+                * Returns local properties and the properties of the external templates
+                *
+                * @return {Array}
+                */
+               this.getChildNodes = function() {
+                       var node = this.getExtNode();
+                       if (node !== null && typeof node.c === 'object') {
+                               for (key in node.c) {
+                                       var tn = new TsParser.treeNode(key, this.tsObjTree);
+                                       tn.global = true;
+                                       tn.value = (node.c[key].v)? node.c[key].v : "";
+                                       tn.isExternal = true;
+                                       this.childNodes[key] = tn;
+                               }
+                       }
+                       return this.childNodes;
+               };
+
+               /**
+                * Returns the value of a node
+                *
+                * @returns {String}
+                */
+               this.getValue = function() {
+                       if (this.value) {
+                               return this.value;
+                       }
+                       var node = this.getExtNode();
+                       if (node && node.v) {
+                               return node.v;
+                       }
+
+                       var type = this.getNodeTypeFromTsref();
+                       if (type) {
+                               return type;
+                       }
+                       return '';
+               };
+
+               /**
+                * This method will try to resolve the properties recursively from right
+                * to left. If the node's value property is not set, it will look for the
+                * value of its parent node, and if there is a matching childProperty
+                * (according to the TSREF) it will return the childProperties value.
+                * If there is no value in the parent node it will go one step further
+                * and look into the parent node of the parent node,...
+                *
+                * @return {String}
+                */
+               this.getNodeTypeFromTsref = function() {
+                       var path = this.extPath.split('.'),
+                               lastSeg = path.pop();
+
+                       // attention: there will be recursive calls if necessary
+                       var parentValue = this.parent.getValue();
+                       if (parentValue) {
+                               if (TsParser.tsRef.typeHasProperty(parentValue, lastSeg)) {
+                                       var type = TsParser.tsRef.getType(parentValue);
+                                       return type.properties[lastSeg].value;
+                               }
+                       }
+                       return '';
+               };
+
+               /**
+                * Will look in the external ts-tree (static templates, templates on other pages)
+                * if there is a value or childproperties assigned to the current node.
+                * The method uses the extPath of the current node to navigate to the corresponding
+                * node in the external tree
+                *
+                * @return {Object}
+                */
+               this.getExtNode = function() {
+                       var extTree = TsParser.extTsObjTree,
+                               path,
+                               pathSeg;
+
+                       if (this.extPath === '') {
+                               return extTree;
+                       }
+                       path = this.extPath.split('.');
+
+                       for (var i = 0; i < path.length; i++) {
+                               pathSeg = path[i];
+                               if (typeof extTree.c === 'undefined' || typeof extTree.c[pathSeg] === 'undefined') {
+                                       return null;
+                               }
+                               extTree = extTree.c[pathSeg];
+                       }
+                       return extTree;
+               };
+       };
+
+       /**
+        * Check if there is an operator in the line and return it
+        * if there is none, return -1
+        *
+        * @return {(String|Number)}
+        */
+       TsParser.getOperator = function(line) {
+               var operators = [':=', '=<', '<', '>', '='];
+               for (var i = 0; i < operators.length; i++) {
+                       var op = operators[i];
+                       if (line.indexOf(op) !== -1) {
+                               // check if there is some HTML in this line (simple check, however it's the only difference between a reference operator and HTML)
+                               // we do this check only in case of the two operators "=<" and "<" since the delete operator would trigger our "HTML-finder"
+                               if ((op === '=<' || op === '<') && line.indexOf('>') > -1) {
+                                       // if there is a ">" in the line suppose there's some HTML
+                                       return '=';
+                               }
+                               return op;
+                       }
+               }
+               return -1;
+       };
+
+       /**
+        * Build the TypoScript object tree
+        *
+        * @param {Object} startNode
+        * @param {Object} cursorNode
+        */
+       TsParser.buildTsObjTree = function(startNode, cursorNode) {
+               TsParser.tsTree = new TsParser.treeNode('');
+               TsParser.tsTree.value = 'TLO';
+
+               function Stack() {
+               }
+               Stack.prototype = [];
+               Stack.prototype.lastElementEquals = function(str) {
+                       return this.length > 0 && this[this.length-1] === str;
+               };
+
+               Stack.prototype.popIfLastElementEquals = function(str) {
+                       if (this.lastElementEquals(str)) {
+                               this.pop();
+                               return true;
+                       }
+                       return false;
+               };
+
+               var currentNode = startNode,
+                       line = '',
+                       stack = new Stack(),
+                       prefixes = [],
+                       ignoreLine = false,
+                       insideCondition = false;
+
+               while (true) {
+                       if (currentNode.hasChildNodes() && currentNode.firstChild.nodeType === 3 && currentNode.currentText.length > 0) {
+                               var node = currentNode.currentText;
+                               if (node[0] === '#') {
+                                       stack.push('#');
+                               }
+                               if (node === '(') {
+                                       stack.push('(');
+                               }
+                               if (node[0] === '/' && node[1] === '*') {
+                                       stack.push('/*');
+                               }
+                               if (node === '{') {
+                                       // TODO: ignore whole block if wrong whitespaces in this line
+                                       if (TsParser.getOperator(line) === -1) {
+                                               stack.push('{');
+                                               prefixes.push($.trim(line));
+                                               ignoreLine = true;
+                                       }
+                               }
+                               // TODO: conditions
+                               // if condition starts -> ignore everything until end of condition
+                               if (node.search(/^\s*\[.*\]/) !== -1
+                                       && line.search(/\S/) === -1
+                                       && node.search(/^\s*\[(global|end|GLOBAL|END)\]/) === -1
+                                       && !stack.lastElementEquals('#')
+                                       && !stack.lastElementEquals('/*')
+                                       && !stack.lastElementEquals('{')
+                                       && !stack.lastElementEquals('(')
+                               ) {
+                                       insideCondition = true;
+                                       ignoreLine = true;
+                               }
+
+                               // if end of condition reached
+                               if (line.search(/\S/) === -1
+                                       && !stack.lastElementEquals('#')
+                                       && !stack.lastElementEquals('/*')
+                                       && !stack.lastElementEquals('(')
+                                       && (
+                                               (node.search(/^\s*\[(global|end|GLOBAL|END)\]/) !== -1
+                                               && !stack.lastElementEquals('{'))
+                                               || (node.search(/^\s*\[(global|GLOBAL)\]/) !== -1)
+                                       )
+                               ) {
+                                       insideCondition = false;
+                                       ignoreLine = true;
+                               }
+
+                               if (node === ')') {
+                                       stack.popIfLastElementEquals('(');
+                               }
+                               if (node[0] === '*' && node[1] === '/') {
+                                       stack.popIfLastElementEquals('/*');
+                                       ignoreLine = true;
+                               }
+                               if (node === '}') {
+                                       //no characters except whitespace allowed before closing bracket
+                                       var trimmedLine = line.replace(/\s/g, '');
+                                       if (trimmedLine === '') {
+                                               stack.popIfLastElementEquals('{');
+                                               if (prefixes.length > 0) {
+                                                       prefixes.pop();
+                                               }
+                                               ignoreLine = true;
+                                       }
+                               }
+                               if (!stack.lastElementEquals('#')) {
+                                       line += node;
+                               }
+                       } else {
+                               //end of line? divide line into path and text and try to build a node
+                               if (currentNode.tagName === 'BR') {
+                                       // ignore comments, ...
+                                       if (!stack.lastElementEquals('/*') && !stack.lastElementEquals('(') && !ignoreLine && !insideCondition) {
+                                               line = $.trim(line);
+                                               // check if there is any operator in this line
+                                               var op = TsParser.getOperator(line);
+                                               if (op !== -1) {
+                                                       // figure out the position of the operator
+                                                       var pos = line.indexOf(op);
+                                                       // the target objectpath should be left to the operator
+                                                       var path = line.substring(0, pos);
+                                                       // if we are in between curly brackets: add prefixes to object path
+                                                       if (prefixes.length > 0) {
+                                                               path = prefixes.join('.') + '.' + path;
+                                                       }
+                                                       // the type or value should be right to the operator
+                                                       var str = line.substring(pos + op.length, line.length);
+                                                       path = $.trim(path);
+                                                       str = $.trim(str);
+                                                       switch (op) { // set a value or create a new object
+                                                               case '=':
+                                                                       //ignore if path is empty or contains whitespace
+                                                                       if (path.search(/\s/g) === -1 && path.length > 0) {
+                                                                               TsParser.setTreeNodeValue(path, str);
+                                                                       }
+                                                                       break;
+                                                               case '=<': // reference to another object in the tree
+                                                                       // resolve relative path
+                                                                       if (prefixes.length > 0 && str.substr(0, 1) === '.') {
+                                                                               str = prefixes.join('.') + str;
+                                                                       }
+                                                                       //ignore if either path or str is empty or contains whitespace
+                                                                       if (path.search(/\s/g) === -1
+                                                                               && path.length > 0
+                                                                               && str.search(/\s/g) === -1
+                                                                               && str.length > 0
+                                                                       ) {
+                                                                               TsParser.setReference(path, str);
+                                                                       }
+                                                                       break;
+                                                               case '<': // copy from another object in the tree
+                                                                       // resolve relative path
+                                                                       if (prefixes.length > 0 && str.substr(0, 1) === '.') {
+                                                                               str = prefixes.join('.') + str;
+                                                                       }
+                                                                       //ignore if either path or str is empty or contains whitespace
+                                                                       if (path.search(/\s/g) === -1
+                                                                               && path.length > 0
+                                                                               && str.search(/\s/g) === -1
+                                                                               && str.length > 0
+                                                                       ) {
+                                                                               TsParser.setCopy(path, str);
+                                                                       }
+                                                                       break;
+                                                               case '>': // delete object value and properties
+                                                                       TsParser.deleteTreeNodeValue(path);
+                                                                       break;
+                                                               case ':=': // function operator
+                                                                       // TODO: function-operator
+                                                                       break;
+                                                       }
+                                               }
+                                       }
+                                       stack.popIfLastElementEquals('#');
+                                       ignoreLine = false;
+                                       line = '';
+                               }
+                       }
+                       // todo: fix problem: occurs if you type something, delete it with backspace and press ctrl+space
+                       // hack: cursor.start does not always return the node on the same level- so we have to check both
+                       // if (currentNode == cursor.start.node.parentNode || currentNode == cursor.start.node.previousSibling){
+                       // another problem: also the filter is calculated wrong, due to the buggy cursor, so this hack is useless
+                       if (currentNode === cursorNode) {
+                               break;
+                       } else {
+                               currentNode = currentNode.nextSibling;
+                       }
+               }
+               // when node at cursorPos is reached:
+               // save currentLine, currentTsTreeNode and filter if necessary
+               // if there is a reference or copy operator ('<' or '=<')
+               // return the treeNode of the path right to the operator,
+               // else try to build a path from the whole line
+               if (!stack.lastElementEquals('/*') && !stack.lastElementEquals('(') && !ignoreLine) {
+                       var currentLine = line,
+                               i = line.indexOf('<');
+
+                       if (i !== -1) {
+                               var path = line.substring(i+1, line.length);
+                               path = $.trim(path);
+                               if (prefixes.length > 0 && path.substr(0, 1) === '.') {
+                                       path = prefixes.join('.') + path;
+                               }
+                       } else {
+                               var path = line;
+                               if (prefixes.length > 0) {
+                                       path = prefixes.join('.') + '.' + path;
+                                       path = path.replace(/\s/g, '');
+                               }
+                       }
+                       var lastDot = path.lastIndexOf('.');
+                       path = path.substring(0, lastDot);
+               }
+               return TsParser.getTreeNode(path);
+       };
+
+       /**
+        * Iterates through the object tree, and creates treenodes
+        * along the path, if necessary
+        *
+        * @return {Object}
+        */
+       TsParser.getTreeNode = function(path) {
+               path = $.trim(path);
+               if (path.length === 0) {
+                       return TsParser.tsTree;
+               }
+               var aPath = path.split('.');
+
+               var subTree = TsParser.tsTree.childNodes,
+                       pathSeg,
+                       parent = TsParser.tsTree;
+
+               // step through the path from left to right
+               for (var i = 0; i < aPath.length; i++) {
+                       pathSeg = aPath[i];
+
+                       // if there isn't already a treenode
+                       if (typeof subTree[pathSeg] === 'undefined' || typeof subTree[pathSeg].childNodes === 'undefined') { // if this subpath is not defined in the code
+                               // create a new treenode
+                               subTree[pathSeg] = new TsParser.treeNode(pathSeg);
+                               subTree[pathSeg].parent = parent;
+                               // the extPath has to be set, so the TreeNode can retrieve the respecting node in the external templates
+                               var extPath = parent.extPath;
+                               if (extPath) {
+                                       extPath += '.';
+                               }
+                               extPath += pathSeg;
+                               subTree[pathSeg].extPath = extPath;
+                       }
+                       if (i === aPath.length - 1) {
+                               return subTree[pathSeg];
+                       }
+                       parent = subTree[pathSeg];
+                       subTree = subTree[pathSeg].childNodes;
+               }
+       };
+
+       /**
+        * Navigates to the respecting treenode,
+        * create nodes in the path, if necessary, and sets the value
+        */
+       TsParser.setTreeNodeValue = function(path, value) {
+               var treeNode = TsParser.getTreeNode(path);
+               // if we are inside a GIFBUILDER Object
+               if (treeNode.parent !== null && (treeNode.parent.value === "GIFBUILDER" || treeNode.parent.getValue() === "GMENU_itemState") && value === "TEXT") {
+                       value = 'GB_TEXT';
+               }
+               if (treeNode.parent !== null && (treeNode.parent.value === "GIFBUILDER" || treeNode.parent.getValue() === "GMENU_itemState") && value === "IMAGE") {
+                       value = 'GB_IMAGE';
+               }
+
+               // just override if it is a real objecttype
+               if (TsParser.tsRef.isType(value)) {
+                       treeNode.value = value;
+               }
+       };
+
+       /**
+        * Navigates to the respecting treenode,
+        * creates nodes if necessary, empties the value and childNodes-Array
+        */
+       TsParser.deleteTreeNodeValue = function(path) {
+               var treeNode = TsPatser.getTreeNode(path);
+               // currently the node is not deleted really, it's just not displayed cause value == null
+               // deleting it would be a cleaner solution
+               treeNode.value = null;
+               treeNode.childNodes = null;
+               treeNode = null;
+       };
+
+       /**
+        * Copies a reference of the treeNode specified by path2
+        * to the location specified by path1
+        */
+       TsParser.setReference = function(path1, path2) {
+               var path1arr = path1.split('.'),
+                       lastNodeName = path1arr[path1arr.length - 1],
+                       treeNode1 = TsParser.getTreeNode(path1),
+                       treeNode2 = TsParser.getTreeNode(path2);
+
+               if (treeNode1.parent !== null) {
+                       treeNode1.parent.childNodes[lastNodeName] = treeNode2;
+               } else {
+                       TsParser.tsTree.childNodes[lastNodeName] = treeNode2;
+               }
+       };
+
+       /**
+        * copies a treeNode specified by path2
+        * to the location specified by path1
+        */
+       TsParser.setCopy = function(path1, path2) {
+               this.clone = function(myObj) {
+                       if (typeof myObj !== 'object') {
+                               return myObj;
+                       }
+
+                       var myNewObj = {};
+                       for (var i in myObj) {
+                               // disable recursive cloning for parent object -> copy by reference
+                               if (i !== 'parent') {
+                                       if (typeof myObj[i] === 'object') {
+                                               myNewObj[i] = this.clone(myObj[i]);
+                                       } else {
+                                               myNewObj[i] = myObj[i];
+                                       }
+                               } else {
+                                       myNewObj.parent = myObj.parent;
+                               }
+                       }
+                       return myNewObj;
+               };
+
+               var path1arr = path1.split('.'),
+                       lastNodeName = path1arr[path1arr.length - 1],
+                       treeNode1 = TsParser.getTreeNode(path1),
+                       treeNode2 = TsParser.getTreeNode(path2);
+
+               if (treeNode1.parent !== null) {
+                       treeNode1.parent.childNodes[lastNodeName] = this.clone(treeNode2);
+               } else {
+                       TsParser.tsTree.childNodes[lastNodeName] = this.clone(treeNode2);
+               }
+       };
+
+       return TsParser;
+});
\ No newline at end of file
diff --git a/typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/TsRef.js b/typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/TsRef.js
new file mode 100644 (file)
index 0000000..bd8e099
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Contains the TsCodeCompletion class
+ */
+define('TYPO3/CMS/T3editor/Plugins/CodeCompletion/TsRef', ['jquery'], function ($) {
+       var TsRef = {
+               typeId: null,
+               properties: null,
+               typeTree: [],
+               doc: null
+       };
+
+       /**
+        * Prototypes a TS reference type object
+        *
+        * @param {String} typeId
+        */
+       TsRef.TsRefType = function(typeId) {
+               this.typeId = typeId;
+               this.properties = [];
+
+               // todo: types can have descriptions too!
+               this.getDescription = function() {
+               }
+       };
+
+       /**
+        * Prototypes a TS reference property object
+        *
+        * @param {String} typeId
+        * @param {String} name
+        * @param {String} value
+        */
+       TsRef.TsRefProperty = function(parentType, name, value) {
+               this.parentType = parentType;
+               this.name = name;
+               this.value = value;
+
+               this.getDescription = function(callBack) {
+                       var urlParameters = '&typeId=' + this.parentType + '&parameterName=' + this.name;
+                       $.ajax({
+                               url: TYPO3.settings.ajaxUrls['T3Editor_TSrefLoader::getDescription'],
+                               data: {
+                                       typeId: this.parentType,
+                                       parameterName: this.name
+                               },
+                               success: function(response) {
+                                       callBack(response);
+                               }
+                       });
+               }
+       };
+
+       /**
+        * Load available TypoScript reference
+        */
+       TsRef.loadTsrefAsync = function() {
+               $.ajax({
+                       url: TYPO3.settings.ajaxUrls['T3Editor_TSrefLoader::getTypes'],
+                       success: function(response) {
+                               TsRef.doc = response;
+                               TsRef.buildTree();
+                       }
+               });
+       };
+
+       /**
+        * Build the TypoScript reference tree
+        */
+       TsRef.buildTree = function() {
+               var typeTree = [];
+               for (var typeId in TsRef.doc) {
+                       var arr = TsRef.doc[typeId];
+                       TsRef.typeTree[typeId] = new TsRef.TsRefType(typeId);
+
+                       if (typeof arr['extends'] !== 'undefined') {
+                               TsRef.typeTree[typeId]['extends'] = arr['extends'];
+                       }
+                       for (propName in arr.properties) {
+                               var propType = arr.properties[propName].type;
+                               TsRef.typeTree[typeId].properties[propName] = new TsRef.TsRefProperty(typeId, propName, propType);
+                       }
+               }
+               for (var typeId in TsRef.typeTree) {
+                       if (typeof TsRef.typeTree[typeId]['extends'] !== 'undefined') {
+                               TsRef.addPropertiesToType(TsRef.typeTree[typeId], TsRef.typeTree[typeId]['extends'], 100);
+                       }
+               }
+       };
+
+       /**
+        * Adds properties to TypoScript types
+        *
+        * @param {String} addToType
+        * @param {String} addFromTypeNames
+        * @param {Number} maxRecDepth
+        */
+       TsRef.addPropertiesToType = function(addToType, addFromTypeNames, maxRecDepth) {
+               if (maxRecDepth < 0) {
+                       throw "Maximum recursion depth exceeded while trying to resolve the extends in the TSREF!";
+                       return;
+               }
+               var exts = addFromTypeNames.split(','),
+                       i;
+               for (i = 0; i < exts.length; i++) {
+                       // "Type 'array' which is used to extend 'undefined', was not found in the TSREF!"
+                       if (typeof TsRef.typeTree[exts[i]] !== 'undefined') {
+                               if (typeof TsRef.typeTree[exts[i]]['extends'] !== 'undefined') {
+                                       TsRef.addPropertiesToType(TsRef.typeTree[exts[i]], TsRef.typeTree[exts[i]]['extends'], maxRecDepth-1);
+                               }
+                               var properties = TsRef.typeTree[exts[i]].properties;
+                               for (propName in properties) {
+                                       // only add this property if it was not already added by a supertype (subtypes override supertypes)
+                                       if (typeof addToType.properties[propName] !== 'undefined') {
+                                               addToType.properties[propName] = properties[propName];
+                                       }
+                               }
+                       }
+               }
+       };
+
+       /**
+        * Get properties from given TypoScript type id
+        *
+        * @param {String} tId
+        * @return {Array}
+        */
+       TsRef.getPropertiesFromTypeId = function(tId) {
+               if (typeof TsRef.typeTree[tId] !== 'undefined') {
+                       // clone is needed to assure that nothing of the tsref is overwritten by user setup
+                       TsRef.typeTree[tId].properties.clone = function() {
+                               var result = [];
+                               for (key in this) {
+                                       result[key] = new TsRef.TsRefProperty(this[key].parentType, this[key].name, this[key].value);
+                               }
+                               return result;
+                       }
+                       return TsRef.typeTree[tId].properties;
+               }
+               return [];
+       };
+
+       /**
+        * Check if a property of a type exists
+        *
+        * @param {String} typeId
+        * @param {String} propertyName
+        * @return {Boolean}
+        */
+       TsRef.typeHasProperty = function(typeId, propertyName) {
+               return typeof TsRef.typeTree[typeId] !== 'undefined'
+                       && typeof TsRef.typeTree[typeId].properties[propertyName] !== 'undefined';
+       };
+
+       /**
+        * Get the type
+        *
+        * @param {String} typeId
+        * @return {Object}
+        */
+       TsRef.getType = function(typeId) {
+               return TsRef.typeTree[typeId];
+       };
+
+       /**
+        * Check if type exists in the type tree
+        *
+        * @param {String} typeId
+        * @return {Boolean}
+        */
+       TsRef.isType = function(typeId) {
+               return typeof TsRef.typeTree[typeId] !== 'undefined';
+       };
+
+       return TsRef;
+});
\ No newline at end of file
index adc0356..42f17b7 100644 (file)
 
 define('TYPO3/CMS/T3editor/T3editor', ['jquery'], function ($) {
 
-       var T3editor = {};
+       var T3editor = {
+               instances: {}
+       };
+
+       /**
+        * Get and initialize editors
+        */
+       T3editor.findAndInitializeEditors = function() {
+               $('div.t3editor').each(function(i) {
+                       T3editor.initializeEditor($(this), i);
+               });
+       };
+
+       /**
+        * Initialize an editor
+        */
+       T3editor.initializeEditor = function($editor, index) {
+               var $textarea = $editor.find('textarea'),
+                       options = {
+                               labels: $textarea.data('labels'),
+                               height: $textarea.height() + 'px',
+                               width: $textarea.width() + 'px',
+                               content: $textarea.val(),
+                               parserfile: $textarea.data('parserfile'),
+                               stylesheet: $textarea.data('stylesheet'),
+                               path: $textarea.data('codemirror-path'),
+                               saveFunction: T3editor.saveFunction,
+                               autoMatchParens: true,
+                               lineNumbers: true,
+                               originalTextarea: $textarea,
+                               ajaxSaveType: $textarea.data('ajaxsavetype')
+                       };
+
+               $editor.find('.t3e_statusbar_title').text($textarea.attr('alt'));
+               $editor.find('.t3e_statusbar_status').text('');
+
+               var codemirror = new CodeMirror($editor.find('.t3e_iframe_wrap')[0], options);
+               T3editor.initializeEditorEvents(codemirror);
+               T3editor.setAjaxSavetypeCallback(codemirror);
+
+               $editor.find('.t3e_modalOverlay').fadeOut({
+                       complete: function() {
+                               T3editor.resize(codemirror, $textarea.width(), $textarea.height());
+                               $(document).trigger('t3editor:init', [codemirror, $editor.find('.t3e_wrap')]);
+                               T3editor.instances[index] = codemirror;
+                               $textarea.hide();
+                       }
+               });
+       };
+
+       /**
+        * Initializes editor events
+        */
+       T3editor.initializeEditorEvents = function(codemirror) {
+               $('input[type="submit"], input[type="image"]').on('click', function(e) {
+                       codemirror.options.originalTextarea.val(codemirror.editor.getCode());
+               });
+
+               $(codemirror.win.document).on('keydown', function(e) {
+                       if ((e.ctrlKey || e.metaKey) && e.which === 122) { // 122 is F11
+                               e.preventDefault();
+                               T3editor.toggleFullscreen(codemirror);
+                       }
+               });
+       };
+
+       /**
+        * Set the ajax save callback
+        */
+       T3editor.setAjaxSavetypeCallback = function(codemirror) {
+               if (codemirror.options.ajaxSaveType !== '') {
+                       $(document).on('t3editor:save', function(e, data) {
+                               var params = Object.extend({
+                                       t3editor_savetype: codemirror.options.ajaxSaveType
+                               }, data.parameters);
+
+                               $.ajax({
+                                       url: TYPO3.settings.ajaxUrls['T3Editor::saveCode'],
+                                       data: params,
+                                       method: 'POST',
+                                       beforeSend: function() {
+                                               codemirror.options.originalTextarea.parent().find('.t3e_modalOverlay').fadeIn();
+                                       },
+                                       complete: function(jqXHR) {
+                                               var wasSuccessful = jqXHR.status === 200 && jqXHR.responseJSON.result === true;
+                                               codemirror.options.originalTextarea.parent().find('.t3e_modalOverlay').fadeOut();
+                                               T3editor.saveFunctionComplete(codemirror, wasSuccessful, jqXHR.responseJSON);
+                                       }
+                               });
+                       });
+               }
+       };
+
+       /**
+        * Save method called upon saving
+        */
+       T3editor.saveFunction = function(codemirror) {
+               if (!codemirror.options.ajaxSaveType || codemirror.options.ajaxSaveType === '') {
+                       return;
+               }
+
+               codemirror.options.originalTextarea.val(codemirror.getCode());
+
+               var params = codemirror.options.originalTextarea.get(0).form.serialize(true);
+               params = Object.extend({t3editor_disableEditor: 'false'}, params);
+
+               $(document).trigger('t3editor:save', {parameters: params, t3editor: this});
+       };
+
+       /**
+        * Method invoked by saveFunction() on completion
+        */
+       T3editor.saveFunctionComplete = function(codemirror, wasSuccessful, returnedData) {
+               if (wasSuccessful) {
+                       this.textModified = false;
+               } else {
+                       if (typeof returnedData.exceptionMessage !== 'undefined') {
+                               top.TYPO3.Notification.error(codemirror.labels.errorWhileSaving[0]['target'], returnedData.exceptionMessage);
+                       } else {
+                               top.TYPO3.Notification.error(codemirror.labels.errorWhileSaving[0]['target'], '');
+                       }
+               }
+       };
+
+       /**
+        * Updates the textarea
+        */
+       T3editor.updateTextarea = function(codemirror) {
+               codemirror.options.originalTextarea.val(codemirror.editor.getCode());
+       };
+
+       /**
+        * Resize the editor
+        */
+       T3editor.resize = function(codemirror, w, h) {
+               var height = (h - 1),
+                       width = (w + 11),
+                       $outerDiv = codemirror.options.originalTextarea.prev('.t3e_wrap'),
+                       $mirrorWrap = codemirror.options.originalTextarea.parents('div.t3editor').find('.t3e_iframe_wrap');
+
+               $outerDiv.height(h + 20).width(width);
+               $outerDiv.find('.t3e_modalOverlay').height(h).width(width);
+               $mirrorWrap.children().first().height(h).width(w - 13);
+       };
+
+       /**
+        * Toggle fullscreen mode of editor
+        */
+       T3editor.toggleFullscreen = function(codemirror) {
+               var $outerDiv = codemirror.options.originalTextarea.prev('.t3e_wrap'),
+                       $parent = $outerDiv.offsetParent(),
+                       parentEl = $parent.get(0),
+                       w, h;
+
+               if ($outerDiv.hasClass('t3e_fullscreen')) {
+                       $outerDiv.removeClass('t3e_fullscreen');
+                       w = parseInt(codemirror.options.width);
+                       h = parseInt(codemirror.options.height);
+                       $parent.css({overflow: ''});
+               } else {
+                       $outerDiv.addClass('t3e_fullscreen');
+                       w = parentEl.clientWidth;
+                       h = parentEl.clientHeight;
+                       $parent.css({overflow: 'hidden'}).scrollTop(0);
+               }
+
+               T3editor.resize(codemirror, w, h);
+       }
 
        /**
         * Convert all textareas to enable tab
@@ -30,11 +197,11 @@ define('TYPO3/CMS/T3editor/T3editor', ['jquery'], function ($) {
        /**
         * Initialize and return the T3editor object
         */
-       return function() {
-               $(document).ready(function() {
-                       T3editor.convertTextareasEnableTab();
-               });
+       $(document).ready(function() {
+               T3editor.findAndInitializeEditors();
+               T3editor.convertTextareasEnableTab();
+       });
 
-               return T3editor;
-       }();
+       TYPO3.T3editor = T3editor;
+       return T3editor;
 });
diff --git a/typo3/sysext/t3editor/res/jslib/t3editor.js b/typo3/sysext/t3editor/res/jslib/t3editor.js
deleted file mode 100644 (file)
index 644f87c..0000000
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-/* t3editor.js uses the Codemirror editor.
- */
-
-T3editor = T3editor || {};
-
-// collection of all t3editor instances on the current page
-T3editor.instances = {};
-
-// path to the editor ext dir
-// can be overwritten in class.tx_t3editor.php
-T3editor.PATH_t3e = "../../../sysext/t3editor/";
-T3editor.PATH_codemirror = "../../../contrib/codemirror/js/";
-
-
-function T3editor(textarea) {
-       var self = this;
-
-               // memorize the textarea
-       this.textarea = $(textarea);
-       var textareaDim = $(this.textarea).getDimensions();
-       this.textarea.hide();
-
-               // outer wrap around the whole t3editor
-       this.outerdiv = new Element("DIV", {
-               "class": "t3e_wrap"
-       });
-
-               // place the div before the textarea
-       this.textarea.parentNode.insertBefore(this.outerdiv, $(this.textarea));
-
-       this.outerdiv.update(T3editor.template);
-
-       this.modalOverlay = this.outerdiv.down('.t3e_modalOverlay');
-       this.modalOverlay.setStyle(this.outerdiv.getDimensions());
-       this.modalOverlay.setStyle({
-               opacity: 0.8
-       });
-
-       this.mirror_wrap = this.outerdiv.down('.t3e_iframe_wrap');
-       this.statusbar_wrap = this.outerdiv.down('.t3e_statusbar_wrap');
-       this.statusbar_title = this.outerdiv.down('.t3e_statusbar_title');
-       this.statusbar_status = this.outerdiv.down('.t3e_statusbar_status');
-
-       this.statusbar_title.update( this.textarea.readAttribute('alt') );
-       this.statusbar_status.update( '' );
-
-               // setting options
-       var options = {
-               height: ( textareaDim.height ) + 'px',
-               width: ( textareaDim.width + this.adjustWidth ) + 'px',
-               content: $(this.textarea).value,
-               parserfile: T3editor.parserfile,
-               stylesheet: T3editor.stylesheet,
-               path: T3editor.PATH_codemirror,
-               outerEditor: this,
-               saveFunction: this.saveFunction.bind(this),
-               initCallback: this.init.bind(this),
-               autoMatchParens: true,
-               lineNumbers: true,
-               onChange: this.onChange.bind(this)
-       };
-
-               // get the editor
-       this.mirror = new CodeMirror(this.mirror_wrap, options);
-       $(this.outerdiv).fire('t3editor:init', {t3editor: this});
-}
-
-T3editor.prototype = {
-               saveFunctionEvent: null,
-               saveButtons: null,
-               updateTextareaEvent: null,
-               adjustWidth: -30,
-               disabled: false,
-
-               init: function() {
-                       var textareaDim = $(this.textarea).getDimensions();
-                       // hide the textarea
-                       this.textarea.hide();
-
-                       this.attachEvents();
-                       this.resize(textareaDim.width + this.adjustWidth, textareaDim.height );
-
-                       this.modalOverlay.hide();
-                       $(this.outerdiv).fire('t3editor:initFinished', {t3editor: this});
-               },
-
-               attachEvents: function() {
-                       var that = this;
-
-                       // get the form object
-                       var form = $(this.textarea.form);
-                       this.saveButtons = form.getInputs('image', 'submit');
-
-                       // initialize ajax saving events
-                       this.saveFunctionEvent = this.saveFunction.bind(this);
-                       this.saveButtons.each(function(button) {
-                               Event.observe(button,'click',this.saveFunctionEvent);
-                       }.bind(this));
-
-                       this.updateTextareaEvent = this.updateTextarea.bind(this);
-
-                       Event.observe($(this.textarea.form), 'submit', this.updateTextareaEvent);
-
-                       Event.observe(this.mirror.win.document, 'keyup', function(event) {
-                               $(that.outerdiv).fire('t3editor:keyup', {t3editor: that, actualEvent: event});
-                       });
-                       Event.observe(this.mirror.win.document, 'keydown', function(event) {
-                               $(that.outerdiv).fire('t3editor:keydown', {t3editor: that, actualEvent: event});
-
-                               if ((event.ctrlKey || event.metaKey) && event.keyCode == 122) {
-                                       that.toggleFullscreen();
-                                       event.stop();
-                               }
-                       });
-                       Event.observe(this.mirror.win.document, 'click', function(event) {
-                               $(that.outerdiv).fire('t3editor:click', {t3editor: that, actualEvent: event});
-                       });
-               },
-
-               // indicates is content is modified and not safed yet
-               textModified: false,
-
-               // check if code in editor has been modified since last saving
-               checkTextModified: function() {
-                       if (!this.textModified) {
-                               this.textModified = true;
-                       }
-               },
-
-               updateTextarea: function(event) {
-                       this.textarea.value = this.mirror.getCode();
-               },
-
-               onChange: function() {
-                       var that = this;
-                       this.checkTextModified();
-                       $(that.outerdiv).fire('t3editor:change', {t3editor: that});
-               },
-
-               saveFunction: function(event) {
-                       if (!T3editor.ajaxSavetype || T3editor.ajaxSavetype == '') {
-                               return;
-                       }
-                       this.modalOverlay.show();
-                       this.updateTextarea(event);
-
-                       if (event) {
-                               Event.stop(event);
-                       }
-
-                       var params = $(this.textarea.form).serialize(true);
-                       params = Object.extend( {t3editor_disableEditor: 'false'}, params);
-
-                       $(this.outerdiv).fire('t3editor:save', {parameters: params, t3editor: this});
-
-               },
-
-               // callback if saving was successful
-               saveFunctionComplete: function(wasSuccessful,returnedData) {
-                       if (wasSuccessful) {
-                               this.textModified = false;
-                       } else {
-                               if (typeof returnedData.exceptionMessage != 'undefined') {
-                                       top.TYPO3.Notification.error(T3editor.lang.errorWhileSaving[0]['target'], returnedData.exceptionMessage);
-                               } else {
-                                       top.TYPO3.Notification.error(T3editor.lang.errorWhileSaving[0]['target'], '');
-                               }
-                       }
-                       this.modalOverlay.hide();
-               },
-
-               // toggle between the textarea and t3editor
-               toggleView: function(disable) {
-                       $(this.outerdiv).fire('t3editor:toggleView', {t3editor: this, disable: disable});
-                       this.disabled = disable;
-                       if (disable) {
-                               this.textarea.value = this.mirror.editor.getCode();
-                               this.outerdiv.hide();
-                               this.textarea.show();
-                               this.saveButtons.each(function(button) {
-                                       Event.stopObserving(button,'click',this.saveFunctionEvent);
-                               }.bind(this));
-                               Event.stopObserving($(this.textarea.form), 'submit', this.updateTextareaEvent);
-
-                       } else {
-                               this.mirror.editor.importCode(this.textarea.value);
-                               this.textarea.hide();
-                               this.outerdiv.show();
-                               this.saveButtons.each(function(button) {
-                                       this.saveFunctionEvent = this.saveFunction.bind(this);
-                                       Event.observe(button,'click',this.saveFunctionEvent);
-                               }.bind(this));
-                               Event.observe($(this.textarea.form), 'submit', this.updateTextareaEvent);
-                       }
-               },
-
-               resize: function(width, height) {
-                       if (this.outerdiv) {
-                               newheight = (height - 1);
-                               newwidth = (width + 11);
-                               if (Prototype.Browser.IE) newwidth = newwidth + 8;
-
-                               $(this.outerdiv).setStyle({
-                                       height: newheight + 'px',
-                                       width: newwidth + 'px'
-                               });
-
-                               $(this.mirror_wrap.firstChild).setStyle({
-                                       'height': ((height - 22) + 'px'),
-                                       'width': ((width - 13) + 'px')
-                               });
-
-                               $(this.modalOverlay).setStyle(this.outerdiv.getDimensions());
-
-                       }
-
-               },
-
-               // toggle between normal view and fullscreen mode
-               toggleFullscreen: function() {
-                       if (this.outerdiv.hasClassName('t3e_fullscreen')) {
-                               // turn fullscreen off
-
-                               // unhide the scrollbar of the body
-                               this.outerdiv.offsetParent.setStyle({
-                                       overflow: ''
-                               });
-
-                               this.outerdiv.removeClassName('t3e_fullscreen');
-                               h = this.textarea.getDimensions().height;
-                               w = this.textarea.getDimensions().width;
-
-                       } else {
-                                       // turn fullscreen on
-                               this.outerdiv.addClassName('t3e_fullscreen');
-                               h = this.outerdiv.offsetParent.getHeight();
-                               w = this.outerdiv.offsetParent.getWidth();
-
-                               // less scrollbar width
-                               w = w - 13;
-
-                               // hide the scrollbar of the body
-                               this.outerdiv.offsetParent.setStyle({
-                                       overflow: 'hidden'
-                               });
-                               this.outerdiv.offsetParent.scrollTop = 0;
-                       }
-                       this.resize(w, h);
-               }
-
-} // T3editor.prototype
-
-
-// ------------------------------------------------------------------------
-
-
-/**
- * toggle between enhanced editor (t3editor) and simple textarea
- */
-T3editor.toggleEditor = function(checkbox, index) {
-       if (!Prototype.Browser.MobileSafari) {
-               if (index == undefined) {
-                       if (top.TYPO3.Storage) {
-                               top.TYPO3.Storage.Persistent.set(
-                                       'disableT3Editor',
-                                       checkbox.checked
-                               );
-                       }
-                       $$('textarea.t3editor').each(
-                               function(textarea, i) {
-                                       T3editor.toggleEditor(checkbox, i);
-                               }
-                       );
-               } else {
-                       if (T3editor.instances[index] != undefined) {
-                               var t3e = T3editor.instances[index];
-                               t3e.toggleView(checkbox.checked);
-                       } else if (!checkbox.checked) {
-                               var t3e = new T3editor($$('textarea.t3editor')[index], index);
-                               T3editor.instances[index] = t3e;
-                       }
-               }
-       }
-}
-
-// ------------------------------------------------------------------------
-
-if (!Prototype.Browser.MobileSafari) {
-       // everything ready: turn textarea's into fancy editors
-       Event.observe(window, 'load',
-               function() {
-                       $$('textarea.t3editor').each(
-                               function(textarea, i) {
-                                       if ($('t3editor_disableEditor_' + (i + 1) + '_checkbox')
-                                       && !$('t3editor_disableEditor_' + (i + 1) + '_checkbox').checked) {
-                                               var t3e = new T3editor(textarea);
-                                               T3editor.instances[i] = t3e;
-                                       }
-                               }
-                       );
-
-                       if (T3editor.ajaxSavetype != "") {
-                               Event.observe(document, 't3editor:save',
-                                       function(event) {
-                                               var params = Object.extend({
-                                                       t3editor_savetype: T3editor.ajaxSavetype
-                                               }, event.memo.parameters);
-
-                                               new Ajax.Request(
-                                                       TYPO3.settings.ajaxUrls['T3Editor::saveCode'], {
-                                                               parameters: params,
-                                                               onComplete: function(ajaxrequest) {
-                                                                       var wasSuccessful = ajaxrequest.status === 200 && ajaxrequest.responseJSON.result === true;
-                                                                       event.memo.t3editor.saveFunctionComplete(wasSuccessful,ajaxrequest.responseJSON);
-                                                               }
-                                                       }
-                                               );
-                                       }
-                               );
-                       }
-               }
-       );
-}
diff --git a/typo3/sysext/t3editor/res/jslib/ts_codecompletion/completionresult.js b/typo3/sysext/t3editor/res/jslib/ts_codecompletion/completionresult.js
deleted file mode 100644 (file)
index f0f6801..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * @fileoverview contains the CompletionResult class
- */
-
-/**
- * @class this class post-processes the result from the codecompletion, so that it can be
- * displayed in the next step.
- * @constructor
- * @param tsRef the TsRef Tree
- * @param tsTreeNode the current Node in the codetree built by the parser
- * @return a new CompletionResult instance
- */
-var CompletionResult = function(tsRef,tsTreeNode) {
-       var currentTsTreeNode = tsTreeNode;
-       var tsRef = tsRef;
-
-       /**
-        * returns the type of the currentTsTreeNode
-        */
-       this.getType = function() {
-               var val = currentTsTreeNode.getValue();
-               if (tsRef.isType(val)) {
-                       return tsRef.getType(val);
-               } else {
-                       return null;
-               }
-       }
-
-       /**
-        * returns a list of possible path completions (proposals), which is:
-        * a list of the children of the current TsTreeNode (= userdefined properties)
-        * and a list of properties allowed for the current object in the TsRef
-        * remove all words from list that don't start with the string in filter
-        * @param {String} filter beginning of the words contained in the proposal list
-        * @returns an Array of Proposals
-        */
-       this.getFilteredProposals = function(filter) {
-
-               var defined = new Array();
-               var propArr = new Array();
-               var childNodes = currentTsTreeNode.getChildNodes();
-               var value = currentTsTreeNode.getValue();
-               // first get the childNodes of the Node (=properties defined by the user)
-               for (key in childNodes) {
-                       if (typeof(childNodes[key].value) != "undefined" && childNodes[key].value != null) {
-                               propObj = new Object();
-                               propObj.word = key;
-                               if(tsRef.typeHasProperty(value,childNodes[key].name)){
-                                       propObj.cssClass = 'definedTSREFProperty';
-                                       propObj.type = childNodes[key].value;
-                               } else {
-                                       propObj.cssClass = 'userProperty';
-                                       if (tsRef.isType(childNodes[key].value)) {
-                                               propObj.type = childNodes[key].value;
-                                       } else {
-                                               propObj.type = '';
-                                       }
-                               }
-                               propArr.push(propObj);
-                               defined[key] = true;
-                       }
-               }
-
-               // then get the tsref properties
-               var props = tsRef.getPropertiesFromTypeId(currentTsTreeNode.getValue());
-               for (key in props) {
-                       // show just the TSREF properties - no properties of the array-prototype and no properties which have been defined by the user
-                       if (props[key].value != null && defined[key]!=true) {
-                               propObj = new Object();
-                               propObj.word = key;
-                               propObj.cssClass = 'undefinedTSREFProperty';
-                               propObj.type = props[key].value;
-                               propArr.push(propObj);
-                       }
-               }
-
-               var result = [];
-               var wordBeginning = "";
-               for (var i=0; i < propArr.length;i++) {
-                       wordBeginning = propArr[i].word.substring(0, filter.length);
-                       if (filter == "" || filter == null || wordBeginning.toLowerCase() == filter.toLowerCase()) {
-                               result.push(propArr[i]);
-                       }
-               }
-               return result;
-       }
-}
diff --git a/typo3/sysext/t3editor/res/jslib/ts_codecompletion/descriptionPlugin.js b/typo3/sysext/t3editor/res/jslib/ts_codecompletion/descriptionPlugin.js
deleted file mode 100644 (file)
index 6e40a93..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * @class Descriptionbox plugin for the t3editor-codecompletion which displays the datatype
- * and the desciption for each property displayed in the completionbox
- * @constructor
- * @return A new DescriptionPlugin instance
- **/
-
-var DescriptionPlugin = function() {
-       var outerdiv;
-       var descriptionBox;
-       var completionBox;
-       var tsRef;
-       var pluginMeta;
-
-       this.init = function(pluginContext,plugin) {
-               pluginMeta = plugin;
-               outerdiv = pluginContext.outerdiv;
-               completionBox = pluginContext.codeCompleteBox;
-               tsRef = pluginContext.tsRef;
-               descriptionBox = new Element("DIV", {"class": "t3e_descriptionBox"});
-               descriptionBox.hide();
-               outerdiv.appendChild(descriptionBox);
-       }
-       this.afterMouseOver = function(currWordObj,compResult) {
-               refreshBox(currWordObj,compResult);
-       }
-       this.afterKeyDown = function(currWordObj,compResult) {
-               refreshBox(currWordObj,compResult);
-       }
-       this.afterKeyUp = function(currWordObj,compResult) {
-               refreshBox(currWordObj,compResult);
-       }
-       this.afterCCRefresh = function(currWordObj,compResult) {
-               refreshBox(currWordObj,compResult);
-       }
-       function descriptionLoaded(desc) {
-               $('TSREF_description').innerHTML = desc;
-       }
-
-       function refreshBox(proposalObj,compResult) {
-               var type = compResult.getType();
-
-               if (type && type.properties[proposalObj.word]) {
-                       // first a container has to be built
-                       descriptionBox.innerHTML  = '<div class="TSREF_type_label">Object-type: </div><div class="TSREF_type">'+type.typeId+'</div>';
-                       descriptionBox.innerHTML += '<div class="TSREF_type_label">Property-type: </div><div class="TSREF_type">'+type.properties[proposalObj.word].value+'</div><br/>';
-                       descriptionBox.innerHTML += '<div class="TSREF_description_label">TSREF-description:</div><div id="TSREF_description"><span class="fa fa-spin fa-spinner" title="one moment please..."></span></div>';
-                       var prop = type.properties[proposalObj.word];
-                       // if there is another request for a description in the queue -> cancel it
-
-                       window.clearTimeout(this.lastTimeoutId);
-                       // add a request for a description onto the queue, but wait for 0.5 seconds
-                       // (look if user really wants to see the description of this property, if not -> don't load it)
-                       this.lastTimeoutId = prop.getDescription.bind(prop).delay(0.5,descriptionLoaded);
-                       descriptionBox.show();
-               } else if (proposalObj.type) {
-                       descriptionBox.innerHTML = '<div class="TSREF_type_label">TSREF-type: </div><div class="TSREF_type">'+proposalObj.type+'</div><br/>';
-                       descriptionBox.show();
-               } else {
-                       descriptionBox.innerHTML = '';
-                       descriptionBox.hide();
-               }
-
-               descriptionBox.scrollTop = 0;
-               descriptionBox.style.overflowY = 'scroll';
-               descriptionBox.addClassName('descriptionBox');
-
-               var addX = 5;
-               if (!Prototype.Browser.Gecko) { // not firefox
-                       addX = 18;
-               }
-               var leftOffset = parseInt(completionBox.getStyle('left').gsub('px','')) + parseInt(completionBox.getStyle('width').gsub('px','')) + addX;
-               leftOffset += 'px';
-               descriptionBox.setStyle({
-                       top: completionBox.getStyle('top'),
-                       left: leftOffset
-               });
-       }
-
-       this.endCodeCompletion = function(){
-               descriptionBox.hide();
-       }
-}
\ No newline at end of file
diff --git a/typo3/sysext/t3editor/res/jslib/ts_codecompletion/tscodecompletion.js b/typo3/sysext/t3editor/res/jslib/ts_codecompletion/tscodecompletion.js
deleted file mode 100644 (file)
index f122cbf..0000000
+++ /dev/null
@@ -1,622 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * @fileoverview contains the TsCodeCompletion class
- */
-
-/**
- * Construct a new TsCodeCompletion object.
- * @class This is the main class of the codeCompletion.
- * it is directly invoked by the editor. It instantiates all other classes
- * manages the control flow and takes care of the completionbox
- *
- * @constructor
- * @param codeMirror codeMirror instance, for retrieving the cursor position
- * @param outerdiv div that contains the editor, for DOM manipulation
- * @return A new TsCodeCompletion instance
- */
-var TsCodeCompletion = function(codeMirror, outerdiv) {
-       // private Vars
-       var tsRef = new TsRef();
-       var mirror = codeMirror;
-       var options = {ccWords : 10};
-       // t3editor index (=0 if there is just one editor on the page, should be set from outside)
-       var index = 0;
-
-       var currWord = 0;
-       var cc_up;
-       var cc_down;
-       var mousePos = {x:0,y:0};
-       var proposals;
-       var compResult;
-       var cc = 0;
-       var linefeedsPrepared = false;
-       var currentCursorPosition = null;
-
-       Event.observe(document, 'mousemove', saveMousePos, false);
-
-       // load the external templates ts-setup into extTsObjTree
-       var extTsObjTree = new Object();
-       var parser = new TsParser(tsRef, extTsObjTree);
-       loadExtTemplatesAsync();
-
-       // @todo port plugin to t3editor.js
-
-       // plugin-array will be retrieved through AJAX from the conf array
-       // plugins can be attached by regular TYPO3-extensions
-       var plugins = [];
-
-       //      we add the description plugin here because its packed with the codecompletion currently
-       //      maybe we will swap it to an external plugin in future
-       var plugin = new Object();
-       plugin.extpath = T3editor.PATH_t3e;
-       plugin.classpath = 'res/jslib/ts_codecompletion/descriptionPlugin.js';
-       plugin.classname = 'DescriptionPlugin';
-
-       plugins.push(plugin);
-
-       var codeCompleteBox = new Element("DIV", {
-               "class": "t3e_codeCompleteBox"
-       });
-       codeCompleteBox.hide();
-       outerdiv.appendChild(codeCompleteBox);
-
-       //      load the external xml-reference
-       tsRef.loadTsrefAsync();
-
-       //      plugins will be provided with the pluginContext
-       var pluginContext = new Object();
-       pluginContext.outerdiv = outerdiv;
-       pluginContext.codeCompleteBox = codeCompleteBox;
-       pluginContext.tsRef = tsRef;
-       pluginContext.parser = parser;
-       pluginContext.plugins = plugins;
-       pluginContext.codeMirror = codeMirror;
-
-       //      should we use a pluginmanager so no for loops are required on each hook?
-       //      e.g. pluginmanager.call('afterKeyUp',....);
-       loadPluginArray();
-
-       /**
-        * loads the array of registered codecompletion plugins
-        * to register a plugin you have to add an array to the localconf
-        * $TYPO3_CONF_VARS['EXTCONF']['t3editor']['plugins'][] = array(
-        *      'extpath' => \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::siteRelPath($_EXTKEY),
-        *      'classpath' => 'js/my_plugin.js',
-        *      'classname'=> 'MyPlugin'
-        * );
-        */
-       function loadPluginArray() {
-               new Ajax.Request(
-                       TYPO3.settings.ajaxUrls['T3Editor::getPlugins'],
-                               {
-                               method: 'get',
-                               onSuccess: function(transport) {
-                                       var loadedPlugins = eval('('+ transport.responseText +')');
-                                       plugins = plugins.concat(loadedPlugins);
-                                       // register an internal plugin
-                                       loadPlugins();
-                               }
-                       }
-               );
-       }
-
-       /**
-        * instantiates all plugins and adds the instances to the plugin array
-        */
-       function loadPlugins() {
-               for (var i = 0; i < plugins.length; i++) {
-                       var script = document.createElement('script');
-                       script.setAttribute('type', 'text/javascript');
-                       script.setAttribute('src', plugins[i].extpath+plugins[i].classpath);
-                       document.getElementsByTagName('head')[0].appendChild(script);
-                       window.setTimeout(makeInstance.bind(this,plugins[i],i),1000);
-               }
-       }
-
-       /**
-        * makes a single plugin instance
-        */
-       function makeInstance(plugin, i) {
-               try {
-                       var localname = "plugins[" + i + "].obj";
-                       eval(localname+' = new ' + plugin.classname + '();');
-                       var obj = eval(localname);
-               } catch(e) {
-                       throw("error occurred while trying to make new instance of \"" + plugin.classname + "\"! maybe syntax error or wrong filepath?");
-                       return;
-               }
-               obj.init(pluginContext,plugin);
-       }
-
-       /**
-        * all external templates along the rootline have to be loaded,
-        * this function retrieves the JSON code by comitting a AJAX request
-        */
-       function loadExtTemplatesAsync() {
-               var urlParameters = '&pageId=' + getGetVar('id');
-               new Ajax.Request(
-                       TYPO3.settings.ajaxUrls['CodeCompletion::loadTemplates'],
-                       {
-                               method: 'get',
-                               parameters: urlParameters,
-                               onSuccess: function(transport) {
-                                       extTsObjTree.c = eval('('+ transport.responseText +')');
-                                       resolveExtReferencesRec(extTsObjTree.c);
-                               }
-                       }
-               );
-       }
-
-       /**
-        * since the references are not resolved server side we have to do it client-side
-        * benefit: less loading time due to less data which has to be transmitted
-        */
-       function resolveExtReferencesRec(childNodes) {
-               for(var key in childNodes) {
-                       var childNode;
-                       // if the childnode has a value and there is a parto of a reference operator ('<')
-                       // and it does not look like a html tag ('>')
-                       if (childNodes[key].v && childNodes[key].v[0] == '<'
-                        && childNodes[key].v.indexOf('>') == -1 ) {
-                               var path = childNodes[key].v.replace(/</,"").strip();
-                               // if there are still whitespaces its no path
-                               if (path.indexOf(' ') == -1) {
-                                       childNode = getExtChildNode(path);
-                                       // if the node was found - reference it
-                                       if (childNode != null) {
-                                               childNodes[key] = childNode;
-                                       }
-                               }
-                       }
-                       // if there was no reference-resolving then we go deeper into the tree
-                       if (!childNode && childNodes[key].c) {
-                               resolveExtReferencesRec(childNodes[key].c);
-                       }
-               }
-       }
-
-       function getExtChildNode(path) {
-               var extTree = extTsObjTree;
-               var path = path.split('.');
-               var pathSeg;
-               var i;
-               for ( i=0; i < path.length; i++) {
-                       pathSeg = path[i];
-                       if(extTree.c == null || extTree.c[pathSeg] == null) {
-                               return null;
-                       }
-                       extTree = extTree.c[pathSeg];
-               }
-               return extTree;
-       }
-
-       /**
-        * replaces editor functions insertNewlineAtCursor and indentAtCursor
-        * with modified ones that only execute when codecompletion box is not shown
-        */
-//     @todo check if this wokrs correctly after updating the codemirror base
-       function prepareLinefeeds() {
-               mirror.win.select.insertNewlineAtCursor_original = mirror.win.select.insertNewlineAtCursor;
-               mirror.win.select.insertNewlineAtCursor = function(window) {
-                       if (cc==0) {
-                               mirror.win.select.insertNewlineAtCursor_original(window);
-                       }
-               };
-               mirror.editor.indentAtCursor_original = mirror.editor.indentAtCursor;
-               mirror.editor.indentAtCursor = function() {
-                       if (cc==0) {
-                               mirror.editor.indentAtCursor_original();
-                       }
-               };
-               linefeedsPrepared = true;
-       }
-
-       /**
-        * Eventhandler function for mouseclicks
-        * ends the codecompletion
-        * @param event fired prototype event object
-        * @type void
-        */
-       this.click = function(event) {
-               endAutoCompletion();
-       }
-
-       function getFilter(cursorNode) {
-               if(cursorNode.currentText) {
-                       var filter = cursorNode.currentText.replace('.','');
-                       return filter.replace(/\s/g,"");
-               } else {
-                       return "";
-               }
-       }
-
-       function getCursorNode() {
-               var cursorNode = mirror.win.select.selectionTopNode(mirror.win.document.body, false);
-               // cursorNode is null if the cursor is positioned at the beginning of the first line
-               if (cursorNode == null) {
-                       cursorNode = mirror.editor.container.firstChild;
-               } else if (cursorNode.tagName=='BR') {
-                       // if cursor is at the end of the line -> jump to beginning of the next line
-                       cursorNode = cursorNode.nextSibling;
-               }
-               return cursorNode;
-       }
-
-       function getCurrentLine(cursor) {
-               var line = "";
-               var currentNode = cursor.start.node.parentNode;
-               while (currentNode.tagName !='BR') {
-                       if (currentNode.hasChildNodes()
-                                       && currentNode.firstChild.nodeType == 3
-                                       && currentNode.currentText.length > 0) {
-                               line = currentNode.currentText + line;
-                       }
-                       if (currentNode.previousSibling == null) {
-                               break;
-                       } else {
-                               currentNode = currentNode.previousSibling;
-                       }
-               }
-               return line;
-       }
-
-       /**
-        * Eventhandler function executed after keystroke release
-        * triggers CC on pressed dot and typing on
-        * @param event fired prototype event object
-        * @type void
-        */
-       this.keyUp = function(event) {
-               var keycode = event.keyCode;
-               if (keycode == 190) {
-                       refreshCodeCompletion();
-               } else if (cc == 1) {
-                       if (keycode != Event.KEY_DOWN && keycode != Event.KEY_UP) {
-                               refreshCodeCompletion();
-                       }
-               }
-       }
-
-       /**
-        * Eventhandler function executed after keystroke release
-        * triggers CC on pressed dot and typing on
-        * @param event fired prototype event object
-        * @type void
-        */
-       this.keyDown = function(event) {
-
-//             prepareLinefeeds() gets called the first time keyDown is executed.
-//             we have to put this here, cause in the constructor mirror.editor is not yet loaded
-               if (!linefeedsPrepared) {
-                       prepareLinefeeds();
-               }
-               var keycode = event.keyCode;
-               if (cc == 1) {
-                       if (keycode == Event.KEY_UP) {
-                               // arrow up: move up cursor in codecomplete box
-                               event.stop();
-                               codeCompleteBoxMoveUpCursor();
-                               for (var i=0; i<plugins.length; i++) {
-                                       if (plugins[i].obj && plugins[i].obj.afterKeyUp) {
-                                               plugins[i].obj.afterKeyUp(proposals[currWord],compResult);
-                                       }
-                               }
-
-                       } else if (keycode == Event.KEY_DOWN) {
-                               // Arrow down: move down cursor in codecomplete box
-                               event.stop();
-                               codeCompleteBoxMoveDownCursor();
-                               for (var i=0; i<plugins.length; i++){
-                                       if (plugins[i].obj && plugins[i].obj.afterKeyDown) {
-                                               plugins[i].obj.afterKeyDown(proposals[currWord],compResult);
-                                       }
-                               }
-
-                       } else if (keycode == Event.KEY_ESC || keycode == Event.KEY_LEFT || keycode== Event.KEY_RIGHT) {
-                               // Esc, Arrow Left, Arrow Right: if codecomplete box is showing, hide it
-                               endAutoCompletion();
-
-                       } else if (keycode == Event.KEY_RETURN) {
-                               event.stop();
-                               if (currWord != -1) {
-                                       insertCurrWordAtCursor();
-                               }
-                               endAutoCompletion();
-
-                       } else if (keycode == 32 && !event.ctrlKey) {
-                               endAutoCompletion();
-
-                       } else if (keycode == 32 && event.ctrlKey) {
-                               refreshCodeCompletion();
-
-                       } else if (keycode == Event.KEY_BACKSPACE) {
-                               var cursorNode = mirror.win.select.selectionTopNode(mirror.win.document.body, false);
-                               if (cursorNode.innerHTML == '.') {
-                                       // force full refresh at keyUp
-                                       compResult = null;
-                               }
-                       }
-
-               } else { // if autocompletion is deactivated and ctrl+space is pressed
-                       if (keycode == 32 && event.ctrlKey) {
-                               event.stop();
-                               refreshCodeCompletion();
-                       }
-               }
-       }
-
-       function refreshCodeCompletion() {
-               // init vars for up/down moving in word list
-               cc_up = 0;
-               cc_down = options.ccWords-1;
-
-               // clear the last completion wordposition
-               currWord = -1;
-               mirror.editor.highlightAtCursor();
-
-               // retrieves the node right to the cursor
-               currentCursorPosition = mirror.win.select.markSelection(mirror.win);
-               cursorNode = getCursorNode();
-
-               // the cursornode has to be stored cause inserted breaks have to be deleted after pressing enter if the codecompletion is active
-               var filter = getFilter(cursorNode);
-
-               if (compResult == null || cursorNode.innerHTML == '.') {
-                       // TODO: implement cases: operatorCompletion reference/copy path completion (formerly found in getCompletionResults())
-                       var currentTsTreeNode = parser.buildTsObjTree(mirror.editor.container.firstChild, cursorNode);
-                       compResult = new CompletionResult(tsRef,currentTsTreeNode);
-               }
-
-               proposals = compResult.getFilteredProposals(filter);
-
-               // if proposals are found - show box
-               if (proposals.length > 0) {
-
-                       // make UL list of completation proposals
-                       var html = '<ul>';
-                       for (i = 0; i < proposals.length; i++) {
-                               html += '<li style="height:16px;vertical-align:middle;" ' +
-                               'id="cc_word_' + i + '" ' +
-                               'onclick="T3editor.instances[' + index + '].tsCodeCompletion.insertCurrWordAtCursor(' + i + ');T3editor.instances[' + index + '].tsCodeCompletion.endAutoCompletion();" ' +
-                               'onmouseover="T3editor.instances[' + index + '].tsCodeCompletion.onMouseOver(' + i + ',event);">' +
-                               '<span class="word_' + proposals[i].cssClass + '">' +
-                               proposals[i].word +
-                               '</span></li>';
-                       }
-                       html += '</ul>';
-
-                       // put HTML and show box
-                       codeCompleteBox.innerHTML = html;
-                       codeCompleteBox.show();
-                       codeCompleteBox.scrollTop = 0;
-
-                       // init styles
-                       codeCompleteBox.style.overflowY = 'scroll';
-                       codeCompleteBox.style.height = (options.ccWords * ($("cc_word_0").offsetHeight)) + 'px';
-
-                       var leftpos = (Position.cumulativeOffset($$('.t3e_iframe_wrap')[index])[0] + Position.cumulativeOffset(cursorNode)[0] + cursorNode.offsetWidth) + 'px';
-                       var toppos = (Position.cumulativeOffset(cursorNode)[1] + cursorNode.offsetHeight - Element.cumulativeScrollOffset(cursorNode)[1]) + 'px';
-                       codeCompleteBox.setStyle({left: leftpos, top: toppos});
-
-                       // set flag to 1 - needed for continue typing word.
-                       cc = 1;
-
-                       // highlight first word in list
-                       highlightCurrWord(0);
-                       for (var i=0;i<plugins.length;i++) {
-                               if (plugins[i].obj && plugins[i].obj.afterCCRefresh) {
-                                       plugins[i].obj.afterCCRefresh(proposals[currWord],compResult);
-                               }
-                       }
-               } else {
-                       endAutoCompletion();
-               }
-       }
-
-       /**
-        * hides codecomplete box and resets completionResult
-        * afterwards the interceptor method endCodeCompletion gets called
-        * @type void
-        */
-       this.endAutoCompletion = function() {
-               endAutoCompletion();
-       }
-
-       function endAutoCompletion(){
-               cc = 0;
-               codeCompleteBox.hide();
-               // force full refresh
-               compResult = null;
-               for (var i=0;i<plugins.length;i++) {
-                       if (plugins[i].obj && plugins[i].obj.endCodeCompletion) {
-                               plugins[i].obj.endCodeCompletion();
-                       }
-               }
-       }
-
-       /**
-        * move cursor in autcomplete box up
-        */
-       function codeCompleteBoxMoveUpCursor() {
-               // if previous position was first or position not initialized - then move cursor to last word, else decrease position
-               if (currWord == 0 || currWord == -1) {
-                       var id = proposals.length - 1;
-               } else {
-                       var id = currWord - 1;
-               }
-               // hightlight new cursor position
-               highlightCurrWord(id);
-               // update id of first and last showing proposals and scroll box
-               if (currWord < cc_up || currWord == (proposals.length - 1)) {
-                       cc_up = currWord;
-                       cc_down = currWord + (options.ccWords - 1);
-                       if (cc_up === (proposals.length - 1)) {
-                               cc_down = proposals.length - 1;
-                               cc_up = cc_down - (options.ccWords - 1);
-                       }
-                       codeCompleteBox.scrollTop = cc_up * 16;
-               }
-       }
-
-       /**
-        * move cursor in codecomplete box down
-        */
-       function codeCompleteBoxMoveDownCursor() {
-               // if previous position was last word in list - then move cursor to first word if not than      position ++
-               if (currWord == proposals.length - 1) {
-                       var id = 0;
-               } else {
-                       var id = currWord + 1;
-               }
-               // highlight new cursor position
-               highlightCurrWord(id);
-
-               // update id of first and last showing proposals and scroll box
-               if (currWord > cc_down || currWord == 0) {
-                       cc_down = currWord;
-                       cc_up = currWord - (options.ccWords - 1);
-                       if (cc_down == 0) {
-                               cc_up = 0;
-                               cc_down = options.ccWords - 1;
-                       }
-                       codeCompleteBox.scrollTop = cc_up * 16;
-               }
-       }
-
-       function saveMousePos(event){
-               mousePos.x = event.clientX;
-               mousePos.y = event.clientY;
-       }
-
-       /**
-        * highlights entry in codecomplete box by id
-        * @param {int} id
-        * @type void
-        */
-       this.onMouseOver = function(id,event){
-               highlightCurrWord(id,event);
-               for(var i=0;i<plugins.length;i++){
-                       if(plugins[i].obj && plugins[i].obj.afterMouseOver) {
-                               plugins[i].obj.afterMouseOver(proposals[currWord],compResult);
-                       }
-               }
-       }
-
-       function highlightCurrWord(id,event) {
-               // if it is a mouseover event
-               if(event){
-                       // if mousecoordinates haven't changed -> mouseover was triggered by scrolling of the result list -> don't highlight another word (return)
-                       if(mousePos.x == event.clientX && mousePos.y == event.clientY) {
-                               return;
-                       }
-                       mousePos.x = event.clientX;
-                       mousePos.y = event.clientY;
-               }
-               if (currWord != -1) {
-                       $('cc_word_' + currWord).className = '';
-               }
-               $('cc_word_' + id).className = 'active';
-               currWord = id;
-       }
-
-       /**
-        * Insert the currently selected item in the proposal list
-        * of the codecompletion box into the editor div at cursor position
-        * @type void
-        * @see #highlightCurrWord
-        */
-       this.insertCurrWordAtCursor = function(){
-               insertCurrWordAtCursor();
-       }
-
-       /**
-        * insert selected word into text from codecompletebox
-        */
-       function insertCurrWordAtCursor() {
-               var word = proposals[currWord].word;
-               // tokenize current line
-               mirror.editor.highlightAtCursor();
-               var select = mirror.win.select;
-               var cursorNode = getCursorNode();
-
-               if (cursorNode.currentText
-                       && cursorNode.currentText != '.'
-                       && cursorNode.currentText.strip() != '' ) {
-                       // if there is some typed text already, left to the "." -> simply replace node content with the word
-                       cursorNode.innerHTML = word;
-                       cursorNode.currentText = word;
-                       select.setCursorPos(mirror.editor.container, {node: cursorNode, offset: 0});
-               } else { // if there is no text there, insert the word at the cursor position
-                       mirror.replaceSelection(word);
-               }
-       }
-
-       /**
-        * retrieves the get-variable with the specified name
-        */
-       function getGetVar(name){
-               var get_string = document.location.search;
-               var return_value = '';
-               var value;
-               do { //This loop is made to catch all instances of any get variable.
-                       var name_index = get_string.indexOf(name + '=');
-                       if (name_index != -1) {
-                               get_string = get_string.substr(name_index + name.length + 1, get_string.length - name_index);
-                               end_of_value = get_string.indexOf('&');
-                               if (end_of_value != -1) {
-                                       value = get_string.substr(0, end_of_value);
-                               } else {
-                                       value = get_string;
-                               }
-
-                               if (return_value == '' || value == '') {
-                                       return_value += value;
-                               } else {
-                                       return_value += ', ' + value;
-                               }
-                       }
-               } while(name_index != -1);
-
-               // Restores all the blank spaces.
-               var space = return_value.indexOf('+');
-               while(space != -1) {
-                       return_value = return_value.substr(0, space) + ' ' +
-                       return_value.substr(space + 1, return_value.length);
-                       space = return_value.indexOf('+');
-               }
-
-               return(return_value);
-       }
-}
-
-document.observe('t3editor:init', function(event) {
-       that = event.memo.t3editor;
-       that.tsCodeCompletion = new TsCodeCompletion(that.mirror, that.outerdiv);
-});
-
-document.observe('t3editor:keyup', function(event) {
-       that = event.memo.t3editor;
-       if (that.tsCodeCompletion) that.tsCodeCompletion.keyUp(event.memo.actualEvent);
-});
-
-document.observe('t3editor:keydown', function(event) {
-       that = event.memo.t3editor;
-       if (that.tsCodeCompletion) that.tsCodeCompletion.keyDown(event.memo.actualEvent);
-});
-
-document.observe('t3editor:click', function(event) {
-       that = event.memo.t3editor;
-       if (that.tsCodeCompletion) that.tsCodeCompletion.click(event.memo.actualEvent);
-});
diff --git a/typo3/sysext/t3editor/res/jslib/ts_codecompletion/tsparser.js b/typo3/sysext/t3editor/res/jslib/ts_codecompletion/tsparser.js
deleted file mode 100644 (file)
index ddf2ac8..0000000
+++ /dev/null
@@ -1,503 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * @fileoverview contains the TsParser class and the TreeNode helper class
- */
-
-/**
- * Construct a new TsParser object.
- * @class This class takes care of the parsing and builds the codeTree
- *
- * @constructor
- * @param tsRef typoscript reference tree
- * @param extTsObjTree codeTree for all typoscript templates
- *                      excluding the current one.
- * @return A new TsParser instance
- */
-var TsParser = function(tsRef,extTsObjTree){
-
-       /**
-        * @class data structure for the nodes of the code tree
-        * mainly used for retrieving the externals templates childnodes
-        * @constructor
-        * @param {String} name
-        */
-       function TreeNode(nodeName){
-               this.name = nodeName;
-               //this.tsObjTree = tsObjTree;
-               this.childNodes = new Array();
-               //has to be set, so the node can retrieve the childnodes of the external templates
-               this.extPath = "";
-               // the TS-objecttype ID (TSREF)
-               this.value = "";
-               //this.extTsObjTree = null;
-               // current template or external template
-               this.isExternal = false;
-
-               /**
-                * returns local properties and the properties of the external templates
-                * @returns {Array} ChildNodes
-                */
-               this.getChildNodes = function(){
-                       var node = this.getExtNode();
-                       if(node){
-                               for(key in node.c){
-                                       var tn = new TreeNode(key,this.tsObjTree);
-                                       tn.global = true;
-                                       tn.value = (node.c[key].v)? node.c[key].v : "";
-                                       tn.isExternal = true;
-                                       this.childNodes[key] = tn;
-                               }
-                       }
-                       return this.childNodes;
-               }
-
-               this.getValue = function(){
-                       if(this.value) {
-                               return this.value;
-                       } else {
-                               var node = this.getExtNode();
-                               if(node && node.v) {
-                                       return node.v;
-                               } else {
-                                       var type = this.getNodeTypeFromTsref();
-                                       if(type) {
-                                               return type;
-                                       } else {
-                                               return '';
-                               }
-                       }
-               }
-               }
-
-               /**
-                * This method will try to resolve the properties recursively from right
-                * to left. If the node's value property is not set, it will look for the
-                * value of its parent node, and if there is a matching childProperty
-                * (according to the TSREF) it will return the childProperties value.
-                * If there is no value in the parent node it will go one step further
-                * and look into the parent node of the parent node,...
-                */
-               this.getNodeTypeFromTsref = function(){
-                       var path = this.extPath.split('.');
-                       var lastSeg = path.pop();
-                       // attention: there will be recursive calls if necessary
-                       var parentValue = this.parent.getValue();
-                       if(parentValue){
-                               if(tsRef.typeHasProperty(parentValue,lastSeg)){
-                                       var type = tsRef.getType(parentValue);
-                                       var propertyTypeId = type.properties[lastSeg].value;
-                                       return propertyTypeId;
-                               }
-                       }
-                       return '';
-               }
-
-               /**
-                * Will look in the external ts-tree (static templates, templates on other pages)
-                * if there is a value or childproperties assigned to the current node.
-                * The method uses the extPath of the current node to navigate to the corresponding
-                * node in the external tree
-                */
-               this.getExtNode = function(){
-                       var extTree = extTsObjTree;
-                       var path = this.extPath.split('.');
-                       var pathSeg;
-                       if (path == "") {
-                       return extTree;
-                       }
-                       var i;
-                       for(i=0;i<path.length;i++){
-                               pathSeg = path[i];
-                               if(extTree.c == null || extTree.c[pathSeg] == null) {
-                                       return null;
-                               }
-                               extTree = extTree.c[pathSeg];
-                       }
-                       return extTree;
-               }
-
-       }
-
-       // the top level treenode
-       var tsTree = new TreeNode("_L_");
-       var currentLine = "";
-
-       /**
-        * build Tree of TsObjects from beginning of editor to actual cursorPos
-        * and store it in tsTree.
-        * also store string from cursor position to the beginning of the line in currentLine
-        * and return the reference to the last path before the cursor position in currentTsTreeNode
-        * @param startNode DOM Node containing the first word in the editor
-        * @param cursorNode DOM Node containing the word at cursor position
-        * @return currentTsTreeNode
-        */
-       this.buildTsObjTree = function(startNode, cursorNode){
-               return buildTsObjTree(startNode, cursorNode);
-       }
-       function buildTsObjTree(startNode, cursorNode) {
-               var currentNode = startNode;
-               var line = "";
-               tsTree = new TreeNode("");
-               tsTree.value = "TLO";
-               function Stack() {
-               }
-
-               Stack.prototype = new Array();
-
-               Stack.prototype.lastElementEquals = function(str) {
-                       if (this.length > 0 && this[this.length-1]==str) {
-                               return true;
-                       }else {
-                               return false;
-                       }
-               }
-
-               Stack.prototype.popIfLastElementEquals = function(str) {
-                       if(this.length > 0 && this[this.length-1]==str) {
-                               this.pop();
-                               return true;
-                       }else {
-                               return false;
-                       }
-               }
-
-               var stack = new Stack();
-               var prefixes = new Array();
-               var ignoreLine = false;
-               //var cursorReached = false;
-               var insideCondition = false;
-
-               while(true) {
-                       if(currentNode.hasChildNodes() && currentNode.firstChild.nodeType==3 && currentNode.currentText.length>0) {
-                               node = currentNode.currentText;
-                               if (node[0] == '#') {
-                                       stack.push('#');
-                               }
-                               if (node == '(') {
-                                       stack.push('(');
-                               }
-                               if (node[0] == '/' && node[1]=='*') {
-                                       stack.push('/*');
-                               }
-                               if (node == '{') {
-                                       // TODO: ignore whole block if wrong whitespaces in this line
-                                       if (getOperator(line)==-1) {
-                                               stack.push('{');
-                                               prefixes.push(line.strip());
-                                               ignoreLine = true;
-                                       }
-                               }
-                               // TODO: conditions
-                               // if condition starts -> ignore everything until end of condition
-                               if (node.search(/^\s*\[.*\]/) != -1
-                                               && line.search(/\S/) == -1
-                                               && node.search(/^\s*\[(global|end|GLOBAL|END)\]/) == -1
-                                               && !stack.lastElementEquals('#')
-                                               && !stack.lastElementEquals('/*')
-                                               && !stack.lastElementEquals('{')
-                                               && !stack.lastElementEquals('(')
-                               ) {
-                                       insideCondition = true;
-                                       ignoreLine = true;
-                               }
-
-                               // if end of condition reached
-                               if (line.search(/\S/) == -1
-                                               && !stack.lastElementEquals('#')
-                                               && !stack.lastElementEquals('/*')
-                                               && !stack.lastElementEquals('(')
-                                               && (
-                                                               (node.search(/^\s*\[(global|end|GLOBAL|END)\]/) != -1
-                                                                               && !stack.lastElementEquals('{'))
-                                                                               || (node.search(/^\s*\[(global|GLOBAL)\]/) != -1)
-                                               )
-                               ) {
-                                       insideCondition = false;
-                                       ignoreLine = true;
-                               }
-
-                               if (node == ')') {
-                                       stack.popIfLastElementEquals('(');
-                               }
-                               if (node[0] == '*' && node[1]=='/') {
-                                       stack.popIfLastElementEquals('/*');
-                                       ignoreLine = true;
-                               }
-                               if (node == '}') {
-                                       //no characters except whitespace allowed before closing bracket
-                                       trimmedLine = line.replace(/\s/g,"");
-                                       if (trimmedLine=="") {
-                                               stack.popIfLastElementEquals('{');
-                                               if (prefixes.length>0) prefixes.pop();
-                                               ignoreLine = true;
-                                       }
-                               }
-                               if (!stack.lastElementEquals('#')) {
-                                       line += node;
-                               }
-
-                       } else {
-                               //end of line? divide line into path and text and try to build a node
-                               if (currentNode.tagName == "BR") {
-                                       // ignore comments, ...
-                                       if(!stack.lastElementEquals('/*') && !stack.lastElementEquals('(') && !ignoreLine && !insideCondition) {
-                                               line = line.strip();
-                                               // check if there is any operator in this line
-                                               var op = getOperator(line);
-                                               if (op != -1) {
-                                                       // figure out the position of the operator
-                                                       var pos = line.indexOf(op);
-                                                       // the target objectpath should be left to the operator
-                                                       var path = line.substring(0,pos);
-                                                       // if we are in between curly brackets: add prefixes to object path
-                                                       if (prefixes.length>0) {
-                                                               path = prefixes.join('.') + '.' + path;
-                                                       }
-                                                       // the type or value should be right to the operator
-                                                       var str = line.substring(pos+op.length, line.length);
-                                                       path = path.strip();
-                                                       str = str.strip();
-                                                       switch(op) { // set a value or create a new object
-                                                       case '=':
-                                                               //ignore if path is empty or contains whitespace
-                                                               if (path.search(/\s/g) == -1 && path.length > 0) {
-                                                               setTreeNodeValue(path, str);
-                                                               }
-                                                               break;
-                                                       case '=<': // reference to another object in the tree
-                                                                // resolve relative path
-                                                               if(prefixes.length > 0 && str.substr(0, 1) == '.') {
-                                                                       str = prefixes.join('.') + str;
-                                                               }
-                                                               //ignore if either path or str is empty or contains whitespace
-                                                               if (path.search(/\s/g) == -1
-                                                                && path.length > 0
-                                                                && str.search(/\s/g) == -1
-                                                                && str.length > 0) {
-                                                               setReference(path, str);
-                                                               }
-                                                               break;
-                                                       case '<': // copy from another object in the tree
-                                                               // resolve relative path
-                                                               if(prefixes.length > 0 && str.substr(0, 1) == '.') {
-                                                                       str = prefixes.join('.') + str;
-                                                               }
-                                                               //ignore if either path or str is empty or contains whitespace
-                                                               if (path.search(/\s/g) == -1
-                                                                && path.length > 0
-                                                                && str.search(/\s/g) == -1
-                                                                && str.length > 0) {
-                                                               setCopy(path, str);
-                                                               }
-                                                               break;
-                                                       case '>': // delete object value and properties
-                                                               deleteTreeNodeValue(path);
-                                                               break;
-                                                       case ':=': // function operator
-                                                               // TODO: function-operator
-                                                               break;
-                                                       }
-                                               }
-                                       }
-                                       stack.popIfLastElementEquals('#');
-                                       ignoreLine = false;
-                                       line = "";
-                               }
-                       }
-                       // todo: fix problem: occurs if you type something, delete it with backspace and press ctrl+space
-                       // hack: cursor.start does not always return the node on the same level- so we have to check both
-                       // if (currentNode == cursor.start.node.parentNode || currentNode == cursor.start.node.previousSibling){
-                       // another problem: also the filter is calculated wrong, due to the buggy cursor, so this hack is useless
-                       if (currentNode == cursorNode) {
-                               break;
-                       } else {
-                               currentNode = currentNode.nextSibling;
-                       }
-               }
-               // when node at cursorPos is reached:
-               // save currentLine, currentTsTreeNode and filter if necessary
-               // if there is a reference or copy operator ('<' or '=<')
-               // return the treeNode of the path right to the operator,
-               // else try to build a path from the whole line
-
-               if(!stack.lastElementEquals('/*') && !stack.lastElementEquals('(') && !ignoreLine) {
-                       currentLine = line;
-                       var i = line.indexOf('<');
-                       if (i != -1) {
-                               var path = line.substring(i+1, line.length);
-                               path = path.strip();
-                               if ( prefixes.length > 0 && path.substr(0,1) == '.') {
-                                       path = prefixes.join('.') + path;
-                               }
-                       } else {
-                               var path = line;
-                               if (prefixes.length>0) {
-                                       path = prefixes.join('.') + '.' + path;
-                                       path = path.replace(/\s/g,"");
-                               }
-                       }
-                       var lastDot = path.lastIndexOf(".");
-                       path = path.substring(0, lastDot);
-               }
-               return getTreeNode(path);
-       }
-
-       /**
-        * check if there is an operator in the line and return it
-        * if there is none, return -1
-        */
-       function getOperator(line) {
-               var operators = new Array(":=", "=<", "<", ">", "=");
-               for (var i=0; i<operators.length; i++) {
-                       var op = operators[i];
-                       if (line.indexOf(op) != -1) {
-                               // check if there is some HTML in this line (simple check, however it's the only difference between a reference operator and HTML)
-                               // we do this check only in case of the two operators "=<" and "<" since the delete operator would trigger our "HTML-finder"
-                               if((op == "=<" || op == "<") && line.indexOf(">") != -1){
-                                       // if there is a ">" in the line suppose there's some HTML
-                                       return "=";
-                               }
-                               return op;
-                       }
-               }
-               return -1;
-       }
-
-       /**
-        * iterates through the object tree, and creates treenodes
-        * along the path, if necessary
-        */
-       function getTreeNode(path){
-               var aPath = path.strip().split(".");
-               if (aPath == "") {
-                       return tsTree;
-               }
-               var subTree = tsTree.childNodes;
-               var pathSeg;
-               var parent = tsTree;
-               var currentNodePath = '';
-               // step through the path from left to right
-               for(i=0;i<aPath.length;i++){
-                       pathSeg = aPath[i];
-
-                       // if there isn't already a treenode
-                       if(subTree[pathSeg] == null || subTree[pathSeg].childNodes == null){ // if this subpath is not defined in the code
-                               // create a new treenode
-                               subTree[pathSeg] = new TreeNode(pathSeg);
-                               subTree[pathSeg].parent = parent;
-                               // the extPath has to be set, so the TreeNode can retrieve the respecting node in the external templates
-                               var extPath = parent.extPath;
-                               if(extPath) {
-                                       extPath += '.';
-                               }
-                               extPath += pathSeg;
-                               subTree[pathSeg].extPath = extPath;
-                       }
-                       if(i==aPath.length-1){
-                               return subTree[pathSeg];
-                       }
-                       parent = subTree[pathSeg];
-                       subTree = subTree[pathSeg].childNodes;
-               }
-       }
-
-       /**
-        * navigates to the respecting treenode,
-        * create nodes in the path, if necessary, and sets the value
-        */
-       function setTreeNodeValue(path, value) {
-               var treeNode = getTreeNode(path);
-               // if we are inside a GIFBUILDER Object
-               if(treeNode.parent != null && (treeNode.parent.value == "GIFBUILDER" || treeNode.parent.getValue() == "GMENU_itemState") && value == "TEXT") {
-                       value = "GB_TEXT";
-               }
-               if(treeNode.parent != null && (treeNode.parent.value == "GIFBUILDER" || treeNode.parent.getValue() == "GMENU_itemState") && value == "IMAGE") {
-                       value = "GB_IMAGE";
-               }
-               // just override if it is a real objecttype
-               if (tsRef.isType(value)) {
-                       treeNode.value = value;
-               }
-       }
-
-       /**
-        * navigates to the respecting treenode,
-        * creates nodes if necessary, empties the value and childNodes-Array
-        */
-       function deleteTreeNodeValue(path) {
-               var treeNode = getTreeNode(path);
-               // currently the node is not deleted really, its just not displayed cause value == null
-               // deleting it would be a cleaner solution
-               treeNode.value = null;
-               treeNode.childNodes = null;
-               treeNode = null;
-       }
-
-       /**
-        * copies a reference of the treeNode specified by path2
-        * to the location specified by path1
-        */
-       function setReference(path1, path2) {
-               path1arr = path1.split('.');
-               lastNodeName = path1arr[path1arr.length-1];
-               var treeNode1 = getTreeNode(path1);
-               var treeNode2 = getTreeNode(path2);
-               if(treeNode1.parent != null) {
-                       treeNode1.parent.childNodes[lastNodeName] = treeNode2;
-               } else {
-                       tsTree.childNodes[lastNodeName] = treeNode2;
-               }
-       }
-
-       /**
-        * copies a treeNode specified by path2
-        * to the location specified by path1
-        */
-       function setCopy(path1,path2){
-               this.clone = function(myObj) {
-                       if (myObj == null || typeof(myObj) != 'object') {
-                               return myObj;
-                       }
-
-                       var myNewObj = new Object();
-
-                       for(var i in myObj){
-                               // disable recursive cloning for parent object -> copy by reference
-                               if(i != "parent"){
-                                       if (typeof myObj[i] == 'object') {
-                                               myNewObj[i] = clone(myObj[i]);
-                                       } else {
-                                               myNewObj[i] = myObj[i];
-                                       }
-                               } else {
-                                       myNewObj.parent = myObj.parent;
-                               }
-                       }
-                       return myNewObj;
-               }
-               var path1arr = path1.split('.');
-               var lastNodeName = path1arr[path1arr.length-1];
-               var treeNode1 = getTreeNode(path1);
-               var treeNode2 = getTreeNode(path2);
-
-               if(treeNode1.parent != null) {
-                       treeNode1.parent.childNodes[lastNodeName] = this.clone(treeNode2);
-                       //treeNode1.parent.childNodes[lastNodeName].extTsObjTree = extTsObjTree;
-               } else {
-                       tsTree.childNodes[lastNodeName] = this.clone(treeNode2);
-                       //tsTree[lastNodeName].extTsObjTree = extTsObjTree;
-               }
-       }
-}
\ No newline at end of file
diff --git a/typo3/sysext/t3editor/res/jslib/ts_codecompletion/tsref.js b/typo3/sysext/t3editor/res/jslib/ts_codecompletion/tsref.js
deleted file mode 100644 (file)
index af15daf..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * @fileoverview contains the TsRef class
- * and the TsRefProperty and TsRefType helper classes
- */
-
-/**
- * @class Represents a TsRefProperty in the tree
- *
- * @constructor
- */
-var TsRefProperty = function(parentType,name,value) {
-       this.parentType = parentType;
-       this.name = name;
-       this.value = value;
-       var descriptionCache = null;
-       this.getDescription = function(callBack) {
-               if(descriptionCache == null){
-                       var urlParameters = '&typeId=' + this.parentType + '&parameterName=' + this.name;
-
-                       new Ajax.Request(
-                               TYPO3.settings.ajaxUrls['T3Editor_TSrefLoader::getDescription'],
-                               {
-                                       method: 'get',
-                                       parameters: urlParameters,
-                                       onSuccess: function(transport) {
-                                               descriptionCache = transport.responseText;
-                                               callBack(transport.responseText);
-                                       }
-                               }
-                       );
-               } else {
-                       callBack(descriptionCache);
-               }
-       }
-}
-
-/**
- * @class Represents a TsRefType in the tree
- *
- * @constructor
- */
-var TsRefType = function(typeId) {
-       this.typeId = typeId;
-       this.properties = new Array();
-
-       // todo: types can have descriptions too!
-       this.getDescription = function() {
-       }
-}
-
-/**
- * Construct a new TsRef object.
- * @class This class receives the TsRef from the server and represents it as a tree
- * also supplies methods for access to treeNodes
- *
- * @constructor
- * @return A new TsRef instance
- */
-var TsRef = function() {
-       var typeTree = new Array();
-
-       var doc;
-
-       this.loadTsrefAsync = function() {
-               new Ajax.Request(
-                       TYPO3.settings.ajaxUrls['T3Editor_TSrefLoader::getTypes'],
-                       {
-                               method: 'get',
-                               onSuccess: function(transport) {
-                                       doc = eval('('+ transport.responseText +')');
-                                       buildTree();
-                               }
-                       }
-               );
-       }
-
-       function buildTree() {
-
-               typeTree = new Array();
-               for (var typeId in doc) {
-
-                       var arr = doc[typeId];
-                       typeTree[typeId] = new TsRefType(typeId);
-
-                       if (arr['extends'] != null) {
-                               typeTree[typeId]['extends'] = arr['extends'];
-                       }
-                       for (propName in arr.properties) {
-                               var propType = arr.properties[propName].type;
-                               typeTree[typeId].properties[propName] = new TsRefProperty(typeId,propName,propType);
-                       }
-               }
-               for (var typeId in typeTree) {
-                       if (typeTree[typeId]['extends'] != null) {
-                               //console.log(typeId+" | "+typeTree[typeId].extends+" |");
-                               addPropertiesToType(typeTree[typeId], typeTree[typeId]['extends'], 100);
-                       }
-               }
-       }
-
-       function addPropertiesToType(addToType,addFromTypeNames,maxRecDepth){
-               if(maxRecDepth<0){
-                       throw "Maximum recursion depth exceeded while trying to resolve the extends in the TSREF!";
-                       return;
-               }
-               var exts = addFromTypeNames.split(',');
-               var i;
-               for(i=0;i<exts.length;i++){
-                       //"Type 'array' which is used to extend 'undefined', was not found in the TSREF!"
-                       if(typeTree[exts[i]]==null){
-                               //console.log("Error: Type '"+exts[i]+"' which is used to extend '"+addToType.typeId+"', was not found in the TSREF!");
-                       }else{
-                               if(typeTree[exts[i]]['extends'] != null){
-                                       addPropertiesToType(typeTree[exts[i]],typeTree[exts[i]]['extends'],maxRecDepth-1);
-                               }
-                               var properties = typeTree[exts[i]].properties;
-                               for(propName in properties){
-                                       // only add this property if it was not already added by a supertype (subtypes override supertypes)
-                                       if(addToType.properties[propName] == null){
-                                               addToType.properties[propName] = properties[propName];
-                                       }
-                               }
-                       }
-               }
-       }
-
-       this.getPropertiesFromTypeId = function(tId) {
-               if (typeTree[tId] != null) {
-                       // clone is needed to assure that nothing of the tsref is overwritten by user setup
-                       typeTree[tId].properties.clone = function() {
-                               var result = new Array();
-                               for (key in this) {
-                                       result[key] = new TsRefProperty(this[key].parentType,this[key].name,this[key].value);
-                               }
-                               return result;
-                       }
-                       return typeTree[tId].properties;
-               } else {
-                       return new Array();
-               }
-       }
-
-       this.typeHasProperty = function(typeId,propertyName) {
-               if (typeTree[typeId] != null && typeTree[typeId].properties[propertyName] != null) {
-                       return true;
-               } else {
-                       return false;
-               }
-       }
-
-       this.getType = function(typeId){
-               return typeTree[typeId];
-       }
-       this.isType = function(typeId){
-               return (typeTree[typeId] != null);
-       }
-}
\ No newline at end of file