15f891b7eSNickeau<?php 25f891b7eSNickeau/** 35f891b7eSNickeau * Copyright (c) 2021. ComboStrap, Inc. and its affiliates. All Rights Reserved. 45f891b7eSNickeau * 55f891b7eSNickeau * This source code is licensed under the GPL license found in the 65f891b7eSNickeau * COPYING file in the root directory of this source tree. 75f891b7eSNickeau * 85f891b7eSNickeau * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 95f891b7eSNickeau * @author ComboStrap <support@combostrap.com> 105f891b7eSNickeau * 115f891b7eSNickeau */ 125f891b7eSNickeau 135f891b7eSNickeau/** 145f891b7eSNickeau * Plugin Webcode: Show webcode (Css, HTML) in a iframe 155f891b7eSNickeau * 165f891b7eSNickeau */ 175f891b7eSNickeau 185f891b7eSNickeau// must be run within Dokuwiki 19531e725cSNickeauuse ComboStrap\CallStack; 20a6bf47aaSNickeauuse ComboStrap\Dimension; 215f891b7eSNickeauuse ComboStrap\LogUtility; 225f891b7eSNickeauuse ComboStrap\PluginUtility; 23531e725cSNickeauuse ComboStrap\Site; 2421913ab3SNickeauuse ComboStrap\TagAttributes; 255f891b7eSNickeau 265f891b7eSNickeauif (!defined('DOKU_INC')) die(); 275f891b7eSNickeau 285f891b7eSNickeau/** 295f891b7eSNickeau * Webcode 305f891b7eSNickeau */ 315f891b7eSNickeauclass syntax_plugin_combo_webcode extends DokuWiki_Syntax_Plugin 325f891b7eSNickeau{ 335f891b7eSNickeau 345f891b7eSNickeau const EXTERNAL_RESOURCES_ATTRIBUTE_DISPLAY = 'externalResources'; // In the action bar 355f891b7eSNickeau const EXTERNAL_RESOURCES_ATTRIBUTE_KEY = 'externalresources'; // In the code 365f891b7eSNickeau 375f891b7eSNickeau // Simple cache bursting implementation for the webCodeConsole.(js|css) file 385f891b7eSNickeau // They must be incremented manually when they changed 395f891b7eSNickeau const WEB_CSS_VERSION = 1.1; 405f891b7eSNickeau const WEB_CONSOLE_JS_VERSION = 2.1; 415f891b7eSNickeau 425f891b7eSNickeau const TAG = 'webcode'; 435f891b7eSNickeau 445f891b7eSNickeau /** 455f891b7eSNickeau * The tag that have codes 465f891b7eSNickeau */ 47531e725cSNickeau const CODE_TAGS = 48531e725cSNickeau array( 49531e725cSNickeau syntax_plugin_combo_code::CODE_TAG, 50531e725cSNickeau "plugin_combo_code", 51531e725cSNickeau syntax_plugin_combo_codemarkdown::TAG 52531e725cSNickeau ); 535f891b7eSNickeau 545f891b7eSNickeau /** 555f891b7eSNickeau * The attribute names in the array 565f891b7eSNickeau */ 575f891b7eSNickeau const CODES_ATTRIBUTE = "codes"; 585f891b7eSNickeau const USE_CONSOLE_ATTRIBUTE = "useConsole"; 59531e725cSNickeau const RENDERING_MODE_ATTRIBUTE = 'renderingmode'; 6021913ab3SNickeau const RENDERING_ONLY_RESULT = "onlyresult"; 615f891b7eSNickeau 625f891b7eSNickeau /** 63531e725cSNickeau * Marki code 645f891b7eSNickeau */ 65531e725cSNickeau const MARKI_LANG = 'marki'; 66531e725cSNickeau const DOKUWIKI_LANG = 'dw'; 67531e725cSNickeau const MARKIS = [self::MARKI_LANG, self::DOKUWIKI_LANG]; 685f891b7eSNickeau 695f891b7eSNickeau /** 705f891b7eSNickeau * Syntax Type. 715f891b7eSNickeau * 725f891b7eSNickeau * Needs to return one of the mode types defined in $PARSER_MODES in parser.php 735f891b7eSNickeau * @see https://www.dokuwiki.org/devel:syntax_plugins#syntax_types 745f891b7eSNickeau * 755f891b7eSNickeau * container because it may contain header in case of how to 765f891b7eSNickeau */ 775f891b7eSNickeau public function getType() 785f891b7eSNickeau { 795f891b7eSNickeau return 'container'; 805f891b7eSNickeau } 815f891b7eSNickeau 82531e725cSNickeau public function getPType() 83531e725cSNickeau { 84531e725cSNickeau return "stack"; 85531e725cSNickeau } 86531e725cSNickeau 87531e725cSNickeau 885f891b7eSNickeau /** 895f891b7eSNickeau * @return array 905f891b7eSNickeau * Allow which kind of plugin inside 915f891b7eSNickeau * 925f891b7eSNickeau * array('container', 'baseonly','formatting', 'substition', 'protected', 'disabled', 'paragraphs') 935f891b7eSNickeau * 945f891b7eSNickeau */ 955f891b7eSNickeau public function getAllowedTypes() 965f891b7eSNickeau { 975f891b7eSNickeau return array('container', 'baseonly', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs'); 985f891b7eSNickeau } 995f891b7eSNickeau 1005f891b7eSNickeau 1015f891b7eSNickeau public function accepts($mode) 1025f891b7eSNickeau { 10321913ab3SNickeau 10421913ab3SNickeau return syntax_plugin_combo_preformatted::disablePreformatted($mode); 10521913ab3SNickeau 1065f891b7eSNickeau } 1075f891b7eSNickeau 1085f891b7eSNickeau /** 1095f891b7eSNickeau * @see Doku_Parser_Mode::getSort() 1105f891b7eSNickeau * The mode (plugin) with the lowest sort number will win out 1115f891b7eSNickeau * 1125f891b7eSNickeau * See {@link Doku_Parser_Mode_code} 1135f891b7eSNickeau */ 1145f891b7eSNickeau public function getSort() 1155f891b7eSNickeau { 1165f891b7eSNickeau return 99; 1175f891b7eSNickeau } 1185f891b7eSNickeau 1195f891b7eSNickeau /** 1205f891b7eSNickeau * Called before any calls to ConnectTo 1215f891b7eSNickeau * @return void 1225f891b7eSNickeau */ 1235f891b7eSNickeau function preConnect() 1245f891b7eSNickeau { 1255f891b7eSNickeau } 1265f891b7eSNickeau 1275f891b7eSNickeau /** 1285f891b7eSNickeau * Create a pattern that will called this plugin 1295f891b7eSNickeau * 1305f891b7eSNickeau * @param string $mode 1315f891b7eSNickeau * 1325f891b7eSNickeau * All dokuwiki mode can be seen in the parser.php file 1335f891b7eSNickeau * @see Doku_Parser_Mode::connectTo() 1345f891b7eSNickeau */ 1355f891b7eSNickeau public function connectTo($mode) 1365f891b7eSNickeau { 1375f891b7eSNickeau 1385f891b7eSNickeau $pattern = PluginUtility::getContainerTagPattern(self::TAG); 139*9337a630SNickeau $this->Lexer->addEntryPattern($pattern, $mode, PluginUtility::getModeFromTag($this->getPluginComponent())); 1405f891b7eSNickeau 1415f891b7eSNickeau } 1425f891b7eSNickeau 1435f891b7eSNickeau 1445f891b7eSNickeau // This where the addPattern and addExitPattern are defined 1455f891b7eSNickeau public function postConnect() 1465f891b7eSNickeau { 147*9337a630SNickeau $this->Lexer->addExitPattern('</' . self::TAG . '>', PluginUtility::getModeFromTag($this->getPluginComponent())); 1485f891b7eSNickeau } 1495f891b7eSNickeau 1505f891b7eSNickeau 1515f891b7eSNickeau /** 1525f891b7eSNickeau * Handle the match 1535f891b7eSNickeau * You get the match for each pattern in the $match variable 1545f891b7eSNickeau * $state says if it's an entry, exit or match pattern 1555f891b7eSNickeau * 1565f891b7eSNickeau * This is an instruction block and is cached apart from the rendering output 1575f891b7eSNickeau * There is two caches levels 1585f891b7eSNickeau * This cache may be suppressed with the url parameters ?purge=true 1595f891b7eSNickeau * 1605f891b7eSNickeau * The returned values are cached in an array that will be passed to the render method 1615f891b7eSNickeau * The handle function goal is to parse the matched syntax through the pattern function 1625f891b7eSNickeau * and to return the result for use in the renderer 1635f891b7eSNickeau * This result is always cached until the page is modified. 1645f891b7eSNickeau * @param string $match 1655f891b7eSNickeau * @param int $state 1665f891b7eSNickeau * @param int $pos 1675f891b7eSNickeau * @param Doku_Handler $handler 1685f891b7eSNickeau * @return array|bool 1695f891b7eSNickeau * @throws Exception 1705f891b7eSNickeau * @see DokuWiki_Syntax_Plugin::handle() 1715f891b7eSNickeau * 1725f891b7eSNickeau */ 1735f891b7eSNickeau public function handle($match, $state, $pos, Doku_Handler $handler) 1745f891b7eSNickeau { 1755f891b7eSNickeau switch ($state) { 1765f891b7eSNickeau 1775f891b7eSNickeau case DOKU_LEXER_ENTER : 1785f891b7eSNickeau 179531e725cSNickeau // Default 180531e725cSNickeau $defaultAttributes = array(); 181531e725cSNickeau $defaultAttributes['frameborder'] = 1; 182531e725cSNickeau $defaultAttributes['width'] = '100%'; 183531e725cSNickeau $defaultAttributes['name'] = "WebCode iFrame"; 184531e725cSNickeau $defaultAttributes[self::RENDERING_MODE_ATTRIBUTE] = 'story'; 185531e725cSNickeau // 'height' is set by the javascript if not set 186531e725cSNickeau // 'width' and 'scrolling' gets their natural value 1875f891b7eSNickeau 188531e725cSNickeau // Parse and create the call stack array 189531e725cSNickeau $tagAttributes = TagAttributes::createFromTagMatch($match, $defaultAttributes); 190531e725cSNickeau $callStackArray = $tagAttributes->toCallStackArray(); 1915f891b7eSNickeau 1925f891b7eSNickeau return array( 1935f891b7eSNickeau PluginUtility::STATE => $state, 194531e725cSNickeau PluginUtility::ATTRIBUTES => $callStackArray 1955f891b7eSNickeau ); 1965f891b7eSNickeau 1975f891b7eSNickeau 1985f891b7eSNickeau case DOKU_LEXER_UNMATCHED : 1995f891b7eSNickeau 20032b85071SNickeau return PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler); 20132b85071SNickeau 2025f891b7eSNickeau 2035f891b7eSNickeau case DOKU_LEXER_EXIT: 2045f891b7eSNickeau 2055f891b7eSNickeau /** 2065f891b7eSNickeau * Capture all codes 2075f891b7eSNickeau */ 2085f891b7eSNickeau $codes = array(); 2095f891b7eSNickeau /** 2105f891b7eSNickeau * Does the javascript contains a console statement 2115f891b7eSNickeau */ 2125f891b7eSNickeau $useConsole = false; 213531e725cSNickeau 2145f891b7eSNickeau /** 215531e725cSNickeau * Callstack 216531e725cSNickeau */ 217531e725cSNickeau $callStack = CallStack::createFromHandler($handler); 218531e725cSNickeau $openingTag = $callStack->moveToPreviousCorrespondingOpeningCall(); 219531e725cSNickeau $renderingMode = strtolower($openingTag->getAttribute(self::RENDERING_MODE_ATTRIBUTE)); 220531e725cSNickeau 221531e725cSNickeau /** 222531e725cSNickeau * The mime (ie xml,html, ...) and code content are in two differents 223531e725cSNickeau * call. To be able to set the content to the good type 2245f891b7eSNickeau * we keep a trace of it 2255f891b7eSNickeau */ 2265f891b7eSNickeau $actualCodeType = ""; 2275f891b7eSNickeau 22821913ab3SNickeau /** 229531e725cSNickeau * Loop 230531e725cSNickeau */ 231531e725cSNickeau while ($actualTag = $callStack->next()) { 232531e725cSNickeau 233531e725cSNickeau 234531e725cSNickeau $tagName = $actualTag->getTagName(); 235531e725cSNickeau if (in_array($tagName, self::CODE_TAGS)) { 236531e725cSNickeau 237531e725cSNickeau /** 238531e725cSNickeau * Only rendering mode, we don't display the node 239531e725cSNickeau * on all node (enter, exit and unmatched) 24021913ab3SNickeau */ 24121913ab3SNickeau if ($renderingMode == self::RENDERING_ONLY_RESULT) { 242531e725cSNickeau $actualTag->addAttribute(TagAttributes::DISPLAY, "none"); 24321913ab3SNickeau } 24421913ab3SNickeau 245531e725cSNickeau switch ($actualTag->getState()) { 246531e725cSNickeau 247531e725cSNickeau case DOKU_LEXER_ENTER: 2485f891b7eSNickeau // Get the code (The content between the code nodes) 2495f891b7eSNickeau // We ltrim because the match gives us the \n at the beginning and at the end 250531e725cSNickeau $actualCodeType = strtolower(trim($actualTag->getType())); 2515f891b7eSNickeau 2525f891b7eSNickeau // Xml is html 2535f891b7eSNickeau if ($actualCodeType == 'xml') { 2545f891b7eSNickeau $actualCodeType = 'html'; 2555f891b7eSNickeau } 256531e725cSNickeau 257531e725cSNickeau // markdown, dokuwiki is marki 258531e725cSNickeau if (in_array($actualCodeType, ['md', 'markdown', 'dw'])) { 259531e725cSNickeau $actualCodeType = self::MARKI_LANG; 260531e725cSNickeau } 261531e725cSNickeau 26221913ab3SNickeau // The code for a language may be scattered in multiple block 2635f891b7eSNickeau if (!isset($codes[$actualCodeType])) { 2645f891b7eSNickeau $codes[$actualCodeType] = ""; 2655f891b7eSNickeau } 26621913ab3SNickeau 267531e725cSNickeau continue 2; 2685f891b7eSNickeau 269531e725cSNickeau case DOKU_LEXER_UNMATCHED: 2705f891b7eSNickeau 271531e725cSNickeau $codeContent = $actualTag->getPluginData()[PluginUtility::PAYLOAD]; 2725f891b7eSNickeau 2735f891b7eSNickeau if (empty($actualCodeType)) { 2745f891b7eSNickeau LogUtility::msg("The type of the code should not be null for the code content " . $codeContent, LogUtility::LVL_MSG_WARNING, self::TAG); 275531e725cSNickeau continue 2; 2765f891b7eSNickeau } 2775f891b7eSNickeau 2785f891b7eSNickeau // Append it 2795f891b7eSNickeau $codes[$actualCodeType] = $codes[$actualCodeType] . $codeContent; 2805f891b7eSNickeau 2815f891b7eSNickeau // Check if a javascript console function is used, only if the flag is not set to true 2825f891b7eSNickeau if (!$useConsole == true) { 2835f891b7eSNickeau if (in_array($actualCodeType, array('babel', 'javascript', 'html', 'xml'))) { 2845f891b7eSNickeau // if the code contains 'console.' 2855f891b7eSNickeau $result = preg_match('/' . 'console\.' . '/is', $codeContent); 2865f891b7eSNickeau if ($result) { 2875f891b7eSNickeau $useConsole = true; 2885f891b7eSNickeau } 2895f891b7eSNickeau } 2905f891b7eSNickeau } 2915f891b7eSNickeau // Reset 2925f891b7eSNickeau $actualCodeType = ""; 293531e725cSNickeau break; 294531e725cSNickeau 2955f891b7eSNickeau } 2965f891b7eSNickeau } 297531e725cSNickeau 2985f891b7eSNickeau } 299531e725cSNickeau 3005f891b7eSNickeau return array( 3015f891b7eSNickeau PluginUtility::STATE => $state, 3025f891b7eSNickeau self::CODES_ATTRIBUTE => $codes, 303531e725cSNickeau self::USE_CONSOLE_ATTRIBUTE => $useConsole, 304531e725cSNickeau PluginUtility::ATTRIBUTES => $openingTag->getAttributes() 3055f891b7eSNickeau ); 3065f891b7eSNickeau 3075f891b7eSNickeau } 3085f891b7eSNickeau return false; 3095f891b7eSNickeau 3105f891b7eSNickeau } 3115f891b7eSNickeau 3125f891b7eSNickeau /** 3135f891b7eSNickeau * Render the output 3145f891b7eSNickeau * @param string $mode 3155f891b7eSNickeau * @param Doku_Renderer $renderer 3165f891b7eSNickeau * @param array $data - what the function handle() return'ed 3175f891b7eSNickeau * @return bool - rendered correctly (not used) 3185f891b7eSNickeau * 3195f891b7eSNickeau * The rendering process 3205f891b7eSNickeau * @see DokuWiki_Syntax_Plugin::render() 3215f891b7eSNickeau * 3225f891b7eSNickeau */ 3235f891b7eSNickeau public function render($mode, Doku_Renderer $renderer, $data) 3245f891b7eSNickeau { 3255f891b7eSNickeau // The $data variable comes from the handle() function 3265f891b7eSNickeau // 3275f891b7eSNickeau // $mode = 'xhtml' means that we output html 3285f891b7eSNickeau // There is other mode such as metadata where you can output data for the headers (Not 100% sure) 3295f891b7eSNickeau if ($mode == 'xhtml') { 3305f891b7eSNickeau 3315f891b7eSNickeau 3325f891b7eSNickeau /** @var Doku_Renderer_xhtml $renderer */ 3335f891b7eSNickeau 3345f891b7eSNickeau $state = $data[PluginUtility::STATE]; 3355f891b7eSNickeau switch ($state) { 3365f891b7eSNickeau 3375f891b7eSNickeau 3385f891b7eSNickeau case DOKU_LEXER_UNMATCHED : 3395f891b7eSNickeau 34032b85071SNickeau $renderer->doc .= PluginUtility::renderUnmatched($data); 3415f891b7eSNickeau break; 3425f891b7eSNickeau 3435f891b7eSNickeau case DOKU_LEXER_EXIT : 3445f891b7eSNickeau $codes = $data[self::CODES_ATTRIBUTE]; 345531e725cSNickeau $callStackArray = $data[PluginUtility::ATTRIBUTES]; 346531e725cSNickeau $iFrameAttributes = TagAttributes::createFromCallStackArray($callStackArray, self::TAG); 347531e725cSNickeau 3485f891b7eSNickeau // Create the real output of webcode 3495f891b7eSNickeau if (sizeof($codes) == 0) { 3505f891b7eSNickeau return false; 3515f891b7eSNickeau } 3525f891b7eSNickeau 353531e725cSNickeau // Credits bar 354531e725cSNickeau $bar = '<div class="webcode-bar">'; 355531e725cSNickeau 3565f891b7eSNickeau 3575f891b7eSNickeau // Dokuwiki Code ? 358531e725cSNickeau if (array_key_exists(self::MARKI_LANG, $codes)) { 3595f891b7eSNickeau 360531e725cSNickeau $markiCode = $codes[self::MARKI_LANG]; 361531e725cSNickeau /** 362531e725cSNickeau * By default, markup code 363531e725cSNickeau * is rendered inside the page 364531e725cSNickeau * We got less problem such as iframe overflow 365531e725cSNickeau * due to lazy loading, such as relative link, ... 366531e725cSNickeau * 367531e725cSNickeau */ 368531e725cSNickeau if (!$iFrameAttributes->hasComponentAttribute("iframe")) { 369531e725cSNickeau $renderer->doc .= PluginUtility::render($markiCode); 370531e725cSNickeau return true; 371531e725cSNickeau } 372531e725cSNickeau 373531e725cSNickeau $queryParams = array( 374531e725cSNickeau 'call' => action_plugin_combo_webcode::CALL_ID, 375531e725cSNickeau action_plugin_combo_webcode::MARKI_PARAM => $markiCode 376531e725cSNickeau ); 377531e725cSNickeau $queryString = http_build_query($queryParams); 378531e725cSNickeau $url = Site::getAjaxUrl() . "?$queryString"; 379531e725cSNickeau $iFrameAttributes->addHtmlAttributeValue("src", $url); 3805f891b7eSNickeau 3815f891b7eSNickeau } else { 3825f891b7eSNickeau 3835f891b7eSNickeau 3845f891b7eSNickeau // Js, Html, Css 385531e725cSNickeau $iframeSrcValue = '<html><head>'; 386e55e4e71SNickeau $iframeSrcValue .= '<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>'; 387531e725cSNickeau $iframeSrcValue .= '<title>Made by WebCode</title>'; 388e55e4e71SNickeau $iframeSrcValue .= '<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css"/>'; 3895f891b7eSNickeau 3905f891b7eSNickeau 3915f891b7eSNickeau // External Resources such as css stylesheet or js 392531e725cSNickeau $externalResources = []; 393531e725cSNickeau if ($iFrameAttributes->hasComponentAttribute(self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY)) { 394531e725cSNickeau $resources = $iFrameAttributes->getValueAndRemove(self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY); 395531e725cSNickeau $externalResources = explode(",", $resources); 3965f891b7eSNickeau } 3975f891b7eSNickeau 3985f891b7eSNickeau // Babel Preprocessor, if babel is used, add it to the external resources 3995f891b7eSNickeau if (array_key_exists('babel', $codes)) { 4005f891b7eSNickeau $babelMin = "https://unpkg.com/babel-standalone@6/babel.min.js"; 4015f891b7eSNickeau // a load of babel invoke it (be sure to not have it twice 4025f891b7eSNickeau if (!(array_key_exists($babelMin, $externalResources))) { 4035f891b7eSNickeau $externalResources[] = $babelMin; 4045f891b7eSNickeau } 4055f891b7eSNickeau } 4065f891b7eSNickeau 4075f891b7eSNickeau // Add the external resources 4085f891b7eSNickeau foreach ($externalResources as $externalResource) { 4095f891b7eSNickeau $pathInfo = pathinfo($externalResource); 4105f891b7eSNickeau $fileExtension = $pathInfo['extension']; 4115f891b7eSNickeau switch ($fileExtension) { 4125f891b7eSNickeau case 'css': 413e55e4e71SNickeau $iframeSrcValue .= '<link rel="stylesheet" type="text/css" href="' . $externalResource . '"/>'; 4145f891b7eSNickeau break; 4155f891b7eSNickeau case 'js': 416531e725cSNickeau $iframeSrcValue .= '<script type="text/javascript" src="' . $externalResource . '"></script>'; 4175f891b7eSNickeau break; 4185f891b7eSNickeau } 4195f891b7eSNickeau } 4205f891b7eSNickeau 4215f891b7eSNickeau 4225f891b7eSNickeau // WebConsole style sheet 423531e725cSNickeau $iframeSrcValue .= '<link rel="stylesheet" type="text/css" href="' . PluginUtility::getResourceBaseUrl() . '/webcode/webcode-iframe.css?ver=' . self::WEB_CSS_VERSION . '"/>'; 4245f891b7eSNickeau 42521913ab3SNickeau // A little margin to make it neater 42621913ab3SNickeau // that can be overwritten via cascade 427531e725cSNickeau $iframeSrcValue .= '<style>body { margin:10px } /* default margin */</style>'; 42821913ab3SNickeau 42921913ab3SNickeau // The css 4305f891b7eSNickeau if (array_key_exists('css', $codes)) { 431531e725cSNickeau $iframeSrcValue .= '<!-- The CSS code -->'; 432531e725cSNickeau $iframeSrcValue .= '<style>' . $codes['css'] . '</style>'; 4335f891b7eSNickeau }; 434531e725cSNickeau $iframeSrcValue .= '</head><body>'; 4355f891b7eSNickeau if (array_key_exists('html', $codes)) { 436531e725cSNickeau $iframeSrcValue .= '<!-- The HTML code -->'; 437531e725cSNickeau $iframeSrcValue .= $codes['html']; 4385f891b7eSNickeau } 4395f891b7eSNickeau // The javascript console area is based at the end of the HTML document 4405f891b7eSNickeau $useConsole = $data[self::USE_CONSOLE_ATTRIBUTE]; 4415f891b7eSNickeau if ($useConsole) { 442531e725cSNickeau $iframeSrcValue .= '<!-- WebCode Console -->'; 443e55e4e71SNickeau $iframeSrcValue .= '<div><p class="webConsoleTitle">Console Output:</p>'; 444e55e4e71SNickeau $iframeSrcValue .= '<div id="webCodeConsole"></div>'; 445e55e4e71SNickeau $iframeSrcValue .= '<script type="text/javascript" src="' . PluginUtility::getResourceBaseUrl() . '/webcode/webcode-console.js?ver=' . self::WEB_CONSOLE_JS_VERSION . '"></script>'; 446531e725cSNickeau $iframeSrcValue .= '</div>'; 4475f891b7eSNickeau } 4485f891b7eSNickeau // The javascript comes at the end because it may want to be applied on previous HTML element 4495f891b7eSNickeau // as the page load in the IO order, javascript must be placed at the end 4505f891b7eSNickeau if (array_key_exists('javascript', $codes)) { 451531e725cSNickeau $iframeSrcValue .= '<!-- The Javascript code -->'; 452531e725cSNickeau $iframeSrcValue .= '<script type="text/javascript">' . $codes['javascript'] . '</script>'; 4535f891b7eSNickeau } 4545f891b7eSNickeau if (array_key_exists('babel', $codes)) { 455531e725cSNickeau $iframeSrcValue .= '<!-- The Babel code -->'; 456531e725cSNickeau $iframeSrcValue .= '<script type="text/babel">' . $codes['babel'] . '</script>'; 4575f891b7eSNickeau } 458531e725cSNickeau $iframeSrcValue .= '</body></html>'; 459e55e4e71SNickeau $iFrameAttributes->addHtmlAttributeValue("srcdoc", $iframeSrcValue); 4605f891b7eSNickeau 461531e725cSNickeau // Code bar with button 462531e725cSNickeau $bar .= '<div class="webcode-bar-item">' . PluginUtility::getUrl(self::TAG, "Rendered by WebCode", false) . '</div>'; 463531e725cSNickeau $bar .= '<div class="webcode-bar-item">' . $this->addJsFiddleButton($codes, $externalResources, $useConsole, $iFrameAttributes->getValue("name")) . '</div>'; 4645f891b7eSNickeau 465531e725cSNickeau 4665f891b7eSNickeau } 4675f891b7eSNickeau 468531e725cSNickeau /** 469531e725cSNickeau * If there is no height 470531e725cSNickeau */ 471a6bf47aaSNickeau if (!$iFrameAttributes->hasComponentAttribute(Dimension::HEIGHT_KEY)) { 4725f891b7eSNickeau 473531e725cSNickeau /** 474531e725cSNickeau * Adjust the height attribute 475531e725cSNickeau * of the iframe element 476531e725cSNickeau * Any styling attribute would take over 477531e725cSNickeau */ 478531e725cSNickeau PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar(self::TAG); 479531e725cSNickeau /** 480531e725cSNickeau * CSS Height auto works when an image is loaded asynchronously but not 481531e725cSNickeau * when there is only text in the iframe 482531e725cSNickeau */ 483531e725cSNickeau //$iFrameAttributes->addStyleDeclaration("height", "auto"); 484531e725cSNickeau /** 485531e725cSNickeau * Due to margin at the bottom with css height=auto, 486531e725cSNickeau * we may see a scroll bar 487531e725cSNickeau * This block of code is to avoid scrolling, 488531e725cSNickeau * then scrolling = no if not set 489531e725cSNickeau */ 490531e725cSNickeau if (!$iFrameAttributes->hasComponentAttribute("scrolling")) { 491531e725cSNickeau $iFrameAttributes->addHtmlAttributeValue("scrolling", "no"); 4925f891b7eSNickeau } 4935f891b7eSNickeau 4945f891b7eSNickeau } 4955f891b7eSNickeau 496531e725cSNickeau 497531e725cSNickeau PluginUtility::getSnippetManager()->attachCssSnippetForBar(self::TAG); 498531e725cSNickeau 499e55e4e71SNickeau /** 500e55e4e71SNickeau * The iframe does not have any width 501e55e4e71SNickeau * By default, we set it to 100% and it can be 502e55e4e71SNickeau * constraint with the `width` attributes that will 503e55e4e71SNickeau * set a a max-width 504e55e4e71SNickeau */ 505e55e4e71SNickeau $iFrameAttributes->addStyleDeclaration("width","100%"); 506e55e4e71SNickeau 507531e725cSNickeau $iFrameHtml = $iFrameAttributes->toHtmlEnterTag("iframe") . '</iframe>'; 508531e725cSNickeau $bar .= '</div>'; // close the bar 509531e725cSNickeau $renderer->doc .= "<div class=\"webcode-wrapper\">" . $iFrameHtml . $bar . '</div>'; 510531e725cSNickeau 511531e725cSNickeau 5125f891b7eSNickeau break; 5135f891b7eSNickeau } 5145f891b7eSNickeau 5155f891b7eSNickeau return true; 5165f891b7eSNickeau } 5175f891b7eSNickeau return false; 5185f891b7eSNickeau } 5195f891b7eSNickeau 5205f891b7eSNickeau /** 5215f891b7eSNickeau * @param array $codes the array containing the codes 522531e725cSNickeau * @param array $externalResources the attributes of a call (for now the externalResources) 523531e725cSNickeau * @param bool $useConsole 524531e725cSNickeau * @param string $snippetTitle 5255f891b7eSNickeau * @return string the HTML form code 5265f891b7eSNickeau * 5275f891b7eSNickeau * Specification, see http://doc.jsfiddle.net/api/post.html 5285f891b7eSNickeau */ 529531e725cSNickeau public function addJsFiddleButton($codes, $externalResources, $useConsole = false, $snippetTitle = null) 5305f891b7eSNickeau { 5315f891b7eSNickeau 5325f891b7eSNickeau $postURL = "https://jsfiddle.net/api/post/library/pure/"; //No Framework 5335f891b7eSNickeau 5345f891b7eSNickeau 535531e725cSNickeau if ($useConsole) { 5365f891b7eSNickeau // If their is a console.log function, add the Firebug Lite support of JsFiddle 5375f891b7eSNickeau // Seems to work only with the Edge version of jQuery 5385f891b7eSNickeau // $postURL .= "edge/dependencies/Lite/"; 5395f891b7eSNickeau // The firebug logging is not working anymore because of 404 5405f891b7eSNickeau // Adding them here 5415f891b7eSNickeau $externalResources[] = 'The firebug resources for the console.log features'; 5425f891b7eSNickeau $externalResources[] = PluginUtility::getResourceBaseUrl() . '/firebug/firebug-lite.css'; 5435f891b7eSNickeau $externalResources[] = PluginUtility::getResourceBaseUrl() . '/firebug/firebug-lite-1.2.js'; 5445f891b7eSNickeau } 5455f891b7eSNickeau 5465f891b7eSNickeau // The below code is to prevent this JsFiddle bug: https://github.com/jsfiddle/jsfiddle-issues/issues/726 5475f891b7eSNickeau // The order of the resources is not guaranteed 5485f891b7eSNickeau // We pass then the resources only if their is one resources 5495f891b7eSNickeau // Otherwise we pass them as a script element in the HTML. 5505f891b7eSNickeau if (count($externalResources) <= 1) { 551*9337a630SNickeau $externalResourcesInput = '<input type="hidden" name="resources" value="' . implode(",", $externalResources) . '"/>'; 5525f891b7eSNickeau } else { 5535f891b7eSNickeau $codes['html'] .= "\n\n\n\n\n<!-- The resources -->\n"; 5545f891b7eSNickeau $codes['html'] .= "<!-- They have been added here because their order is not guarantee through the API. -->\n"; 5555f891b7eSNickeau $codes['html'] .= "<!-- See: https://github.com/jsfiddle/jsfiddle-issues/issues/726 -->\n"; 5565f891b7eSNickeau foreach ($externalResources as $externalResource) { 5575f891b7eSNickeau if ($externalResource != "") { 5585f891b7eSNickeau $extension = pathinfo($externalResource)['extension']; 5595f891b7eSNickeau switch ($extension) { 5605f891b7eSNickeau case "css": 5615f891b7eSNickeau $codes['html'] .= "<link href=\"" . $externalResource . "\" rel=\"stylesheet\">\n"; 5625f891b7eSNickeau break; 5635f891b7eSNickeau case "js": 5645f891b7eSNickeau $codes['html'] .= "<script src=\"" . $externalResource . "\"></script>\n"; 5655f891b7eSNickeau break; 5665f891b7eSNickeau default: 5675f891b7eSNickeau $codes['html'] .= "<!-- " . $externalResource . " -->\n"; 5685f891b7eSNickeau } 5695f891b7eSNickeau } 5705f891b7eSNickeau } 5715f891b7eSNickeau } 5725f891b7eSNickeau 5735f891b7eSNickeau $jsCode = $codes['javascript']; 5745f891b7eSNickeau $jsPanel = 0; // language for the js specific panel (0 = JavaScript) 5755f891b7eSNickeau if (array_key_exists('babel', $codes)) { 5765f891b7eSNickeau $jsCode = $codes['babel']; 5775f891b7eSNickeau $jsPanel = 3; // 3 = Babel 5785f891b7eSNickeau } 5795f891b7eSNickeau 5805f891b7eSNickeau // Title and description 5815f891b7eSNickeau global $ID; 5825f891b7eSNickeau $pageTitle = tpl_pagetitle($ID, true); 583531e725cSNickeau if (!$snippetTitle) { 5845f891b7eSNickeau 585531e725cSNickeau $snippetTitle = "Code from " . $pageTitle; 5865f891b7eSNickeau } 5875f891b7eSNickeau $description = "Code from the page '" . $pageTitle . "' \n" . wl($ID, $absolute = true); 5885f891b7eSNickeau return '<form method="post" action="' . $postURL . '" target="_blank">' . 589*9337a630SNickeau '<input type="hidden" name="title" value="' . htmlentities($snippetTitle) . '"/>' . 590*9337a630SNickeau '<input type="hidden" name="description" value="' . htmlentities($description) . '"/>' . 591*9337a630SNickeau '<input type="hidden" name="css" value="' . htmlentities($codes['css']) . '"/>' . 592*9337a630SNickeau '<input type="hidden" name="html" value="' . htmlentities("<!-- The HTML -->" . $codes['html']) . '"/>' . 593*9337a630SNickeau '<input type="hidden" name="js" value="' . htmlentities($jsCode) . '"/>' . 594*9337a630SNickeau '<input type="hidden" name="panel_js" value="' . htmlentities($jsPanel) . '"/>' . 595*9337a630SNickeau '<input type="hidden" name="wrap" value="b"/>' . //javascript no wrap in body 5965f891b7eSNickeau $externalResourcesInput . 5975f891b7eSNickeau '<button>Try the code</button>' . 5985f891b7eSNickeau '</form>'; 5995f891b7eSNickeau 6005f891b7eSNickeau } 6015f891b7eSNickeau 6025f891b7eSNickeau /** 6035f891b7eSNickeau * @param $codes the array containing the codes 6045f891b7eSNickeau * @param $attributes the attributes of a call (for now the externalResources) 6055f891b7eSNickeau * @return string the HTML form code 6065f891b7eSNickeau */ 6075f891b7eSNickeau public function addCodePenButton($codes, $attributes) 6085f891b7eSNickeau { 6095f891b7eSNickeau // TODO 6105f891b7eSNickeau // http://blog.codepen.io/documentation/api/prefill/ 6115f891b7eSNickeau } 6125f891b7eSNickeau 6135f891b7eSNickeau 6145f891b7eSNickeau} 615