104fd306cSNickeau<?php 204fd306cSNickeau 304fd306cSNickeaunamespace ComboStrap\Tag; 404fd306cSNickeau 504fd306cSNickeauuse ComboStrap\CallStack; 604fd306cSNickeauuse ComboStrap\Dimension; 704fd306cSNickeauuse ComboStrap\Display; 804fd306cSNickeauuse ComboStrap\ExceptionBadState; 904fd306cSNickeauuse ComboStrap\ExceptionCompile; 1004fd306cSNickeauuse ComboStrap\ExceptionNotFound; 1104fd306cSNickeauuse ComboStrap\ExecutionContext; 1204fd306cSNickeauuse ComboStrap\FetcherMarkup; 1304fd306cSNickeauuse ComboStrap\FetcherMarkupWebcode; 1404fd306cSNickeauuse ComboStrap\FetcherRawLocalPath; 1504fd306cSNickeauuse ComboStrap\LogUtility; 1604fd306cSNickeauuse ComboStrap\PluginUtility; 1704fd306cSNickeauuse ComboStrap\TagAttribute\StyleAttribute; 1804fd306cSNickeauuse ComboStrap\TagAttributes; 1904fd306cSNickeauuse ComboStrap\WikiPath; 2004fd306cSNickeauuse syntax_plugin_combo_code; 2104fd306cSNickeauuse syntax_plugin_combo_codemarkdown; 2204fd306cSNickeau 2304fd306cSNickeauclass WebCodeTag 2404fd306cSNickeau{ 2504fd306cSNickeau 2604fd306cSNickeau public const TAG = 'webcode'; 2704fd306cSNickeau /** 2804fd306cSNickeau * The tag that have codes 2904fd306cSNickeau */ 3004fd306cSNickeau public const CODE_TAGS = array( 3104fd306cSNickeau syntax_plugin_combo_code::CODE_TAG, 3204fd306cSNickeau "plugin_combo_code", 3304fd306cSNickeau syntax_plugin_combo_codemarkdown::TAG 3404fd306cSNickeau ); 3504fd306cSNickeau /** 3604fd306cSNickeau * The attribute names in the array 3704fd306cSNickeau */ 3804fd306cSNickeau public const CODES_ATTRIBUTE = "codes"; 3904fd306cSNickeau public const EXTERNAL_RESOURCES_ATTRIBUTE_DISPLAY = 'externalResources'; 4004fd306cSNickeau public const USE_CONSOLE_ATTRIBUTE = "useConsole"; 4104fd306cSNickeau public const RENDERING_ONLY_RESULT_DEPRECATED = "onlyresult"; 4204fd306cSNickeau public const CANONICAL = WebCodeTag::TAG; 4304fd306cSNickeau public const DOKUWIKI_LANG = 'dw'; 4404fd306cSNickeau public const FRAMEBORDER_ATTRIBUTE = "frameborder"; 4504fd306cSNickeau /** 4604fd306cSNickeau * @deprecated for type 4704fd306cSNickeau */ 4804fd306cSNickeau public const RENDERING_MODE_ATTRIBUTE = 'renderingmode'; 4904fd306cSNickeau public const MARKIS = [WebCodeTag::MARKI_LANG, WebCodeTag::DOKUWIKI_LANG]; 5004fd306cSNickeau public const EXTERNAL_RESOURCES_ATTRIBUTE_KEY = 'externalresources'; 5104fd306cSNickeau /** 5204fd306cSNickeau * Marki code 5304fd306cSNickeau */ 5404fd306cSNickeau public const MARKI_LANG = 'marki'; 5504fd306cSNickeau public const IFRAME_BOOLEAN_ATTRIBUTE = "iframe"; 5604fd306cSNickeau const STORY_TYPE = "story"; 5704fd306cSNickeau const RESULT_TYPE = "result"; 5804fd306cSNickeau const INJECT_TYPE = "inject"; 5904fd306cSNickeau 6004fd306cSNickeau public static function getClass(): string 6104fd306cSNickeau { 6204fd306cSNickeau return StyleAttribute::addComboStrapSuffix(WebCodeTag::TAG); 6304fd306cSNickeau } 6404fd306cSNickeau 6504fd306cSNickeau public static function getKnownTypes(): array 6604fd306cSNickeau { 6704fd306cSNickeau return [self::STORY_TYPE, self::RESULT_TYPE, self::INJECT_TYPE]; 6804fd306cSNickeau } 6904fd306cSNickeau 7004fd306cSNickeau public static function getDefaultAttributes(): array 7104fd306cSNickeau { 7204fd306cSNickeau $defaultAttributes = array(); 7304fd306cSNickeau $defaultAttributes[Dimension::WIDTH_KEY] = '100%'; 7404fd306cSNickeau // 'type': no default to see if it was set because the default now is dependent on the content 7504fd306cSNickeau // 'height' is set by the javascript if not set 7604fd306cSNickeau // 'width' and 'scrolling' gets their natural value 7704fd306cSNickeau return $defaultAttributes; 7804fd306cSNickeau } 7904fd306cSNickeau 8004fd306cSNickeau public static function handleExit(\Doku_Handler $handler): array 8104fd306cSNickeau { 8204fd306cSNickeau /** 8304fd306cSNickeau * Capture all codes 8404fd306cSNickeau */ 8504fd306cSNickeau $codes = array(); 8604fd306cSNickeau /** 8704fd306cSNickeau * Does the javascript contains a console statement 8804fd306cSNickeau */ 8904fd306cSNickeau $useConsole = false; 9004fd306cSNickeau 9104fd306cSNickeau /** 9204fd306cSNickeau * Callstack 9304fd306cSNickeau */ 9404fd306cSNickeau $callStack = CallStack::createFromHandler($handler); 9504fd306cSNickeau $openingTag = $callStack->moveToPreviousCorrespondingOpeningCall(); 9604fd306cSNickeau $type = $openingTag->getType(); 9704fd306cSNickeau $renderingMode = $openingTag->getAttribute(WebCodeTag::RENDERING_MODE_ATTRIBUTE); 9804fd306cSNickeau if ($renderingMode !== null) { 9904fd306cSNickeau LogUtility::warning("The `renderingmode` attribute has been deprecated for the webcode `type` attribute."); 10004fd306cSNickeau if ($type === null) { 10104fd306cSNickeau $type = strtolower($renderingMode); 10204fd306cSNickeau } 10304fd306cSNickeau } 10404fd306cSNickeau if ($type === WebCodeTag::RENDERING_ONLY_RESULT_DEPRECATED) { 10504fd306cSNickeau LogUtility::warning("The `type` value (" . self::RENDERING_ONLY_RESULT_DEPRECATED . ") should be replaced by (" . self::RESULT_TYPE . ")"); 10604fd306cSNickeau $type = WebCodeTag::RESULT_TYPE; 10704fd306cSNickeau } 10804fd306cSNickeau 10904fd306cSNickeau /** 11004fd306cSNickeau * The mime (ie xml,html, ...) and code content are in two differents 11104fd306cSNickeau * call. To be able to set the content to the good type 11204fd306cSNickeau * we keep a trace of it 11304fd306cSNickeau */ 11404fd306cSNickeau $actualCodeType = ""; 11504fd306cSNickeau 11604fd306cSNickeau /** 11704fd306cSNickeau * Loop 11804fd306cSNickeau */ 11904fd306cSNickeau while ($actualTag = $callStack->next()) { 12004fd306cSNickeau 12104fd306cSNickeau 12204fd306cSNickeau $tagName = $actualTag->getTagName(); 12304fd306cSNickeau if (in_array($tagName, WebCodeTag::CODE_TAGS)) { 12404fd306cSNickeau 12504fd306cSNickeau /** 12604fd306cSNickeau * Only result or inject mode, we don't display the code 12704fd306cSNickeau * on all node (enter, exit and unmatched) 12804fd306cSNickeau */ 12904fd306cSNickeau if (in_array($type, [WebCodeTag::RESULT_TYPE, self::INJECT_TYPE])) { 13004fd306cSNickeau $actualTag->addAttribute(Display::DISPLAY, Display::DISPLAY_NONE_VALUE); 13104fd306cSNickeau } 13204fd306cSNickeau 13304fd306cSNickeau switch ($actualTag->getState()) { 13404fd306cSNickeau 13504fd306cSNickeau case DOKU_LEXER_ENTER: 13604fd306cSNickeau // Get the code (The content between the code nodes) 13704fd306cSNickeau // We ltrim because the match gives us the \n at the beginning and at the end 13804fd306cSNickeau $actualCodeType = strtolower(trim($actualTag->getType())); 13904fd306cSNickeau 14004fd306cSNickeau // Xml is html 14104fd306cSNickeau if ($actualCodeType === 'xml') { 14204fd306cSNickeau $actualCodeType = 'html'; 14304fd306cSNickeau } 14404fd306cSNickeau 14504fd306cSNickeau // markdown, dokuwiki is marki 14604fd306cSNickeau if (in_array($actualCodeType, ['md', 'markdown', 'dw'])) { 14704fd306cSNickeau $actualCodeType = WebCodeTag::MARKI_LANG; 14804fd306cSNickeau } 14904fd306cSNickeau 15004fd306cSNickeau // The code for a language may be scattered in multiple block 15104fd306cSNickeau if (!isset($codes[$actualCodeType])) { 15204fd306cSNickeau $codes[$actualCodeType] = ""; 15304fd306cSNickeau } 15404fd306cSNickeau 15504fd306cSNickeau continue 2; 15604fd306cSNickeau 15704fd306cSNickeau case DOKU_LEXER_UNMATCHED: 15804fd306cSNickeau 15904fd306cSNickeau $codeContent = $actualTag->getPluginData()[PluginUtility::PAYLOAD]; 16004fd306cSNickeau 16104fd306cSNickeau if (empty($actualCodeType)) { 16204fd306cSNickeau LogUtility::msg("The type of the code should not be null for the code content " . $codeContent, LogUtility::LVL_MSG_WARNING, WebCodeTag::TAG); 16304fd306cSNickeau continue 2; 16404fd306cSNickeau } 16504fd306cSNickeau 16604fd306cSNickeau // Append it 16704fd306cSNickeau $codes[$actualCodeType] = $codes[$actualCodeType] . $codeContent; 16804fd306cSNickeau 16904fd306cSNickeau // Check if a javascript console function is used, only if the flag is not set to true 17004fd306cSNickeau if (!$useConsole) { 17104fd306cSNickeau if (in_array($actualCodeType, array('babel', 'javascript', 'html', 'xml'))) { 17204fd306cSNickeau // if the code contains 'console.' 17304fd306cSNickeau $result = preg_match('/' . 'console\.' . '/is', $codeContent); 17404fd306cSNickeau if ($result) { 17504fd306cSNickeau $useConsole = true; 17604fd306cSNickeau } 17704fd306cSNickeau } 17804fd306cSNickeau } 17904fd306cSNickeau // Reset 18004fd306cSNickeau $actualCodeType = ""; 18104fd306cSNickeau break; 18204fd306cSNickeau 18304fd306cSNickeau } 18404fd306cSNickeau } 18504fd306cSNickeau 18604fd306cSNickeau } 18704fd306cSNickeau 18804fd306cSNickeau /** 18904fd306cSNickeau * By default, markup code 19004fd306cSNickeau * is rendered inside the page 19104fd306cSNickeau * We got less problem such as iframe overflow 19204fd306cSNickeau * due to lazy loading, such as relative link, ... 19304fd306cSNickeau */ 19404fd306cSNickeau if ( 19504fd306cSNickeau array_key_exists(WebCodeTag::MARKI_LANG, $codes) 19604fd306cSNickeau && count($codes) === 1 19704fd306cSNickeau && $openingTag->getAttribute(WebCodeTag::IFRAME_BOOLEAN_ATTRIBUTE) === null 19804fd306cSNickeau && $openingTag->getType() === null 19904fd306cSNickeau ) { 20004fd306cSNickeau $openingTag->setType(self::INJECT_TYPE); 20104fd306cSNickeau } 20204fd306cSNickeau 20304fd306cSNickeau return [ 20404fd306cSNickeau WebCodeTag::CODES_ATTRIBUTE => $codes, 20504fd306cSNickeau WebCodeTag::USE_CONSOLE_ATTRIBUTE => $useConsole, 20604fd306cSNickeau PluginUtility::ATTRIBUTES => $openingTag->getAttributes() 20704fd306cSNickeau ]; 20804fd306cSNickeau } 20904fd306cSNickeau 21004fd306cSNickeau /** 21104fd306cSNickeau * Tag is of an iframe (Web code) or a div (wiki markup) 21204fd306cSNickeau */ 21304fd306cSNickeau public static function renderExit(TagAttributes $tagAttributes, array $data) 21404fd306cSNickeau { 21504fd306cSNickeau 21604fd306cSNickeau $codes = $data[WebCodeTag::CODES_ATTRIBUTE]; 21704fd306cSNickeau 21804fd306cSNickeau $type = $tagAttributes->getType(); 21904fd306cSNickeau if ($type === null) { 22004fd306cSNickeau $type = self::STORY_TYPE; 22104fd306cSNickeau } 22204fd306cSNickeau 22304fd306cSNickeau /** 22404fd306cSNickeau * Rendering mode is used in handle exit, we delete it 22504fd306cSNickeau * to not get it in the HTML output 22604fd306cSNickeau */ 22704fd306cSNickeau $tagAttributes->removeComponentAttributeIfPresent(WebCodeTag::RENDERING_MODE_ATTRIBUTE); 22804fd306cSNickeau 22904fd306cSNickeau // Create the real output of webcode 23004fd306cSNickeau if (sizeof($codes) == 0) { 23104fd306cSNickeau return false; 23204fd306cSNickeau } 23304fd306cSNickeau 23404fd306cSNickeau 23504fd306cSNickeau // Css 23604fd306cSNickeau $snippetSystem = PluginUtility::getSnippetManager(); 23704fd306cSNickeau $snippetSystem->attachCssInternalStyleSheet(WebCodeTag::TAG); 23804fd306cSNickeau $snippetSystem->attachJavascriptFromComponentId(WebCodeTag::TAG); 23904fd306cSNickeau 24004fd306cSNickeau // Mermaid code ? 24104fd306cSNickeau if (array_key_exists(MermaidTag::MERMAID_CODE, $codes)) { 24204fd306cSNickeau $mermaidCode = ""; 24304fd306cSNickeau foreach ($codes as $codeKey => $code) { 24404fd306cSNickeau if ($codeKey !== MermaidTag::MERMAID_CODE) { 24504fd306cSNickeau LogUtility::error("The code type ($codeKey) was mixed with mermaid code in a webcode and this is not yet supported. The code was skipped"); 24604fd306cSNickeau continue; 24704fd306cSNickeau } 24804fd306cSNickeau $mermaidCode .= $code; 24904fd306cSNickeau } 25004fd306cSNickeau $tagAttributes->addComponentAttributeValue(MermaidTag::MARKUP_CONTENT_ATTRIBUTE, $mermaidCode); 25104fd306cSNickeau return MermaidTag::renderEnter($tagAttributes); 25204fd306cSNickeau } 25304fd306cSNickeau 25404fd306cSNickeau /** 25504fd306cSNickeau * Dokuwiki Code 25604fd306cSNickeau * (Just HTML) 25704fd306cSNickeau */ 25804fd306cSNickeau if (array_key_exists(WebCodeTag::MARKI_LANG, $codes)) { 25904fd306cSNickeau 26004fd306cSNickeau $markupCode = $codes[WebCodeTag::MARKI_LANG]; 26104fd306cSNickeau 26204fd306cSNickeau if ($type === self::INJECT_TYPE) { 26304fd306cSNickeau /** 26404fd306cSNickeau * the div is to be able to apply some CSS 26504fd306cSNickeau * such as don't show editbutton on webcode 26604fd306cSNickeau */ 26704fd306cSNickeau $html = $tagAttributes->toHtmlEnterTag("div"); 26804fd306cSNickeau try { 26904fd306cSNickeau $contextPath = ExecutionContext::getActualOrCreateFromEnv() 27004fd306cSNickeau ->getContextPath(); 27104fd306cSNickeau $html .= FetcherMarkup::confChild() 27204fd306cSNickeau ->setRequestedMarkupString($markupCode) 27304fd306cSNickeau ->setDeleteRootBlockElement(false) 27404fd306cSNickeau ->setIsDocument(false) 27504fd306cSNickeau ->setRequestedContextPath($contextPath) 27604fd306cSNickeau ->setRequestedMimeToXhtml() 27704fd306cSNickeau ->build() 27804fd306cSNickeau ->getFetchString(); 27904fd306cSNickeau } catch (ExceptionCompile $e) { 28004fd306cSNickeau $html .= $e->getMessage(); 28104fd306cSNickeau LogUtility::log2file("Error while rendering webcode", LogUtility::LVL_MSG_ERROR, WebCodeTag::CANONICAL, $e); 28204fd306cSNickeau } 28304fd306cSNickeau $html .= "</div>"; 28404fd306cSNickeau return $html; 28504fd306cSNickeau } 28604fd306cSNickeau 28704fd306cSNickeau /** 28804fd306cSNickeau * Iframe output 28904fd306cSNickeau */ 29004fd306cSNickeau $tagAttributes->removeComponentAttribute(WebCodeTag::IFRAME_BOOLEAN_ATTRIBUTE); 29104fd306cSNickeau 29204fd306cSNickeau if (!$tagAttributes->hasAttribute(TagAttributes::NAME_ATTRIBUTE)) { 29304fd306cSNickeau $tagAttributes->addOutputAttributeValueIfNotEmpty(TagAttributes::NAME_ATTRIBUTE, "WebCode iFrame"); 29404fd306cSNickeau } 29504fd306cSNickeau try { 29604fd306cSNickeau $url = FetcherMarkupWebcode::createFetcherMarkup($markupCode) 29704fd306cSNickeau ->getFetchUrl() 29804fd306cSNickeau ->toString(); 29904fd306cSNickeau $tagAttributes->addOutputAttributeValue("src", $url); 30004fd306cSNickeau } catch (ExceptionBadState $e) { 30104fd306cSNickeau // The markup is provided, we shouldn't have a bad state 30204fd306cSNickeau LogUtility::internalError("We were unable to set the iframe URL. Error:{$e->getMessage()}", WebCodeTag::CANONICAL); 30304fd306cSNickeau } 30404fd306cSNickeau return self::finishIframe($tagAttributes); 30504fd306cSNickeau 30604fd306cSNickeau 30704fd306cSNickeau } 30804fd306cSNickeau 30904fd306cSNickeau 31004fd306cSNickeau /** 31104fd306cSNickeau * Js Html Css language 31204fd306cSNickeau */ 31304fd306cSNickeau if ($type === self::INJECT_TYPE) { 31404fd306cSNickeau $htmlToInject = self::getCss($codes); 31504fd306cSNickeau return $htmlToInject . self::getBodyHtmlAndJavascript($codes, false); 31604fd306cSNickeau } 31704fd306cSNickeau 31804fd306cSNickeau /** @noinspection JSUnresolvedLibraryURL */ 31904fd306cSNickeau 32004fd306cSNickeau $headIFrame = <<<EOF 32104fd306cSNickeau<meta http-equiv="content-type" content="text/html; charset=UTF-8"/> 32204fd306cSNickeau<link id="normalize" rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css"/> 32304fd306cSNickeauEOF; 32404fd306cSNickeau 32504fd306cSNickeau 32604fd306cSNickeau // External Resources such as css stylesheet or js 32704fd306cSNickeau $externalResources = []; 32804fd306cSNickeau if ($tagAttributes->hasComponentAttribute(WebCodeTag::EXTERNAL_RESOURCES_ATTRIBUTE_KEY)) { 32904fd306cSNickeau LogUtility::warning("The (" . WebCodeTag::EXTERNAL_RESOURCES_ATTRIBUTE_KEY . ") has been deprecated. You should put your script/link in a code block with the `display` attribute set to `none`."); 33004fd306cSNickeau $resources = $tagAttributes->getValueAndRemove(WebCodeTag::EXTERNAL_RESOURCES_ATTRIBUTE_KEY); 33104fd306cSNickeau $externalResources = explode(",", $resources); 33204fd306cSNickeau } 33304fd306cSNickeau 334*1e6623d9Sgerardnico // Jsx / Babel Preprocessor, if babel is used, add it to the external resources 33504fd306cSNickeau if (array_key_exists('babel', $codes)) { 33604fd306cSNickeau $babelMin = "https://unpkg.com/babel-standalone@6/babel.min.js"; 33704fd306cSNickeau // a load of babel invoke it (be sure to not have it twice 33804fd306cSNickeau if (!(array_key_exists($babelMin, $externalResources))) { 33904fd306cSNickeau $externalResources[] = $babelMin; 34004fd306cSNickeau } 34104fd306cSNickeau } 34204fd306cSNickeau 34304fd306cSNickeau // Add the external resources 34404fd306cSNickeau foreach ($externalResources as $externalResource) { 34504fd306cSNickeau $pathInfo = pathinfo($externalResource); 34604fd306cSNickeau $fileExtension = $pathInfo['extension']; 34704fd306cSNickeau switch ($fileExtension) { 34804fd306cSNickeau case 'css': 34904fd306cSNickeau $headIFrame .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"$externalResource\"/>"; 35004fd306cSNickeau break; 35104fd306cSNickeau case 'js': 35204fd306cSNickeau $headIFrame .= "<script type=\"text/javascript\" src=\"$externalResource\"></script>"; 35304fd306cSNickeau break; 35404fd306cSNickeau } 35504fd306cSNickeau } 35604fd306cSNickeau 35704fd306cSNickeau // WebConsole style sheet 35804fd306cSNickeau $webcodeClass = WebCodeTag::getClass(); 35904fd306cSNickeau $cssUrl = FetcherRawLocalPath::createFromPath(WikiPath::createComboResource("webcode:webcode-iframe.css"))->getFetchUrl()->toHtmlString(); 36004fd306cSNickeau $headIFrame .= "<link class='$webcodeClass' rel=\"stylesheet\" type=\"text/css\" href=\"$cssUrl\"/>"; 36104fd306cSNickeau 36204fd306cSNickeau // A little margin to make it neater 36304fd306cSNickeau // that can be overwritten via cascade 36404fd306cSNickeau $headIFrame .= "<style class=\"$webcodeClass\">body { margin:10px } /* default margin */</style>"; 36504fd306cSNickeau 36604fd306cSNickeau // The css 36704fd306cSNickeau $headIFrame .= self::getCss($codes); 36804fd306cSNickeau 36904fd306cSNickeau // The javascript console script should be first to handle console.log in the content 37004fd306cSNickeau $useConsole = $data[WebCodeTag::USE_CONSOLE_ATTRIBUTE]; 37104fd306cSNickeau if ($useConsole) { 37204fd306cSNickeau $url = FetcherRawLocalPath::createFromPath(WikiPath::createComboResource("webcode:webcode-console.js"))->getFetchUrl()->toHtmlString(); 37304fd306cSNickeau $headIFrame .= <<<EOF 37404fd306cSNickeau<script class="$webcodeClass" type="text/javascript" src="$url"></script> 37504fd306cSNickeauEOF; 37604fd306cSNickeau } 37704fd306cSNickeau $body = self::getBodyHtmlAndJavascript($codes, $useConsole); 37804fd306cSNickeau $iframeSrcValue = <<<EOF 37904fd306cSNickeau<html lang="en"> 38004fd306cSNickeau<head> 38104fd306cSNickeau<title>Made by WebCode</title> 38204fd306cSNickeau$headIFrame 38304fd306cSNickeau</head> 38404fd306cSNickeau<body> 38504fd306cSNickeau$body 38604fd306cSNickeau</body> 38704fd306cSNickeau</html> 38804fd306cSNickeauEOF; 38904fd306cSNickeau $tagAttributes->addOutputAttributeValue("srcdoc", $iframeSrcValue); 39004fd306cSNickeau 39104fd306cSNickeau // Code bar with button 39204fd306cSNickeau // Credits bar 39304fd306cSNickeau $bar = '<div class="webcode-bar">'; 39404fd306cSNickeau $bar .= '<div class="webcode-bar-item">' . PluginUtility::getDocumentationHyperLink(WebCodeTag::TAG, "Rendered by WebCode", false) . '</div>'; 39504fd306cSNickeau $bar .= '<div class="webcode-bar-item">' . self::addJsFiddleButton($codes, $externalResources, $useConsole, $tagAttributes->getValue("name")) . '</div>'; 39604fd306cSNickeau $bar .= '</div>'; 39704fd306cSNickeau 39804fd306cSNickeau return self::finishIframe($tagAttributes, $bar); 39904fd306cSNickeau 40004fd306cSNickeau 40104fd306cSNickeau } 40204fd306cSNickeau 40304fd306cSNickeau /** 40404fd306cSNickeau * @param array $codes the array containing the codes 40504fd306cSNickeau * @param array $externalResources the attributes of a call (for now the externalResources) 40604fd306cSNickeau * @param bool $useConsole 40704fd306cSNickeau * @param null $snippetTitle 40804fd306cSNickeau * @return string the HTML form code 40904fd306cSNickeau * 41004fd306cSNickeau * Specification, see http://doc.jsfiddle.net/api/post.html 41104fd306cSNickeau */ 41204fd306cSNickeau public static function addJsFiddleButton($codes, $externalResources, $useConsole = false, $snippetTitle = null): string 41304fd306cSNickeau { 41404fd306cSNickeau 41504fd306cSNickeau $postURL = "https://jsfiddle.net/api/post/library/pure/"; //No Framework 41604fd306cSNickeau 41704fd306cSNickeau 41804fd306cSNickeau if ($useConsole) { 41904fd306cSNickeau // If their is a console.log function, add the Firebug Lite support of JsFiddle 42004fd306cSNickeau // Seems to work only with the Edge version of jQuery 42104fd306cSNickeau // $postURL .= "edge/dependencies/Lite/"; 42204fd306cSNickeau // The firebug logging is not working anymore because of 404 42304fd306cSNickeau 42404fd306cSNickeau // Adding them here 42504fd306cSNickeau // The firebug resources for the console.log features 42604fd306cSNickeau try { 42704fd306cSNickeau $externalResources[] = FetcherRawLocalPath::createFromPath(WikiPath::createComboResource(':firebug:firebug-lite.css'))->getFetchUrl()->toString(); 42804fd306cSNickeau $externalResources[] = FetcherRawLocalPath::createFromPath(WikiPath::createComboResource(':firebug:firebug-lite-1.2.js'))->getFetchUrl()->toString(); 42904fd306cSNickeau } catch (ExceptionNotFound $e) { 43004fd306cSNickeau LogUtility::internalError("We were unable to add the firebug css and js. Error: {$e->getMessage()}", WebCodeTag::CANONICAL); 43104fd306cSNickeau } 43204fd306cSNickeau 43304fd306cSNickeau } 43404fd306cSNickeau 43504fd306cSNickeau // The below code is to prevent this JsFiddle bug: https://github.com/jsfiddle/jsfiddle-issues/issues/726 43604fd306cSNickeau // The order of the resources is not guaranteed 43704fd306cSNickeau // We pass then the resources only if their is one resources 43804fd306cSNickeau // Otherwise we pass them as a script element in the HTML. 43904fd306cSNickeau if (count($externalResources) <= 1) { 44004fd306cSNickeau $externalResourcesInput = '<input type="hidden" name="resources" value="' . implode(",", $externalResources) . '"/>'; 44104fd306cSNickeau } else { 44270bbd7f1Sgerardnico $externalResourcesInput = ''; 44370bbd7f1Sgerardnico if (!array_key_exists('html', $codes)) { 44470bbd7f1Sgerardnico $codes['html'] = ''; 44570bbd7f1Sgerardnico } 44604fd306cSNickeau $codes['html'] .= "\n\n\n\n\n<!-- The resources -->\n"; 44704fd306cSNickeau $codes['html'] .= "<!-- They have been added here because their order is not guarantee through the API. -->\n"; 44804fd306cSNickeau $codes['html'] .= "<!-- See: https://github.com/jsfiddle/jsfiddle-issues/issues/726 -->\n"; 44904fd306cSNickeau foreach ($externalResources as $externalResource) { 45004fd306cSNickeau if ($externalResource !== "") { 45104fd306cSNickeau $extension = pathinfo($externalResource)['extension']; 45204fd306cSNickeau switch ($extension) { 45304fd306cSNickeau case "css": 45404fd306cSNickeau $codes['html'] .= "<link href=\"$externalResource\" rel=\"stylesheet\"/>\n"; 45504fd306cSNickeau break; 45604fd306cSNickeau case "js": 45704fd306cSNickeau $codes['html'] .= "<script src=\"$externalResource\"></script>\n"; 45804fd306cSNickeau break; 45904fd306cSNickeau default: 46004fd306cSNickeau $codes['html'] .= "<!-- " . $externalResource . " -->\n"; 46104fd306cSNickeau } 46204fd306cSNickeau } 46304fd306cSNickeau } 46404fd306cSNickeau } 46504fd306cSNickeau 46670bbd7f1Sgerardnico $jsCode = $codes['javascript'] ?? null; 46704fd306cSNickeau $jsPanel = 0; // language for the js specific panel (0 = JavaScript) 46804fd306cSNickeau if (array_key_exists('babel', $codes)) { 46904fd306cSNickeau $jsCode = $codes['babel']; 47004fd306cSNickeau $jsPanel = 3; // 3 = Babel 47104fd306cSNickeau } 47204fd306cSNickeau 47304fd306cSNickeau // Title and description 47404fd306cSNickeau global $ID; 47504fd306cSNickeau $pageTitle = tpl_pagetitle($ID, true); 47604fd306cSNickeau if (!$snippetTitle) { 47704fd306cSNickeau 47804fd306cSNickeau $snippetTitle = "Code from " . $pageTitle; 47904fd306cSNickeau } 48004fd306cSNickeau $description = "Code from the page '" . $pageTitle . "' \n" . wl($ID, $absolute = true); 48104fd306cSNickeau return '<form method="post" action="' . $postURL . '" target="_blank">' . 48204fd306cSNickeau '<input type="hidden" name="title" value="' . htmlentities($snippetTitle) . '"/>' . 48304fd306cSNickeau '<input type="hidden" name="description" value="' . htmlentities($description) . '"/>' . 48470bbd7f1Sgerardnico '<input type="hidden" name="css" value="' . htmlentities($codes['css'] ?? '') . '"/>' . 48570bbd7f1Sgerardnico '<input type="hidden" name="html" value="' . htmlentities("<!-- The HTML -->" . $codes['html'] ?? '') . '"/>' . 48604fd306cSNickeau '<input type="hidden" name="js" value="' . htmlentities($jsCode) . '"/>' . 48704fd306cSNickeau '<input type="hidden" name="panel_js" value="' . htmlentities($jsPanel) . '"/>' . 48804fd306cSNickeau '<input type="hidden" name="wrap" value="b"/>' . //javascript no wrap in body 48904fd306cSNickeau $externalResourcesInput . 49004fd306cSNickeau '<button>Try the code</button>' . 49104fd306cSNickeau '</form>'; 49204fd306cSNickeau 49304fd306cSNickeau } 49404fd306cSNickeau 49504fd306cSNickeau private static function finishIframe(TagAttributes $tagAttributes, string $bar = ""): string 49604fd306cSNickeau { 49704fd306cSNickeau /** 49804fd306cSNickeau * The iframe does not have any width 49904fd306cSNickeau * By default, we set it to 100% and it can be 50004fd306cSNickeau * constraint with the `width` attributes that will 50104fd306cSNickeau * set a a max-width 50204fd306cSNickeau */ 50304fd306cSNickeau $tagAttributes->addStyleDeclarationIfNotSet("width", "100%"); 50404fd306cSNickeau 50504fd306cSNickeau /** 50604fd306cSNickeau * FrameBorder 50704fd306cSNickeau */ 50804fd306cSNickeau $frameBorder = $tagAttributes->getValueAndRemoveIfPresent(WebCodeTag::FRAMEBORDER_ATTRIBUTE); 50904fd306cSNickeau if ($frameBorder !== null && $frameBorder == 0) { 51004fd306cSNickeau $tagAttributes->addStyleDeclarationIfNotSet("border", "none"); 51104fd306cSNickeau } 51204fd306cSNickeau 51304fd306cSNickeau $iFrameHtml = $tagAttributes->toHtmlEnterTag("iframe") . '</iframe>'; 51404fd306cSNickeau return "<div class=\"webcode-wrapper\">" . $iFrameHtml . $bar . '</div>'; 51504fd306cSNickeau } 51604fd306cSNickeau 51704fd306cSNickeau /** 51804fd306cSNickeau * Return the body 51904fd306cSNickeau * @param $codes - the code to apply 52004fd306cSNickeau * @param $useConsole - if the console area should be printed 52104fd306cSNickeau * @return string - the html and javascript 52204fd306cSNickeau */ 52304fd306cSNickeau private static function getBodyHtmlAndJavascript($codes, $useConsole): string 52404fd306cSNickeau { 52504fd306cSNickeau 52604fd306cSNickeau $body = ""; 52704fd306cSNickeau if (array_key_exists('html', $codes)) { 52804fd306cSNickeau // The HTML code 52904fd306cSNickeau $body .= $codes['html']; 53004fd306cSNickeau } 53104fd306cSNickeau // The javascript console area is based at the end of the HTML document 53204fd306cSNickeau if ($useConsole) { 53304fd306cSNickeau 53404fd306cSNickeau $body .= <<<EOF 53504fd306cSNickeau<!-- WebCode Console --> 53604fd306cSNickeau<div class="webcode-console-wrapper"> 53704fd306cSNickeau <p class="webConsoleTitle">Console Output:</p> 53804fd306cSNickeau <div id="webCodeConsole"></div> 53904fd306cSNickeau</div> 54004fd306cSNickeauEOF; 54104fd306cSNickeau } 54204fd306cSNickeau // The javascript comes at the end because it may want to be applied on previous HTML element 54304fd306cSNickeau // as the page load in the IO order, javascript must be placed at the end 54404fd306cSNickeau if (array_key_exists('javascript', $codes)) { 54504fd306cSNickeau /** 54604fd306cSNickeau * The user should escapes the following character * <, >, ", ', \, and &. 54704fd306cSNickeau * because they will interfere with the HTML parser 54804fd306cSNickeau * 54904fd306cSNickeau * The user should write `<\/script>` and note `</script>` 55004fd306cSNickeau */ 55104fd306cSNickeau // The Javascript code 55204fd306cSNickeau $body .= '<script class="webcode-javascript" type="text/javascript">' . $codes['javascript'] . '</script>'; 55304fd306cSNickeau } 55404fd306cSNickeau if (array_key_exists('babel', $codes)) { 55504fd306cSNickeau // The Babel code 55604fd306cSNickeau $body .= '<script type="text/babel">' . $codes['babel'] . '</script>'; 55704fd306cSNickeau } 55804fd306cSNickeau return $body; 55904fd306cSNickeau 56004fd306cSNickeau } 56104fd306cSNickeau 56204fd306cSNickeau private static function getCss($codes): string 56304fd306cSNickeau { 56404fd306cSNickeau if (array_key_exists('css', $codes)) { 56504fd306cSNickeau return '<style>' . $codes['css'] . '</style>'; 56604fd306cSNickeau }; 56704fd306cSNickeau return ""; 56804fd306cSNickeau } 56904fd306cSNickeau} 570