From db317442d27f771ee6ab58b696467b15e86a3cd8 Mon Sep 17 00:00:00 2001 From: Guite Date: Thu, 10 Jul 2014 21:24:00 +0200 Subject: [PATCH] basic migration of Prototype Ajax.AutoCompleter to bootstrap3-typeahead --- .../zclassic/controller/ControllerLayer.xtend | 1 + .../zclassic/controller/additions/Ajax.xtend | 168 +++++++--- .../controller/javascript/EditFunctions.xtend | 294 ++++++++++++++---- .../cartridges/zclassic/view/Forms.xtend | 3 + .../cartridges/zclassic/view/Styles.xtend | 69 ++-- .../form/RelationSelectorAutoComplete.xtend | 39 ++- .../zclassic/view/plugin/form/UserInput.xtend | 30 +- 7 files changed, 429 insertions(+), 175 deletions(-) diff --git a/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/controller/ControllerLayer.xtend b/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/controller/ControllerLayer.xtend index 49e5853be..4d6a9e7c2 100644 --- a/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/controller/ControllerLayer.xtend +++ b/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/controller/ControllerLayer.xtend @@ -257,6 +257,7 @@ class ControllerLayer { use Zikula\Core\Response\Ajax\BadDataResponse; use Zikula\Core\Response\Ajax\FatalResponse; use Zikula\Core\Response\Ajax\NotFoundResponse; + use Symfony\Component\HttpFoundation\JsonResponse; «ENDIF» use Zikula\Core\Response\PlainResponse; ''' diff --git a/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/controller/additions/Ajax.xtend b/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/controller/additions/Ajax.xtend index 94fd8a70b..7ad6e8a31 100644 --- a/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/controller/additions/Ajax.xtend +++ b/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/controller/additions/Ajax.xtend @@ -123,19 +123,48 @@ class Ajax { $results = $query->getArrayResult(); «ENDIF» - $out = ''; + // load avatar plugin + «IF app.targets('1.3.5')» + include_once 'lib/viewplugins/function.useravatar.php'; + «ELSE» + include_once 'lib/legacy/viewplugins/function.useravatar.php'; + «ENDIF» + $view = Zikula_View::getInstance('«app.appName»', false); «IF app.targets('1.3.5')» - return new Zikula_Response_Ajax_Plain($out); + $out = ''; + + «IF app.targets('1.3.5')» + return new Zikula_Response_Ajax_Plain($out); + «ELSE» + return new PlainResponse($out); + «ENDIF» «ELSE» - return new PlainResponse($out); + $resultItems = array(); + if (is_array($results) && count($results) > 0) { + foreach ($results as $result) { + $resultItems[] = array( + 'uid' => $result['uid'], + 'uname' => DataUtil::formatForDisplay($result['uname']), + 'avatar' => smarty_function_useravatar(array('uid' => $result['uid'], 'rating' => 'g'), $view) + ); + } + } + + return new JsonResponse($resultItems); «ENDIF» ''' @@ -342,50 +371,91 @@ class Ajax { // get objects from database list($entities, $objectCount) = $repository->selectSearch($fragment, $exclude, $sortParam, $currentPage, $resultsPerPage); - $out = ''; - // return response - return new «IF app.targets('1.3.5')»Zikula_Response_Ajax_Plain«ELSE»PlainResponse«ENDIF»($out); + return new JsonResponse($resultItems); + «ENDIF» + ''' + + def private prepareForAutoCompletionProcessing(AjaxController it, Application app) ''' + $descriptionFieldName = $repository->getDescriptionFieldName(); + $previewFieldName = $repository->getPreviewFieldName(); + «IF app.hasImageFields» + if (!empty($previewFieldName)) { + «IF app.targets('1.3.5')» + $imageHelper = new «app.appName»_Util_Image($this->serviceManager); + «ELSE» + $imageHelper = $this->serviceManager->get('«app.appName.formatForDB».image_helper'); + «ENDIF» + $imagineManager = $imageHelper->getManager($objectType, $previewFieldName, 'controllerAction', $utilArgs); + } + «ENDIF» ''' def private checkForDuplicateBase(AjaxController it, Application app) ''' diff --git a/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/controller/javascript/EditFunctions.xtend b/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/controller/javascript/EditFunctions.xtend index f4e985ab0..2fe24f07c 100644 --- a/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/controller/javascript/EditFunctions.xtend +++ b/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/controller/javascript/EditFunctions.xtend @@ -73,27 +73,111 @@ class EditFunctions { */ function «prefix()»InitUserField(fieldName, getterName) { - if ($(fieldName + 'LiveSearch') === null) { - return; - } - $(fieldName + 'LiveSearch').removeClassName('«IF targets('1.3.5')»z-hide«ELSE»hidden«ENDIF»'); - new Ajax.Autocompleter( - fieldName + 'Selector', - fieldName + 'SelectorChoices', - «IF targets('1.3.5')» + «IF !targets('1.3.5')» + var users, userMap; + + «ENDIF» + «IF targets('1.3.5')» + if ($(fieldName + 'LiveSearch') === null) { + return; + } + $(fieldName + 'LiveSearch').removeClassName('z-hide'); + «ELSE» + if ($('#' + fieldName + 'LiveSearch').length < 1) { + return; + } + $('#' + fieldName + 'LiveSearch').removeClass('hidden'); + «ENDIF» + + «IF targets('1.3.5')» + new Ajax.Autocompleter( + fieldName + 'Selector', + fieldName + 'SelectorChoices', Zikula.Config.baseURL + 'ajax.php?module=«appName»&func=' + getterName, - «ELSE» - Routing.generate('«appName.formatForDB»_ajax_' + getterName), - «ENDIF» - { - paramName: 'fragment', - minChars: 3, - indicator: fieldName + 'Indicator', - afterUpdateElement: function(data) { - $(fieldName).value = $($(data).value).value; + { + paramName: 'fragment', + minChars: 3, + indicator: fieldName + 'Indicator', + afterUpdateElement: function (inputField, selectedListItem) { + var itemId = selectedListItem.id; + var userId = itemId.replace('user', ''); + $(fieldName).value = userId; + } } - } - ); + ); + «ELSE» + users = []; + userMap = []; + + $('#' + fieldName + 'Selector').typeahead({ + items: 25, + minLength: 2, + showHintOnFocus: true, + scrollHeight: 400, + + // The data source to query against. Receives the query value in the input field and the process callback. + source: function (query, process) { + users[fieldName] = []; + userMap[fieldName] = {}; + + // Retrieve data from server using "query" parameter as it contains the search string entered by the user + $('#' + fieldName + 'Indicator').removeClass('hidden') + $.getJSON( Routing.generate('«appName.formatForDB»_ajax_' + getterName, { fragment: query }), function( data ) { + + if (data.length > 0) { + $('#' + idPrefix + 'NoResultsHint').addClass('hidden'); + + // map dropdown options to corresponding objects + $.each(data, function (key, user) { + userMap[fieldName][user.uname] = user; + users[fieldName].push(user.uname); + }); + } else { + $('#' + idPrefix + 'NoResultsHint').removeClass('hidden'); + } + + $('#' + fieldName + 'Indicator').addClass('hidden') + }); + + // call process() function with dropdown array + return process(users[fieldName]); + }, + + // custom formatting of result items + highlighter: function(item) { + var html, user; + + user = userMap[fieldName][item]; + + html = '
'; + html += '
' + html += '
'; + html += '

' + user.uname + '

'; + html += '
'; + html += '
'; + + return html; + }, + + // Called after the user selects an item. Here we can do something with the selection. + updater: function (item) { + var userId; + + userId = userMap[fieldName][item].uid; + + $('#' + fieldName).val(userId); + + return item; + } + }); + + // Ensure that clearing out the selector is reflected into the hidden field properly + $('#' + fieldName + 'Selector').blur(function() { + if ($(this).val().length == 0 || $('#' + fieldName).val() != userMap[fieldName][$(this).val()]) { + $('#' + fieldName).val(''); + } + }); + «ENDIF» } «ENDIF» @@ -262,7 +346,7 @@ class EditFunctions { ''' def private relationFunctionsPreparation(Application it) ''' - «IF !getJoinRelations.empty» + «IF !getJoinRelations.empty && targets('1.3.5')» /** * Override method of Scriptaculous auto completer method. @@ -528,7 +612,7 @@ class EditFunctions { newTitle = $F(idPrefix + 'Selector'); includeEditing = !!(($F(idPrefix + 'Mode') == '1')); «ELSE» - newItemId = selectedListItem.attr('id'); + newItemId = selectedListItem.id; newTitle = $('#' + idPrefix + 'Selector').val(); includeEditing = !!(($('#' + idPrefix + 'Mode').val() == '1')); «ENDIF» @@ -540,8 +624,8 @@ class EditFunctions { itemPreview = $('itemPreview' + selectedListItem.id).innerHTML; } «ELSE» - if ($('#itemPreview' + selectedListItem.attr('id')).size() > 0) { - itemPreview = $('#itemPreview' + selectedListItem.attr('id')).innerHTML; + if (selectedListItem.image != '') { + itemPreview = selectedListItem.image; } «ENDIF» @@ -650,7 +734,7 @@ class EditFunctions { */ function «prefixSmall»InitRelationItemsForm(objectType, idPrefix, includeEditing) { - var acOptions, itemIds, itemIdsArr; + var acOptions, itemIds, itemIdsArr«IF !targets('1.3.5')», listItems, listItemMap, acUrl«ENDIF»; «IF targets('1.3.5')» // add handling for the toggle link if existing @@ -682,16 +766,16 @@ class EditFunctions { // clear values and ensure starting state «prefixSmall»ResetRelatedItemForm(idPrefix); - acOptions = { - paramName: 'fragment', - minChars: 2, - indicator: idPrefix + 'Indicator', - callback: function (inputField, defaultQueryString) { - var queryString; - - // modify the query string before the request - queryString = defaultQueryString + '&ot=' + objectType; - «IF targets('1.3.5')» + «IF targets('1.3.5')» + acOptions = { + paramName: 'fragment', + minChars: 2, + indicator: idPrefix + 'Indicator', + callback: function (inputField, defaultQueryString) { + var queryString; + + // modify the query string before the request + queryString = defaultQueryString + '&ot=' + objectType; if ($(idPrefix + 'ItemList') !== null) { queryString += '&exclude=' + $F(idPrefix + 'ItemList'); } @@ -699,39 +783,115 @@ class EditFunctions { if ($(idPrefix + 'NoResultsHint') != null) { $(idPrefix + 'NoResultsHint').addClassName('z-hide'); } - «ELSE» + + return queryString; + }, + afterUpdateElement: function (inputField, selectedListItem) { + // Called after the input element has been updated (i.e. when the user has selected an entry). + // This function is called after the built-in function that adds the list item text to the input field. + «prefixSmall»SelectRelatedItem(objectType, idPrefix, inputField, selectedListItem); + } + }; + relationHandler.each(function (relationHandler) { + if (relationHandler.prefix === (idPrefix + 'SelectorDoNew') && relationHandler.acInstance === null) { + relationHandler.acInstance = new Ajax.Autocompleter( + idPrefix + 'Selector', + idPrefix + 'SelectorChoices', + Zikula.Config.baseURL + 'ajax.php?module=' + relationHandler.moduleName + '&func=getItemListAutoCompletion', + acOptions + ); + } + }); + «ELSE» + listItems = []; + listItemMap = []; + + acOptions = { + items: 25, + minLength: 2, + showHintOnFocus: true, + scrollHeight: 400, + + // The data source to query against. Receives the query value in the input field and the process callback. + source: function (query, process) { + listItems[idPrefix] = []; + listItemMap[idPrefix] = {}; + + // Retrieve data from server using "query" parameter as it contains the search string entered by the user + $('#' + idPrefix + 'Indicator').removeClass('hidden') + $.getJSON( acUrl, { fragment: query }), function( data ) { + + if (data.length > 0) { + $('#' + idPrefix + 'NoResultsHint').addClass('hidden'); + + // map dropdown options to corresponding objects + $.each(data, function (key, listItem) { + listItemMap[idPrefix][listItem.title] = listItem; + listItems[idPrefix].push(listItem.title); + }); + } else { + $('#' + idPrefix + 'NoResultsHint').removeClass('hidden'); + } + + $('#' + idPrefix + 'Indicator').addClass('hidden') + }); + + // call process() function with dropdown array + return process(listItems[idPrefix]); + }, + + // custom formatting of result items + highlighter: function(item) { + var html, listItem; + + listItem = listItemMap[idPrefix][item]; + + html = '
'; + html += '
' + html += '
'; + html += '

' + listItem.title + '

'; + html += listItem.description; + html += '
'; + html += '
'; + + return html; + }, + + // Called after the user selects an item. Here we can do something with the selection. + updater: function (item) { + var inputField, listItem; + + inputField = $('#' + idPrefix); + listItem = listItemMap[idPrefix][item]; + + «prefixSmall»SelectRelatedItem(objectType, idPrefix, inputField, listItem); + inputField.val(listItemId); + + return item; + } + }; + + relationHandler.each(function (key, relationHandler) { + if (relationHandler.prefix === (idPrefix + 'SelectorDoNew') && relationHandler.acInstance === null) { + relationHandler.acInstance = 'yes'; + + acUrl = Routing.generate(relationHandler.moduleName.toLowerCase() + '_ajax_getItemListAutoCompletion'); + acUrl += '&ot=' + objectType; if ($('#' + idPrefix + 'ItemList').size() > 0) { - queryString += '&exclude=' + $('#' + idPrefix + 'ItemList').val(); + acUrl += '&exclude=' + $('#' + idPrefix + 'ItemList').val(); } - if ($('#' + idPrefix + 'NoResultsHint').size() > 0) { - $('#' + idPrefix + 'NoResultsHint').addClass('hidden'); - } - «ENDIF» + $('#' + idPrefix + 'Selector').typeahead(acOptions); - return queryString; - }, - afterUpdateElement: function (inputField, selectedListItem) { - // Called after the input element has been updated (i.e. when the user has selected an entry). - // This function is called after the built-in function that adds the list item text to the input field. - «prefixSmall»SelectRelatedItem(objectType, idPrefix, inputField, selectedListItem); - } - }; - relationHandler.each(function (relationHandler) { - if (relationHandler.prefix === (idPrefix + 'SelectorDoNew') && relationHandler.acInstance === null) { - relationHandler.acInstance = new Ajax.Autocompleter( - idPrefix + 'Selector', - idPrefix + 'SelectorChoices', - «IF targets('1.3.5')» - Zikula.Config.baseURL + 'ajax.php?module=' + relationHandler.moduleName + '&func=getItemListAutoCompletion', - «ELSE» - //Routing.generate('«appName.formatForDB»_ajax_getItemListAutoCompletion'), - Routing.generate(relationHandler.moduleName.toLowerCase() + '_ajax_getItemListAutoCompletion'), - «ENDIF» - acOptions - ); - } - }); + // Ensure that clearing out the selector is reflected into the hidden field properly + $('#' + idPrefix + 'Selector').blur(function() { + if ($(this).val().length == 0 || $('#' + idPrefix).val() != listItemMap[idPrefix][$(this).val()]) { + $('#' + idPrefix).val(''); + } + }); + } + }); + «ENDIF» «IF targets('1.3.5')» if (!includeEditing || $(idPrefix + 'SelectorDoNew') === null) { @@ -807,9 +967,13 @@ class EditFunctions { // look whether there is an auto completion instance if (relationHandler.acInstance !== null) { // activate it - relationHandler.acInstance.activate(); + «IF targets('1.3.5')» + relationHandler.acInstance.activate(); + «ELSE» + $('#' + idPrefix + 'Selector').lookup(); + «ENDIF» // show a message - «IF targets('1.3.5')» + «IF targets('1.3.5')» Zikula.UI.Alert(Zikula.__('Action has been completed.', 'module_«appName.formatForDB»_js'), Zikula.__('Information', 'module_«appName.formatForDB»_js'), { autoClose: 3 // time in seconds }); diff --git a/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/view/Forms.xtend b/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/view/Forms.xtend index 9458fda9f..8b09743d4 100644 --- a/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/view/Forms.xtend +++ b/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/view/Forms.xtend @@ -89,6 +89,9 @@ class Forms { «ENDIF» {pageaddvar name='javascript' value='«app.rootFolder»/«app.appName»/«IF app.targets('1.3.5')»javascript/«ELSE»«app.getAppJsPath»«ENDIF»«app.appName»«IF app.targets('1.3.5')»_e«ELSE».E«ENDIF»ditFunctions.js'} {pageaddvar name='javascript' value='«app.rootFolder»/«app.appName»/«IF app.targets('1.3.5')»javascript/«ELSE»«app.getAppJsPath»«ENDIF»«app.appName»«IF app.targets('1.3.5')»_v«ELSE».V«ENDIF»alidation.js'} + «IF !app.targets('1.3.5') && (hasUserFieldsEntity || !getOutgoingJoinRelations.empty || !getIncomingJoinRelations.empty)» + {pageaddvar name='javascript' value='web/bootstrap3-typeahead/bootstrap3-typeahead.min.js'} + «ENDIF» {if $mode eq 'edit'} {gt text='Edit «name.formatForDisplay»' assign='templateTitle'} diff --git a/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/view/Styles.xtend b/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/view/Styles.xtend index a6af53395..35e8ed43e 100644 --- a/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/view/Styles.xtend +++ b/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/view/Styles.xtend @@ -165,50 +165,43 @@ class Styles { display: none; } - «IF hasUserFields» - div.«cssPrefix»-livesearch-user { - margin: 0; - } + «IF targets('1.3.5')» + «IF hasUserFields» + div.«cssPrefix»-livesearch-user { + margin: 0; + } - «ENDIF» - «/*required for IE*/» - div.«cssPrefix»-autocomplete-wrap { - position: absolute; - height: 40px; - margin: 0; - padding: 0; - left: 260px; - top: 10px; - } + «ENDIF» - div.«cssPrefix»-autocomplete«IF hasUserFields», - div.«cssPrefix»-autocomplete-user«ENDIF»«IF hasImageFields», - div.«cssPrefix»-autocomplete-withimage«ENDIF» { - position: relative !important; - top: 2px !important; - width: 191px !important; - background-color: #fff; - border: 1px solid #888; - margin: 0; - padding: 0; - } + div.«cssPrefix»-autocomplete«IF hasUserFields», + div.«cssPrefix»-autocomplete-user«ENDIF»«IF hasImageFields», + div.«cssPrefix»-autocomplete-withimage«ENDIF» { + position: relative !important; + top: 2px !important; + width: 191px !important; + background-color: #fff; + border: 1px solid #888; + margin: 0; + padding: 0; + } - div.«cssPrefix»-autocomplete«IF hasImageFields», - div.«cssPrefix»-autocomplete-with-image«ENDIF» { - left: 0 !important; - } - «IF hasUserFields» - div.«cssPrefix»-autocomplete-user { - left: 29% !important; + div.«cssPrefix»-autocomplete«IF hasImageFields», + div.«cssPrefix»-autocomplete-with-image«ENDIF» { + left: 0 !important; } + «IF hasUserFields» + div.«cssPrefix»-autocomplete-user { + left: 29% !important; + } + «ENDIF» + div.«cssPrefix»-autocomplete ul«IF hasUserFields», + div.«cssPrefix»-autocomplete-user ul«ENDIF»«IF hasImageFields», + div.«cssPrefix»-autocomplete-with-image ul«ENDIF» { + margin: 0; + padding: 0; + } «ENDIF» - div.«cssPrefix»-autocomplete ul«IF hasUserFields», - div.«cssPrefix»-autocomplete-user ul«ENDIF»«IF hasImageFields», - div.«cssPrefix»-autocomplete-with-image ul«ENDIF» { - margin: 0; - padding: 0; - } div.«cssPrefix»-autocomplete ul li«IF hasUserFields», div.«cssPrefix»-autocomplete-user ul li«ENDIF»«IF hasImageFields», diff --git a/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/view/plugin/form/RelationSelectorAutoComplete.xtend b/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/view/plugin/form/RelationSelectorAutoComplete.xtend index 72f1c1ad4..5b2f7623d 100644 --- a/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/view/plugin/form/RelationSelectorAutoComplete.xtend +++ b/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/view/plugin/form/RelationSelectorAutoComplete.xtend @@ -118,7 +118,7 @@ class RelationSelectorAutoComplete { */ protected function getStyleClass() { - return 'z-form-relationlist autocomplete'; + return 'z-form-relationlist «IF targets('1.3.5')»autocomplete«ELSE»typeahead«ENDIF»'; } /** @@ -155,24 +155,35 @@ class RelationSelectorAutoComplete { } $alias = $this->id; + $class = $this->getStyleClass(); $result = '
' . $addLink . ' -
+
-
- ' . $searchIconText . ' - - - - ' . __('No results found!', $dom) . ' -
- ' - . $createLink . ' - -
-
'; +
'; + + «IF targets('1.3.5')» + $result .= '' . $searchIconText . ' + + + + ' . __('No results found!', $dom) . ' +
'; + «ELSE» + $result .= ' + + + + '; + «ENDIF» + $result .= ' + ' + . $createLink . ' + +
+
' . "\n"; return $result; } diff --git a/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/view/plugin/form/UserInput.xtend b/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/view/plugin/form/UserInput.xtend index cd2c07b0b..b4999ab7e 100644 --- a/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/view/plugin/form/UserInput.xtend +++ b/org.zikula.modulestudio.generator/src/org/zikula/modulestudio/generator/cartridges/zclassic/view/plugin/form/UserInput.xtend @@ -82,7 +82,7 @@ class UserInput { { $class = parent::getStyleClass(); - return str_replace('z-form-text', 'z-form-user', $class); + return str_replace('z-form-text', 'z-form-user«IF !targets('1.3.5')» typeahead«ENDIF»', $class); } /** @@ -120,20 +120,32 @@ class UserInput { $searchTitle = __('Search user', $dom); $selectorAttributes = $titleHtml . $sizeHtml . $maxLengthHtml . $readOnlyHtml . ' value="' . $selectorDefaultValue . '" class="' . $class . '"' . $attributes; - $result = '
- ' . $searchTitle . ' - - - ' . __('No results found!', $dom) . ' -
'; + «IF targets('1.3.5')» + $result = '
+ ' . $searchTitle . ' + + + ' . __('No results found!', $dom) . ' +
'; + «ELSE» + $result = '' . "\n"; $result .= '' . "\n"; - $result .= '' . "\n"; + «IF targets('1.3.5')» + $result .= '' . "\n"; + «ELSE» + $result .= ' + + + '; + «ENDIF» + $result .= '
' . "\n"; return $result; }