Created
March 15, 2026 11:11
-
-
Save Pathologic/f9da96cce9de50872e505ab74a64351f to your computer and use it in GitHub Desktop.
Evo snippets call converter to php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>Конвертер вызовов сниппетов MODX</title> | |
| <style> | |
| body { font-family: Arial, sans-serif; max-width: 800px; margin: 20px auto; padding: 0 20px; } | |
| textarea { width: 100%; height: 200px; margin-bottom: 10px; font-family: monospace; } | |
| .button-group { display: flex; gap: 10px; margin-bottom: 10px; } | |
| button { padding: 10px 20px; background: #4CAF50; color: white; border: none; cursor: pointer; border-radius: 4px; } | |
| button:hover { background: #45a049; } | |
| .clear-btn { background: #f44336; } | |
| .clear-btn:hover { background: #da190b; } | |
| .result { margin-top: 20px; } | |
| pre { background: #f4f4f4; padding: 10px; border-radius: 4px; overflow-x: auto; } | |
| .example { background: #e8f4f8; padding: 10px; border-radius: 4px; margin-bottom: 15px; font-size: 14px; } | |
| .note { color: #666; font-size: 12px; margin-top: 5px; } | |
| </style> | |
| </head> | |
| <body> | |
| <h2>Конвертер MODX: [!...!] / [[...]] → evo()->runSnippet()</h2> | |
| <div class="example"> | |
| <strong>Примеры:</strong><br> | |
| [!mySnippet? ¶m=`value` &number=`123` &json=`{"key":"value"}` !]<br> | |
| [[getResources? &parents=`3` &tpl=`item` &limit=`10` ]]<br> | |
| [!DocLister? &display=`12` &tpl=`item` !] | |
| </div> | |
| <label for="input">Введите вызов сниппета:</label> | |
| <textarea id="input" placeholder="[!mySnippet? ¶m=`value` !] или [[mySnippet? ¶m=`value` ]]">[[getResources? | |
| &parents=`3` | |
| &tpl=`item` | |
| &limit=`10` | |
| &includeTVs=`1` | |
| &tvPrefix=`` | |
| ]]</textarea> | |
| <div class="button-group"> | |
| <button onclick="convert()">Конвертировать</button> | |
| <button onclick="clearInput()" class="clear-btn">Очистить поле</button> | |
| </div> | |
| <div class="result"> | |
| <label for="output">Результат:</label> | |
| <pre id="output"></pre> | |
| <button onclick="copyToClipboard()" id="copyBtn" style="margin-top: 5px;">Копировать</button> | |
| </div> | |
| <script> | |
| function escapeSingleQuotes(str) { | |
| // Экранируем одинарные кавычки, но не трогаем уже экранированные | |
| return str.replace(/(?<!\\)'/g, "\\'"); | |
| } | |
| function formatJSONValue(value, indent = 4) { | |
| const spaces = ' '.repeat(indent); | |
| if (Array.isArray(value)) { | |
| if (value.length === 0) return '[]'; | |
| const items = value.map(item => { | |
| if (typeof item === 'string') { | |
| return `'${escapeSingleQuotes(item)}'`; | |
| } else if (Array.isArray(item) || typeof item === 'object') { | |
| return formatJSONValue(item, indent + 4); | |
| } else { | |
| return item; | |
| } | |
| }).join(',\n' + spaces); | |
| return `[\n${spaces}${items}\n${' '.repeat(indent - 4)}]`; | |
| } | |
| if (typeof value === 'object' && value !== null) { | |
| const entries = Object.entries(value).map(([k, v]) => { | |
| let formattedValue; | |
| if (typeof v === 'string') { | |
| formattedValue = `'${escapeSingleQuotes(v)}'`; | |
| } else if (Array.isArray(v) || typeof v === 'object') { | |
| formattedValue = formatJSONValue(v, indent + 4); | |
| } else { | |
| formattedValue = v; | |
| } | |
| return ` '${k}' => ${formattedValue}`; | |
| }).join(',\n' + spaces); | |
| return `[\n${spaces}${entries}\n${' '.repeat(indent - 4)}]`; | |
| } | |
| return value; | |
| } | |
| function processJSONObject(obj) { | |
| if (Array.isArray(obj)) { | |
| return obj.map(item => { | |
| if (typeof item === 'object' && item !== null) { | |
| return processJSONObject(item); | |
| } else if (typeof item === 'string') { | |
| if (/^\d+$/.test(item)) { | |
| return parseInt(item, 10); | |
| } else if (/^\d+\.\d+$/.test(item)) { | |
| return parseFloat(item); | |
| } | |
| return item; | |
| } | |
| return item; | |
| }); | |
| } else if (typeof obj === 'object' && obj !== null) { | |
| const result = {}; | |
| for (const [key, val] of Object.entries(obj)) { | |
| if (typeof val === 'object' && val !== null) { | |
| result[key] = processJSONObject(val); | |
| } else if (typeof val === 'string') { | |
| if (/^\d+$/.test(val)) { | |
| result[key] = parseInt(val, 10); | |
| } else if (/^\d+\.\d+$/.test(val)) { | |
| result[key] = parseFloat(val); | |
| } else { | |
| result[key] = val; | |
| } | |
| } else { | |
| result[key] = val; | |
| } | |
| } | |
| return result; | |
| } | |
| return obj; | |
| } | |
| function extractSnippetName(input) { | |
| // Улучшенное регулярное выражение для извлечения имени сниппета | |
| // Ищем после [! или [[ до ? или пробела или конца строки | |
| const match = input.match(/\[\!?(\w+)(?:\?|\s|\])/); | |
| if (match && match[1]) { | |
| return match[1]; | |
| } | |
| // Если не нашли с ? или пробелом, пробуем просто взять слово после тега | |
| const fallbackMatch = input.match(/\[\!?(\w+)/); | |
| return fallbackMatch ? fallbackMatch[1] : 'SnippetName'; | |
| } | |
| function cleanInput(input) { | |
| // Удаляем внешние теги [! !] или [[ ]] | |
| // Более аккуратная очистка, сохраняющая все параметры | |
| let cleaned = input.replace(/^\[\!?/, '').replace(/\!?\]$/, '').trim(); | |
| // Убираем знак вопроса после имени сниппета, если он есть | |
| cleaned = cleaned.replace(/^(\w+)\?/, '$1'); | |
| return cleaned; | |
| } | |
| function unescapeBackslashes(str) { | |
| // Преобразуем обратно экранированные символы, кроме одинарных кавычек | |
| return str.replace(/\\([^'])/g, '$1'); | |
| } | |
| function convertCallToEvo(input) { | |
| // Извлекаем имя сниппета | |
| const snippetName = extractSnippetName(input); | |
| // Очищаем входную строку от тегов | |
| const cleanString = cleanInput(input); | |
| // Регулярное выражение для извлечения параметров | |
| const regex = /&(\w+)=`([^`]*)`/g; | |
| const params = {}; | |
| let match; | |
| while ((match = regex.exec(cleanString)) !== null) { | |
| const key = match[1]; | |
| let value = match[2]; | |
| // Пробуем распарсить JSON | |
| try { | |
| const trimmed = value.trim(); | |
| if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || | |
| (trimmed.startsWith('[') && trimmed.endsWith(']'))) { | |
| const parsed = JSON.parse(trimmed); | |
| if (Array.isArray(parsed)) { | |
| value = parsed.map(item => { | |
| if (typeof item === 'string') { | |
| if (/^\d+$/.test(item)) { | |
| return parseInt(item, 10); | |
| } else if (/^\d+\.\d+$/.test(item)) { | |
| return parseFloat(item); | |
| } | |
| } | |
| return item; | |
| }); | |
| } else if (typeof parsed === 'object' && parsed !== null) { | |
| value = processJSONObject(parsed); | |
| } else { | |
| value = parsed; | |
| } | |
| } else { | |
| // Не JSON, обрабатываем как обычную строку | |
| value = unescapeBackslashes(value); | |
| if (/^\d+$/.test(value)) { | |
| value = parseInt(value, 10); | |
| } else if (/^\d+\.\d+$/.test(value)) { | |
| value = parseFloat(value); | |
| } | |
| } | |
| } catch { | |
| // Не JSON, обрабатываем как обычную строку | |
| value = unescapeBackslashes(value); | |
| if (/^\d+$/.test(value)) { | |
| value = parseInt(value, 10); | |
| } else if (/^\d+\.\d+$/.test(value)) { | |
| value = parseFloat(value); | |
| } | |
| } | |
| params[key] = value; | |
| } | |
| // Форматируем массив параметров в строку | |
| const paramsString = Object.entries(params) | |
| .map(([key, value]) => { | |
| if (typeof value === 'string') { | |
| return ` '${key}' => '${escapeSingleQuotes(value)}'`; | |
| } else if (Array.isArray(value) || typeof value === 'object') { | |
| const formattedValue = formatJSONValue(value); | |
| return ` '${key}' => ${formattedValue}`; | |
| } else { | |
| return ` '${key}' => ${value}`; | |
| } | |
| }) | |
| .join(',\n'); | |
| // Если параметров нет | |
| if (paramsString.length === 0) { | |
| return `evo()->runSnippet('${snippetName}')`; | |
| } | |
| return `evo()->runSnippet('${snippetName}', [\n${paramsString}\n])`; | |
| } | |
| function convert() { | |
| const input = document.getElementById('input').value; | |
| const output = document.getElementById('output'); | |
| try { | |
| const result = convertCallToEvo(input); | |
| output.textContent = result; | |
| } catch (error) { | |
| output.textContent = 'Ошибка конвертации: ' + error.message; | |
| } | |
| } | |
| function clearInput() { | |
| document.getElementById('input').value = ''; | |
| document.getElementById('output').textContent = ''; | |
| } | |
| function copyToClipboard() { | |
| const output = document.getElementById('output').textContent; | |
| navigator.clipboard.writeText(output).then(() => { | |
| const btn = document.getElementById('copyBtn'); | |
| btn.textContent = 'Скопировано!'; | |
| setTimeout(() => { | |
| btn.textContent = 'Копировать'; | |
| }, 2000); | |
| }); | |
| } | |
| window.onload = convert; | |
| </script> | |
| <div class="note"> | |
| <strong>Примечание:</strong> Конвертер поддерживает оба формата: [!snippet!] и [[snippet]]. | |
| Числовые значения и JSON преобразуются автоматически. Одинарные кавычки в строках правильно экранируются. | |
| </div> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment