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; 205f891b7eSNickeauuse ComboStrap\LogUtility; 215f891b7eSNickeauuse ComboStrap\PluginUtility; 22531e725cSNickeauuse ComboStrap\Site; 2321913ab3SNickeauuse ComboStrap\TagAttributes; 245f891b7eSNickeau 255f891b7eSNickeauif (!defined('DOKU_INC')) die(); 265f891b7eSNickeau 275f891b7eSNickeau/** 285f891b7eSNickeau * Webcode 295f891b7eSNickeau */ 305f891b7eSNickeauclass syntax_plugin_combo_webcode extends DokuWiki_Syntax_Plugin 315f891b7eSNickeau{ 325f891b7eSNickeau 335f891b7eSNickeau const EXTERNAL_RESOURCES_ATTRIBUTE_DISPLAY = 'externalResources'; // In the action bar 345f891b7eSNickeau const EXTERNAL_RESOURCES_ATTRIBUTE_KEY = 'externalresources'; // In the code 355f891b7eSNickeau 365f891b7eSNickeau // Simple cache bursting implementation for the webCodeConsole.(js|css) file 375f891b7eSNickeau // They must be incremented manually when they changed 385f891b7eSNickeau const WEB_CSS_VERSION = 1.1; 395f891b7eSNickeau const WEB_CONSOLE_JS_VERSION = 2.1; 405f891b7eSNickeau 415f891b7eSNickeau const TAG = 'webcode'; 425f891b7eSNickeau 435f891b7eSNickeau /** 445f891b7eSNickeau * The tag that have codes 455f891b7eSNickeau */ 46531e725cSNickeau const CODE_TAGS = 47531e725cSNickeau array( 48531e725cSNickeau syntax_plugin_combo_code::CODE_TAG, 49531e725cSNickeau "plugin_combo_code", 50531e725cSNickeau syntax_plugin_combo_codemarkdown::TAG 51531e725cSNickeau ); 525f891b7eSNickeau 535f891b7eSNickeau /** 545f891b7eSNickeau * The attribute names in the array 555f891b7eSNickeau */ 565f891b7eSNickeau const CODES_ATTRIBUTE = "codes"; 575f891b7eSNickeau const USE_CONSOLE_ATTRIBUTE = "useConsole"; 58531e725cSNickeau const RENDERING_MODE_ATTRIBUTE = 'renderingmode'; 5921913ab3SNickeau const RENDERING_ONLY_RESULT = "onlyresult"; 605f891b7eSNickeau 615f891b7eSNickeau /** 62531e725cSNickeau * Marki code 635f891b7eSNickeau */ 64531e725cSNickeau const MARKI_LANG = 'marki'; 65531e725cSNickeau const DOKUWIKI_LANG = 'dw'; 66531e725cSNickeau const MARKIS = [self::MARKI_LANG, self::DOKUWIKI_LANG]; 675f891b7eSNickeau 685f891b7eSNickeau /** 695f891b7eSNickeau * Syntax Type. 705f891b7eSNickeau * 715f891b7eSNickeau * Needs to return one of the mode types defined in $PARSER_MODES in parser.php 725f891b7eSNickeau * @see https://www.dokuwiki.org/devel:syntax_plugins#syntax_types 735f891b7eSNickeau * 745f891b7eSNickeau * container because it may contain header in case of how to 755f891b7eSNickeau */ 765f891b7eSNickeau public function getType() 775f891b7eSNickeau { 785f891b7eSNickeau return 'container'; 795f891b7eSNickeau } 805f891b7eSNickeau 81531e725cSNickeau public function getPType() 82531e725cSNickeau { 83531e725cSNickeau return "stack"; 84531e725cSNickeau } 85531e725cSNickeau 86531e725cSNickeau 875f891b7eSNickeau /** 885f891b7eSNickeau * @return array 895f891b7eSNickeau * Allow which kind of plugin inside 905f891b7eSNickeau * 915f891b7eSNickeau * array('container', 'baseonly','formatting', 'substition', 'protected', 'disabled', 'paragraphs') 925f891b7eSNickeau * 935f891b7eSNickeau */ 945f891b7eSNickeau public function getAllowedTypes() 955f891b7eSNickeau { 965f891b7eSNickeau return array('container', 'baseonly', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs'); 975f891b7eSNickeau } 985f891b7eSNickeau 995f891b7eSNickeau 1005f891b7eSNickeau public function accepts($mode) 1015f891b7eSNickeau { 10221913ab3SNickeau 10321913ab3SNickeau return syntax_plugin_combo_preformatted::disablePreformatted($mode); 10421913ab3SNickeau 1055f891b7eSNickeau } 1065f891b7eSNickeau 1075f891b7eSNickeau /** 1085f891b7eSNickeau * @see Doku_Parser_Mode::getSort() 1095f891b7eSNickeau * The mode (plugin) with the lowest sort number will win out 1105f891b7eSNickeau * 1115f891b7eSNickeau * See {@link Doku_Parser_Mode_code} 1125f891b7eSNickeau */ 1135f891b7eSNickeau public function getSort() 1145f891b7eSNickeau { 1155f891b7eSNickeau return 99; 1165f891b7eSNickeau } 1175f891b7eSNickeau 1185f891b7eSNickeau /** 1195f891b7eSNickeau * Called before any calls to ConnectTo 1205f891b7eSNickeau * @return void 1215f891b7eSNickeau */ 1225f891b7eSNickeau function preConnect() 1235f891b7eSNickeau { 1245f891b7eSNickeau } 1255f891b7eSNickeau 1265f891b7eSNickeau /** 1275f891b7eSNickeau * Create a pattern that will called this plugin 1285f891b7eSNickeau * 1295f891b7eSNickeau * @param string $mode 1305f891b7eSNickeau * 1315f891b7eSNickeau * All dokuwiki mode can be seen in the parser.php file 1325f891b7eSNickeau * @see Doku_Parser_Mode::connectTo() 1335f891b7eSNickeau */ 1345f891b7eSNickeau public function connectTo($mode) 1355f891b7eSNickeau { 1365f891b7eSNickeau 1375f891b7eSNickeau $pattern = PluginUtility::getContainerTagPattern(self::TAG); 1385f891b7eSNickeau $this->Lexer->addEntryPattern($pattern, $mode, PluginUtility::getModeForComponent($this->getPluginComponent())); 1395f891b7eSNickeau 1405f891b7eSNickeau } 1415f891b7eSNickeau 1425f891b7eSNickeau 1435f891b7eSNickeau // This where the addPattern and addExitPattern are defined 1445f891b7eSNickeau public function postConnect() 1455f891b7eSNickeau { 1465f891b7eSNickeau $this->Lexer->addExitPattern('</' . self::TAG . '>', PluginUtility::getModeForComponent($this->getPluginComponent())); 1475f891b7eSNickeau } 1485f891b7eSNickeau 1495f891b7eSNickeau 1505f891b7eSNickeau /** 1515f891b7eSNickeau * Handle the match 1525f891b7eSNickeau * You get the match for each pattern in the $match variable 1535f891b7eSNickeau * $state says if it's an entry, exit or match pattern 1545f891b7eSNickeau * 1555f891b7eSNickeau * This is an instruction block and is cached apart from the rendering output 1565f891b7eSNickeau * There is two caches levels 1575f891b7eSNickeau * This cache may be suppressed with the url parameters ?purge=true 1585f891b7eSNickeau * 1595f891b7eSNickeau * The returned values are cached in an array that will be passed to the render method 1605f891b7eSNickeau * The handle function goal is to parse the matched syntax through the pattern function 1615f891b7eSNickeau * and to return the result for use in the renderer 1625f891b7eSNickeau * This result is always cached until the page is modified. 1635f891b7eSNickeau * @param string $match 1645f891b7eSNickeau * @param int $state 1655f891b7eSNickeau * @param int $pos 1665f891b7eSNickeau * @param Doku_Handler $handler 1675f891b7eSNickeau * @return array|bool 1685f891b7eSNickeau * @throws Exception 1695f891b7eSNickeau * @see DokuWiki_Syntax_Plugin::handle() 1705f891b7eSNickeau * 1715f891b7eSNickeau */ 1725f891b7eSNickeau public function handle($match, $state, $pos, Doku_Handler $handler) 1735f891b7eSNickeau { 1745f891b7eSNickeau switch ($state) { 1755f891b7eSNickeau 1765f891b7eSNickeau case DOKU_LEXER_ENTER : 1775f891b7eSNickeau 178531e725cSNickeau // Default 179531e725cSNickeau $defaultAttributes = array(); 180531e725cSNickeau $defaultAttributes['frameborder'] = 1; 181531e725cSNickeau $defaultAttributes['width'] = '100%'; 182531e725cSNickeau $defaultAttributes['name'] = "WebCode iFrame"; 183531e725cSNickeau $defaultAttributes[self::RENDERING_MODE_ATTRIBUTE] = 'story'; 184531e725cSNickeau // 'height' is set by the javascript if not set 185531e725cSNickeau // 'width' and 'scrolling' gets their natural value 1865f891b7eSNickeau 187531e725cSNickeau // Parse and create the call stack array 188531e725cSNickeau $tagAttributes = TagAttributes::createFromTagMatch($match, $defaultAttributes); 189531e725cSNickeau $callStackArray = $tagAttributes->toCallStackArray(); 1905f891b7eSNickeau 1915f891b7eSNickeau return array( 1925f891b7eSNickeau PluginUtility::STATE => $state, 193531e725cSNickeau PluginUtility::ATTRIBUTES => $callStackArray 1945f891b7eSNickeau ); 1955f891b7eSNickeau 1965f891b7eSNickeau 1975f891b7eSNickeau case DOKU_LEXER_UNMATCHED : 1985f891b7eSNickeau 19932b85071SNickeau return PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler); 20032b85071SNickeau 2015f891b7eSNickeau 2025f891b7eSNickeau case DOKU_LEXER_EXIT: 2035f891b7eSNickeau 2045f891b7eSNickeau /** 2055f891b7eSNickeau * Capture all codes 2065f891b7eSNickeau */ 2075f891b7eSNickeau $codes = array(); 2085f891b7eSNickeau /** 2095f891b7eSNickeau * Does the javascript contains a console statement 2105f891b7eSNickeau */ 2115f891b7eSNickeau $useConsole = false; 212531e725cSNickeau 2135f891b7eSNickeau /** 214531e725cSNickeau * Callstack 215531e725cSNickeau */ 216531e725cSNickeau $callStack = CallStack::createFromHandler($handler); 217531e725cSNickeau $openingTag = $callStack->moveToPreviousCorrespondingOpeningCall(); 218531e725cSNickeau $renderingMode = strtolower($openingTag->getAttribute(self::RENDERING_MODE_ATTRIBUTE)); 219531e725cSNickeau 220531e725cSNickeau /** 221531e725cSNickeau * The mime (ie xml,html, ...) and code content are in two differents 222531e725cSNickeau * call. To be able to set the content to the good type 2235f891b7eSNickeau * we keep a trace of it 2245f891b7eSNickeau */ 2255f891b7eSNickeau $actualCodeType = ""; 2265f891b7eSNickeau 22721913ab3SNickeau /** 228531e725cSNickeau * Loop 229531e725cSNickeau */ 230531e725cSNickeau while ($actualTag = $callStack->next()) { 231531e725cSNickeau 232531e725cSNickeau 233531e725cSNickeau $tagName = $actualTag->getTagName(); 234531e725cSNickeau if (in_array($tagName, self::CODE_TAGS)) { 235531e725cSNickeau 236531e725cSNickeau /** 237531e725cSNickeau * Only rendering mode, we don't display the node 238531e725cSNickeau * on all node (enter, exit and unmatched) 23921913ab3SNickeau */ 24021913ab3SNickeau if ($renderingMode == self::RENDERING_ONLY_RESULT) { 241531e725cSNickeau $actualTag->addAttribute(TagAttributes::DISPLAY, "none"); 24221913ab3SNickeau } 24321913ab3SNickeau 244531e725cSNickeau switch ($actualTag->getState()) { 245531e725cSNickeau 246531e725cSNickeau case DOKU_LEXER_ENTER: 2475f891b7eSNickeau // Get the code (The content between the code nodes) 2485f891b7eSNickeau // We ltrim because the match gives us the \n at the beginning and at the end 249531e725cSNickeau $actualCodeType = strtolower(trim($actualTag->getType())); 2505f891b7eSNickeau 2515f891b7eSNickeau // Xml is html 2525f891b7eSNickeau if ($actualCodeType == 'xml') { 2535f891b7eSNickeau $actualCodeType = 'html'; 2545f891b7eSNickeau } 255531e725cSNickeau 256531e725cSNickeau // markdown, dokuwiki is marki 257531e725cSNickeau if (in_array($actualCodeType, ['md', 'markdown', 'dw'])) { 258531e725cSNickeau $actualCodeType = self::MARKI_LANG; 259531e725cSNickeau } 260531e725cSNickeau 26121913ab3SNickeau // The code for a language may be scattered in multiple block 2625f891b7eSNickeau if (!isset($codes[$actualCodeType])) { 2635f891b7eSNickeau $codes[$actualCodeType] = ""; 2645f891b7eSNickeau } 26521913ab3SNickeau 266531e725cSNickeau continue 2; 2675f891b7eSNickeau 268531e725cSNickeau case DOKU_LEXER_UNMATCHED: 2695f891b7eSNickeau 270531e725cSNickeau $codeContent = $actualTag->getPluginData()[PluginUtility::PAYLOAD]; 2715f891b7eSNickeau 2725f891b7eSNickeau if (empty($actualCodeType)) { 2735f891b7eSNickeau LogUtility::msg("The type of the code should not be null for the code content " . $codeContent, LogUtility::LVL_MSG_WARNING, self::TAG); 274531e725cSNickeau continue 2; 2755f891b7eSNickeau } 2765f891b7eSNickeau 2775f891b7eSNickeau // Append it 2785f891b7eSNickeau $codes[$actualCodeType] = $codes[$actualCodeType] . $codeContent; 2795f891b7eSNickeau 2805f891b7eSNickeau // Check if a javascript console function is used, only if the flag is not set to true 2815f891b7eSNickeau if (!$useConsole == true) { 2825f891b7eSNickeau if (in_array($actualCodeType, array('babel', 'javascript', 'html', 'xml'))) { 2835f891b7eSNickeau // if the code contains 'console.' 2845f891b7eSNickeau $result = preg_match('/' . 'console\.' . '/is', $codeContent); 2855f891b7eSNickeau if ($result) { 2865f891b7eSNickeau $useConsole = true; 2875f891b7eSNickeau } 2885f891b7eSNickeau } 2895f891b7eSNickeau } 2905f891b7eSNickeau // Reset 2915f891b7eSNickeau $actualCodeType = ""; 292531e725cSNickeau break; 293531e725cSNickeau 2945f891b7eSNickeau } 2955f891b7eSNickeau } 296531e725cSNickeau 2975f891b7eSNickeau } 298531e725cSNickeau 2995f891b7eSNickeau return array( 3005f891b7eSNickeau PluginUtility::STATE => $state, 3015f891b7eSNickeau self::CODES_ATTRIBUTE => $codes, 302531e725cSNickeau self::USE_CONSOLE_ATTRIBUTE => $useConsole, 303531e725cSNickeau PluginUtility::ATTRIBUTES => $openingTag->getAttributes() 3045f891b7eSNickeau ); 3055f891b7eSNickeau 3065f891b7eSNickeau } 3075f891b7eSNickeau return false; 3085f891b7eSNickeau 3095f891b7eSNickeau } 3105f891b7eSNickeau 3115f891b7eSNickeau /** 3125f891b7eSNickeau * Render the output 3135f891b7eSNickeau * @param string $mode 3145f891b7eSNickeau * @param Doku_Renderer $renderer 3155f891b7eSNickeau * @param array $data - what the function handle() return'ed 3165f891b7eSNickeau * @return bool - rendered correctly (not used) 3175f891b7eSNickeau * 3185f891b7eSNickeau * The rendering process 3195f891b7eSNickeau * @see DokuWiki_Syntax_Plugin::render() 3205f891b7eSNickeau * 3215f891b7eSNickeau */ 3225f891b7eSNickeau public function render($mode, Doku_Renderer $renderer, $data) 3235f891b7eSNickeau { 3245f891b7eSNickeau // The $data variable comes from the handle() function 3255f891b7eSNickeau // 3265f891b7eSNickeau // $mode = 'xhtml' means that we output html 3275f891b7eSNickeau // There is other mode such as metadata where you can output data for the headers (Not 100% sure) 3285f891b7eSNickeau if ($mode == 'xhtml') { 3295f891b7eSNickeau 3305f891b7eSNickeau 3315f891b7eSNickeau /** @var Doku_Renderer_xhtml $renderer */ 3325f891b7eSNickeau 3335f891b7eSNickeau $state = $data[PluginUtility::STATE]; 3345f891b7eSNickeau switch ($state) { 3355f891b7eSNickeau 3365f891b7eSNickeau 3375f891b7eSNickeau case DOKU_LEXER_UNMATCHED : 3385f891b7eSNickeau 33932b85071SNickeau $renderer->doc .= PluginUtility::renderUnmatched($data); 3405f891b7eSNickeau break; 3415f891b7eSNickeau 3425f891b7eSNickeau case DOKU_LEXER_EXIT : 3435f891b7eSNickeau $codes = $data[self::CODES_ATTRIBUTE]; 344531e725cSNickeau $callStackArray = $data[PluginUtility::ATTRIBUTES]; 345531e725cSNickeau $iFrameAttributes = TagAttributes::createFromCallStackArray($callStackArray, self::TAG); 346531e725cSNickeau 3475f891b7eSNickeau // Create the real output of webcode 3485f891b7eSNickeau if (sizeof($codes) == 0) { 3495f891b7eSNickeau return false; 3505f891b7eSNickeau } 3515f891b7eSNickeau 352531e725cSNickeau // Credits bar 353531e725cSNickeau $bar = '<div class="webcode-bar">'; 354531e725cSNickeau 3555f891b7eSNickeau 3565f891b7eSNickeau // Dokuwiki Code ? 357531e725cSNickeau if (array_key_exists(self::MARKI_LANG, $codes)) { 3585f891b7eSNickeau 359531e725cSNickeau $markiCode = $codes[self::MARKI_LANG]; 360531e725cSNickeau /** 361531e725cSNickeau * By default, markup code 362531e725cSNickeau * is rendered inside the page 363531e725cSNickeau * We got less problem such as iframe overflow 364531e725cSNickeau * due to lazy loading, such as relative link, ... 365531e725cSNickeau * 366531e725cSNickeau */ 367531e725cSNickeau if (!$iFrameAttributes->hasComponentAttribute("iframe")) { 368531e725cSNickeau $renderer->doc .= PluginUtility::render($markiCode); 369531e725cSNickeau return true; 370531e725cSNickeau } 371531e725cSNickeau 372531e725cSNickeau $queryParams = array( 373531e725cSNickeau 'call' => action_plugin_combo_webcode::CALL_ID, 374531e725cSNickeau action_plugin_combo_webcode::MARKI_PARAM => $markiCode 375531e725cSNickeau ); 376531e725cSNickeau $queryString = http_build_query($queryParams); 377531e725cSNickeau $url = Site::getAjaxUrl() . "?$queryString"; 378531e725cSNickeau $iFrameAttributes->addHtmlAttributeValue("src", $url); 3795f891b7eSNickeau 3805f891b7eSNickeau } else { 3815f891b7eSNickeau 3825f891b7eSNickeau 3835f891b7eSNickeau // Js, Html, Css 384531e725cSNickeau $iframeSrcValue = '<html><head>'; 385*e55e4e71SNickeau $iframeSrcValue .= '<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>'; 386531e725cSNickeau $iframeSrcValue .= '<title>Made by WebCode</title>'; 387*e55e4e71SNickeau $iframeSrcValue .= '<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css"/>'; 3885f891b7eSNickeau 3895f891b7eSNickeau 3905f891b7eSNickeau // External Resources such as css stylesheet or js 391531e725cSNickeau $externalResources = []; 392531e725cSNickeau if ($iFrameAttributes->hasComponentAttribute(self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY)) { 393531e725cSNickeau $resources = $iFrameAttributes->getValueAndRemove(self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY); 394531e725cSNickeau $externalResources = explode(",", $resources); 3955f891b7eSNickeau } 3965f891b7eSNickeau 3975f891b7eSNickeau // Babel Preprocessor, if babel is used, add it to the external resources 3985f891b7eSNickeau if (array_key_exists('babel', $codes)) { 3995f891b7eSNickeau $babelMin = "https://unpkg.com/babel-standalone@6/babel.min.js"; 4005f891b7eSNickeau // a load of babel invoke it (be sure to not have it twice 4015f891b7eSNickeau if (!(array_key_exists($babelMin, $externalResources))) { 4025f891b7eSNickeau $externalResources[] = $babelMin; 4035f891b7eSNickeau } 4045f891b7eSNickeau } 4055f891b7eSNickeau 4065f891b7eSNickeau // Add the external resources 4075f891b7eSNickeau foreach ($externalResources as $externalResource) { 4085f891b7eSNickeau $pathInfo = pathinfo($externalResource); 4095f891b7eSNickeau $fileExtension = $pathInfo['extension']; 4105f891b7eSNickeau switch ($fileExtension) { 4115f891b7eSNickeau case 'css': 412*e55e4e71SNickeau $iframeSrcValue .= '<link rel="stylesheet" type="text/css" href="' . $externalResource . '"/>'; 4135f891b7eSNickeau break; 4145f891b7eSNickeau case 'js': 415531e725cSNickeau $iframeSrcValue .= '<script type="text/javascript" src="' . $externalResource . '"></script>'; 4165f891b7eSNickeau break; 4175f891b7eSNickeau } 4185f891b7eSNickeau } 4195f891b7eSNickeau 4205f891b7eSNickeau 4215f891b7eSNickeau // WebConsole style sheet 422531e725cSNickeau $iframeSrcValue .= '<link rel="stylesheet" type="text/css" href="' . PluginUtility::getResourceBaseUrl() . '/webcode/webcode-iframe.css?ver=' . self::WEB_CSS_VERSION . '"/>'; 4235f891b7eSNickeau 42421913ab3SNickeau // A little margin to make it neater 42521913ab3SNickeau // that can be overwritten via cascade 426531e725cSNickeau $iframeSrcValue .= '<style>body { margin:10px } /* default margin */</style>'; 42721913ab3SNickeau 42821913ab3SNickeau // The css 4295f891b7eSNickeau if (array_key_exists('css', $codes)) { 430531e725cSNickeau $iframeSrcValue .= '<!-- The CSS code -->'; 431531e725cSNickeau $iframeSrcValue .= '<style>' . $codes['css'] . '</style>'; 4325f891b7eSNickeau }; 433531e725cSNickeau $iframeSrcValue .= '</head><body>'; 4345f891b7eSNickeau if (array_key_exists('html', $codes)) { 435531e725cSNickeau $iframeSrcValue .= '<!-- The HTML code -->'; 436531e725cSNickeau $iframeSrcValue .= $codes['html']; 4375f891b7eSNickeau } 4385f891b7eSNickeau // The javascript console area is based at the end of the HTML document 4395f891b7eSNickeau $useConsole = $data[self::USE_CONSOLE_ATTRIBUTE]; 4405f891b7eSNickeau if ($useConsole) { 441531e725cSNickeau $iframeSrcValue .= '<!-- WebCode Console -->'; 442*e55e4e71SNickeau $iframeSrcValue .= '<div><p class="webConsoleTitle">Console Output:</p>'; 443*e55e4e71SNickeau $iframeSrcValue .= '<div id="webCodeConsole"></div>'; 444*e55e4e71SNickeau $iframeSrcValue .= '<script type="text/javascript" src="' . PluginUtility::getResourceBaseUrl() . '/webcode/webcode-console.js?ver=' . self::WEB_CONSOLE_JS_VERSION . '"></script>'; 445531e725cSNickeau $iframeSrcValue .= '</div>'; 4465f891b7eSNickeau } 4475f891b7eSNickeau // The javascript comes at the end because it may want to be applied on previous HTML element 4485f891b7eSNickeau // as the page load in the IO order, javascript must be placed at the end 4495f891b7eSNickeau if (array_key_exists('javascript', $codes)) { 450531e725cSNickeau $iframeSrcValue .= '<!-- The Javascript code -->'; 451531e725cSNickeau $iframeSrcValue .= '<script type="text/javascript">' . $codes['javascript'] . '</script>'; 4525f891b7eSNickeau } 4535f891b7eSNickeau if (array_key_exists('babel', $codes)) { 454531e725cSNickeau $iframeSrcValue .= '<!-- The Babel code -->'; 455531e725cSNickeau $iframeSrcValue .= '<script type="text/babel">' . $codes['babel'] . '</script>'; 4565f891b7eSNickeau } 457531e725cSNickeau $iframeSrcValue .= '</body></html>'; 458*e55e4e71SNickeau $iFrameAttributes->addHtmlAttributeValue("srcdoc", $iframeSrcValue); 4595f891b7eSNickeau 460531e725cSNickeau // Code bar with button 461531e725cSNickeau $bar .= '<div class="webcode-bar-item">' . PluginUtility::getUrl(self::TAG, "Rendered by WebCode", false) . '</div>'; 462531e725cSNickeau $bar .= '<div class="webcode-bar-item">' . $this->addJsFiddleButton($codes, $externalResources, $useConsole, $iFrameAttributes->getValue("name")) . '</div>'; 4635f891b7eSNickeau 464531e725cSNickeau 4655f891b7eSNickeau } 4665f891b7eSNickeau 467531e725cSNickeau /** 468531e725cSNickeau * If there is no height 469531e725cSNickeau */ 470531e725cSNickeau if (!$iFrameAttributes->hasComponentAttribute(TagAttributes::HEIGHT_KEY)) { 4715f891b7eSNickeau 472531e725cSNickeau /** 473531e725cSNickeau * Adjust the height attribute 474531e725cSNickeau * of the iframe element 475531e725cSNickeau * Any styling attribute would take over 476531e725cSNickeau */ 477531e725cSNickeau PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar(self::TAG); 478531e725cSNickeau /** 479531e725cSNickeau * CSS Height auto works when an image is loaded asynchronously but not 480531e725cSNickeau * when there is only text in the iframe 481531e725cSNickeau */ 482531e725cSNickeau //$iFrameAttributes->addStyleDeclaration("height", "auto"); 483531e725cSNickeau /** 484531e725cSNickeau * Due to margin at the bottom with css height=auto, 485531e725cSNickeau * we may see a scroll bar 486531e725cSNickeau * This block of code is to avoid scrolling, 487531e725cSNickeau * then scrolling = no if not set 488531e725cSNickeau */ 489531e725cSNickeau if (!$iFrameAttributes->hasComponentAttribute("scrolling")) { 490531e725cSNickeau $iFrameAttributes->addHtmlAttributeValue("scrolling", "no"); 4915f891b7eSNickeau } 4925f891b7eSNickeau 4935f891b7eSNickeau } 4945f891b7eSNickeau 495531e725cSNickeau 496531e725cSNickeau PluginUtility::getSnippetManager()->attachCssSnippetForBar(self::TAG); 497531e725cSNickeau 498*e55e4e71SNickeau /** 499*e55e4e71SNickeau * The iframe does not have any width 500*e55e4e71SNickeau * By default, we set it to 100% and it can be 501*e55e4e71SNickeau * constraint with the `width` attributes that will 502*e55e4e71SNickeau * set a a max-width 503*e55e4e71SNickeau */ 504*e55e4e71SNickeau $iFrameAttributes->addStyleDeclaration("width","100%"); 505*e55e4e71SNickeau 506531e725cSNickeau $iFrameHtml = $iFrameAttributes->toHtmlEnterTag("iframe") . '</iframe>'; 507531e725cSNickeau $bar .= '</div>'; // close the bar 508531e725cSNickeau $renderer->doc .= "<div class=\"webcode-wrapper\">" . $iFrameHtml . $bar . '</div>'; 509531e725cSNickeau 510531e725cSNickeau 5115f891b7eSNickeau break; 5125f891b7eSNickeau } 5135f891b7eSNickeau 5145f891b7eSNickeau return true; 5155f891b7eSNickeau } 5165f891b7eSNickeau return false; 5175f891b7eSNickeau } 5185f891b7eSNickeau 5195f891b7eSNickeau /** 5205f891b7eSNickeau * @param array $codes the array containing the codes 521531e725cSNickeau * @param array $externalResources the attributes of a call (for now the externalResources) 522531e725cSNickeau * @param bool $useConsole 523531e725cSNickeau * @param string $snippetTitle 5245f891b7eSNickeau * @return string the HTML form code 5255f891b7eSNickeau * 5265f891b7eSNickeau * Specification, see http://doc.jsfiddle.net/api/post.html 5275f891b7eSNickeau */ 528531e725cSNickeau public function addJsFiddleButton($codes, $externalResources, $useConsole = false, $snippetTitle = null) 5295f891b7eSNickeau { 5305f891b7eSNickeau 5315f891b7eSNickeau $postURL = "https://jsfiddle.net/api/post/library/pure/"; //No Framework 5325f891b7eSNickeau 5335f891b7eSNickeau 534531e725cSNickeau if ($useConsole) { 5355f891b7eSNickeau // If their is a console.log function, add the Firebug Lite support of JsFiddle 5365f891b7eSNickeau // Seems to work only with the Edge version of jQuery 5375f891b7eSNickeau // $postURL .= "edge/dependencies/Lite/"; 5385f891b7eSNickeau // The firebug logging is not working anymore because of 404 5395f891b7eSNickeau // Adding them here 5405f891b7eSNickeau $externalResources[] = 'The firebug resources for the console.log features'; 5415f891b7eSNickeau $externalResources[] = PluginUtility::getResourceBaseUrl() . '/firebug/firebug-lite.css'; 5425f891b7eSNickeau $externalResources[] = PluginUtility::getResourceBaseUrl() . '/firebug/firebug-lite-1.2.js'; 5435f891b7eSNickeau } 5445f891b7eSNickeau 5455f891b7eSNickeau // The below code is to prevent this JsFiddle bug: https://github.com/jsfiddle/jsfiddle-issues/issues/726 5465f891b7eSNickeau // The order of the resources is not guaranteed 5475f891b7eSNickeau // We pass then the resources only if their is one resources 5485f891b7eSNickeau // Otherwise we pass them as a script element in the HTML. 5495f891b7eSNickeau if (count($externalResources) <= 1) { 5505f891b7eSNickeau $externalResourcesInput = '<input type="hidden" name="resources" value="' . implode(",", $externalResources) . '">'; 5515f891b7eSNickeau } else { 5525f891b7eSNickeau $codes['html'] .= "\n\n\n\n\n<!-- The resources -->\n"; 5535f891b7eSNickeau $codes['html'] .= "<!-- They have been added here because their order is not guarantee through the API. -->\n"; 5545f891b7eSNickeau $codes['html'] .= "<!-- See: https://github.com/jsfiddle/jsfiddle-issues/issues/726 -->\n"; 5555f891b7eSNickeau foreach ($externalResources as $externalResource) { 5565f891b7eSNickeau if ($externalResource != "") { 5575f891b7eSNickeau $extension = pathinfo($externalResource)['extension']; 5585f891b7eSNickeau switch ($extension) { 5595f891b7eSNickeau case "css": 5605f891b7eSNickeau $codes['html'] .= "<link href=\"" . $externalResource . "\" rel=\"stylesheet\">\n"; 5615f891b7eSNickeau break; 5625f891b7eSNickeau case "js": 5635f891b7eSNickeau $codes['html'] .= "<script src=\"" . $externalResource . "\"></script>\n"; 5645f891b7eSNickeau break; 5655f891b7eSNickeau default: 5665f891b7eSNickeau $codes['html'] .= "<!-- " . $externalResource . " -->\n"; 5675f891b7eSNickeau } 5685f891b7eSNickeau } 5695f891b7eSNickeau } 5705f891b7eSNickeau } 5715f891b7eSNickeau 5725f891b7eSNickeau $jsCode = $codes['javascript']; 5735f891b7eSNickeau $jsPanel = 0; // language for the js specific panel (0 = JavaScript) 5745f891b7eSNickeau if (array_key_exists('babel', $codes)) { 5755f891b7eSNickeau $jsCode = $codes['babel']; 5765f891b7eSNickeau $jsPanel = 3; // 3 = Babel 5775f891b7eSNickeau } 5785f891b7eSNickeau 5795f891b7eSNickeau // Title and description 5805f891b7eSNickeau global $ID; 5815f891b7eSNickeau $pageTitle = tpl_pagetitle($ID, true); 582531e725cSNickeau if (!$snippetTitle) { 5835f891b7eSNickeau 584531e725cSNickeau $snippetTitle = "Code from " . $pageTitle; 5855f891b7eSNickeau } 5865f891b7eSNickeau $description = "Code from the page '" . $pageTitle . "' \n" . wl($ID, $absolute = true); 5875f891b7eSNickeau return '<form method="post" action="' . $postURL . '" target="_blank">' . 588531e725cSNickeau '<input type="hidden" name="title" value="' . htmlentities($snippetTitle) . '">' . 5895f891b7eSNickeau '<input type="hidden" name="description" value="' . htmlentities($description) . '">' . 5905f891b7eSNickeau '<input type="hidden" name="css" value="' . htmlentities($codes['css']) . '">' . 5915f891b7eSNickeau '<input type="hidden" name="html" value="' . htmlentities("<!-- The HTML -->" . $codes['html']) . '">' . 5925f891b7eSNickeau '<input type="hidden" name="js" value="' . htmlentities($jsCode) . '">' . 5935f891b7eSNickeau '<input type="hidden" name="panel_js" value="' . htmlentities($jsPanel) . '">' . 5945f891b7eSNickeau '<input type="hidden" name="wrap" value="b">' . //javascript no wrap in body 5955f891b7eSNickeau $externalResourcesInput . 5965f891b7eSNickeau '<button>Try the code</button>' . 5975f891b7eSNickeau '</form>'; 5985f891b7eSNickeau 5995f891b7eSNickeau } 6005f891b7eSNickeau 6015f891b7eSNickeau /** 6025f891b7eSNickeau * @param $codes the array containing the codes 6035f891b7eSNickeau * @param $attributes the attributes of a call (for now the externalResources) 6045f891b7eSNickeau * @return string the HTML form code 6055f891b7eSNickeau */ 6065f891b7eSNickeau public function addCodePenButton($codes, $attributes) 6075f891b7eSNickeau { 6085f891b7eSNickeau // TODO 6095f891b7eSNickeau // http://blog.codepen.io/documentation/api/prefill/ 6105f891b7eSNickeau } 6115f891b7eSNickeau 6125f891b7eSNickeau 6135f891b7eSNickeau} 614