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 19*531e725cSNickeauuse ComboStrap\CallStack; 205f891b7eSNickeauuse ComboStrap\LogUtility; 215f891b7eSNickeauuse ComboStrap\PluginUtility; 22*531e725cSNickeauuse 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 */ 46*531e725cSNickeau const CODE_TAGS = 47*531e725cSNickeau array( 48*531e725cSNickeau syntax_plugin_combo_code::CODE_TAG, 49*531e725cSNickeau "plugin_combo_code", 50*531e725cSNickeau syntax_plugin_combo_codemarkdown::TAG 51*531e725cSNickeau ); 525f891b7eSNickeau 535f891b7eSNickeau /** 545f891b7eSNickeau * The attribute names in the array 555f891b7eSNickeau */ 565f891b7eSNickeau const CODES_ATTRIBUTE = "codes"; 575f891b7eSNickeau const USE_CONSOLE_ATTRIBUTE = "useConsole"; 58*531e725cSNickeau const RENDERING_MODE_ATTRIBUTE = 'renderingmode'; 5921913ab3SNickeau const RENDERING_ONLY_RESULT = "onlyresult"; 605f891b7eSNickeau 615f891b7eSNickeau /** 62*531e725cSNickeau * Marki code 635f891b7eSNickeau */ 64*531e725cSNickeau const MARKI_LANG = 'marki'; 65*531e725cSNickeau const DOKUWIKI_LANG = 'dw'; 66*531e725cSNickeau 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 81*531e725cSNickeau public function getPType() 82*531e725cSNickeau { 83*531e725cSNickeau return "stack"; 84*531e725cSNickeau } 85*531e725cSNickeau 86*531e725cSNickeau 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 178*531e725cSNickeau // Default 179*531e725cSNickeau $defaultAttributes = array(); 180*531e725cSNickeau $defaultAttributes['frameborder'] = 1; 181*531e725cSNickeau $defaultAttributes['width'] = '100%'; 182*531e725cSNickeau $defaultAttributes['name'] = "WebCode iFrame"; 183*531e725cSNickeau $defaultAttributes[self::RENDERING_MODE_ATTRIBUTE] = 'story'; 184*531e725cSNickeau // 'height' is set by the javascript if not set 185*531e725cSNickeau // 'width' and 'scrolling' gets their natural value 1865f891b7eSNickeau 187*531e725cSNickeau // Parse and create the call stack array 188*531e725cSNickeau $tagAttributes = TagAttributes::createFromTagMatch($match, $defaultAttributes); 189*531e725cSNickeau $callStackArray = $tagAttributes->toCallStackArray(); 1905f891b7eSNickeau 1915f891b7eSNickeau return array( 1925f891b7eSNickeau PluginUtility::STATE => $state, 193*531e725cSNickeau 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; 212*531e725cSNickeau 2135f891b7eSNickeau /** 214*531e725cSNickeau * Callstack 215*531e725cSNickeau */ 216*531e725cSNickeau $callStack = CallStack::createFromHandler($handler); 217*531e725cSNickeau $openingTag = $callStack->moveToPreviousCorrespondingOpeningCall(); 218*531e725cSNickeau $renderingMode = strtolower($openingTag->getAttribute(self::RENDERING_MODE_ATTRIBUTE)); 219*531e725cSNickeau 220*531e725cSNickeau /** 221*531e725cSNickeau * The mime (ie xml,html, ...) and code content are in two differents 222*531e725cSNickeau * call. To be able to set the content to the good type 2235f891b7eSNickeau * we keep a trace of it 2245f891b7eSNickeau */ 2255f891b7eSNickeau $actualCodeType = ""; 2265f891b7eSNickeau 22721913ab3SNickeau /** 228*531e725cSNickeau * Loop 229*531e725cSNickeau */ 230*531e725cSNickeau while ($actualTag = $callStack->next()) { 231*531e725cSNickeau 232*531e725cSNickeau 233*531e725cSNickeau $tagName = $actualTag->getTagName(); 234*531e725cSNickeau if (in_array($tagName, self::CODE_TAGS)) { 235*531e725cSNickeau 236*531e725cSNickeau /** 237*531e725cSNickeau * Only rendering mode, we don't display the node 238*531e725cSNickeau * on all node (enter, exit and unmatched) 23921913ab3SNickeau */ 24021913ab3SNickeau if ($renderingMode == self::RENDERING_ONLY_RESULT) { 241*531e725cSNickeau $actualTag->addAttribute(TagAttributes::DISPLAY, "none"); 24221913ab3SNickeau } 24321913ab3SNickeau 244*531e725cSNickeau switch ($actualTag->getState()) { 245*531e725cSNickeau 246*531e725cSNickeau 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 249*531e725cSNickeau $actualCodeType = strtolower(trim($actualTag->getType())); 2505f891b7eSNickeau 2515f891b7eSNickeau // Xml is html 2525f891b7eSNickeau if ($actualCodeType == 'xml') { 2535f891b7eSNickeau $actualCodeType = 'html'; 2545f891b7eSNickeau } 255*531e725cSNickeau 256*531e725cSNickeau // markdown, dokuwiki is marki 257*531e725cSNickeau if (in_array($actualCodeType, ['md', 'markdown', 'dw'])) { 258*531e725cSNickeau $actualCodeType = self::MARKI_LANG; 259*531e725cSNickeau } 260*531e725cSNickeau 26121913ab3SNickeau // The code for a language may be scattered in multiple block 2625f891b7eSNickeau if (!isset($codes[$actualCodeType])) { 2635f891b7eSNickeau $codes[$actualCodeType] = ""; 2645f891b7eSNickeau } 26521913ab3SNickeau 266*531e725cSNickeau continue 2; 2675f891b7eSNickeau 268*531e725cSNickeau case DOKU_LEXER_UNMATCHED: 2695f891b7eSNickeau 270*531e725cSNickeau $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); 274*531e725cSNickeau 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 = ""; 292*531e725cSNickeau break; 293*531e725cSNickeau 2945f891b7eSNickeau } 2955f891b7eSNickeau } 296*531e725cSNickeau 2975f891b7eSNickeau } 298*531e725cSNickeau 2995f891b7eSNickeau return array( 3005f891b7eSNickeau PluginUtility::STATE => $state, 3015f891b7eSNickeau self::CODES_ATTRIBUTE => $codes, 302*531e725cSNickeau self::USE_CONSOLE_ATTRIBUTE => $useConsole, 303*531e725cSNickeau 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]; 344*531e725cSNickeau $callStackArray = $data[PluginUtility::ATTRIBUTES]; 345*531e725cSNickeau $iFrameAttributes = TagAttributes::createFromCallStackArray($callStackArray, self::TAG); 346*531e725cSNickeau 3475f891b7eSNickeau // Create the real output of webcode 3485f891b7eSNickeau if (sizeof($codes) == 0) { 3495f891b7eSNickeau return false; 3505f891b7eSNickeau } 3515f891b7eSNickeau 352*531e725cSNickeau // Credits bar 353*531e725cSNickeau $bar = '<div class="webcode-bar">'; 354*531e725cSNickeau 3555f891b7eSNickeau 3565f891b7eSNickeau // Dokuwiki Code ? 357*531e725cSNickeau if (array_key_exists(self::MARKI_LANG, $codes)) { 3585f891b7eSNickeau 359*531e725cSNickeau $markiCode = $codes[self::MARKI_LANG]; 360*531e725cSNickeau /** 361*531e725cSNickeau * By default, markup code 362*531e725cSNickeau * is rendered inside the page 363*531e725cSNickeau * We got less problem such as iframe overflow 364*531e725cSNickeau * due to lazy loading, such as relative link, ... 365*531e725cSNickeau * 366*531e725cSNickeau */ 367*531e725cSNickeau if (!$iFrameAttributes->hasComponentAttribute("iframe")) { 368*531e725cSNickeau $renderer->doc .= PluginUtility::render($markiCode); 369*531e725cSNickeau return true; 370*531e725cSNickeau } 371*531e725cSNickeau 372*531e725cSNickeau $queryParams = array( 373*531e725cSNickeau 'call' => action_plugin_combo_webcode::CALL_ID, 374*531e725cSNickeau action_plugin_combo_webcode::MARKI_PARAM => $markiCode 375*531e725cSNickeau ); 376*531e725cSNickeau $queryString = http_build_query($queryParams); 377*531e725cSNickeau $url = Site::getAjaxUrl() . "?$queryString"; 378*531e725cSNickeau $iFrameAttributes->addHtmlAttributeValue("src", $url); 3795f891b7eSNickeau 3805f891b7eSNickeau } else { 3815f891b7eSNickeau 3825f891b7eSNickeau 3835f891b7eSNickeau // Js, Html, Css 384*531e725cSNickeau $iframeSrcValue = '<html><head>'; 385*531e725cSNickeau $iframeSrcValue .= '<meta http-equiv="content-type" content="text/html; charset=UTF-8">'; 386*531e725cSNickeau $iframeSrcValue .= '<title>Made by WebCode</title>'; 387*531e725cSNickeau $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 391*531e725cSNickeau $externalResources = []; 392*531e725cSNickeau if ($iFrameAttributes->hasComponentAttribute(self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY)) { 393*531e725cSNickeau $resources = $iFrameAttributes->getValueAndRemove(self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY); 394*531e725cSNickeau $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*531e725cSNickeau $iframeSrcValue .= '<link rel="stylesheet" type="text/css" href="' . $externalResource . '">'; 4135f891b7eSNickeau break; 4145f891b7eSNickeau case 'js': 415*531e725cSNickeau $iframeSrcValue .= '<script type="text/javascript" src="' . $externalResource . '"></script>'; 4165f891b7eSNickeau break; 4175f891b7eSNickeau } 4185f891b7eSNickeau } 4195f891b7eSNickeau 4205f891b7eSNickeau 4215f891b7eSNickeau // WebConsole style sheet 422*531e725cSNickeau $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 426*531e725cSNickeau $iframeSrcValue .= '<style>body { margin:10px } /* default margin */</style>'; 42721913ab3SNickeau 42821913ab3SNickeau // The css 4295f891b7eSNickeau if (array_key_exists('css', $codes)) { 430*531e725cSNickeau $iframeSrcValue .= '<!-- The CSS code -->'; 431*531e725cSNickeau $iframeSrcValue .= '<style>' . $codes['css'] . '</style>'; 4325f891b7eSNickeau }; 433*531e725cSNickeau $iframeSrcValue .= '</head><body>'; 4345f891b7eSNickeau if (array_key_exists('html', $codes)) { 435*531e725cSNickeau $iframeSrcValue .= '<!-- The HTML code -->'; 436*531e725cSNickeau $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) { 441*531e725cSNickeau $iframeSrcValue .= '<!-- WebCode Console -->'; 442*531e725cSNickeau $iframeSrcValue .= '<div><p class=\'webConsoleTitle\'>Console Output:</p>'; 443*531e725cSNickeau $iframeSrcValue .= '<div id=\'webCodeConsole\'></div>'; 444*531e725cSNickeau $iframeSrcValue .= '<script type=\'text/javascript\' src=\'' . PluginUtility::getResourceBaseUrl() . '/webcode/webcode-console.js?ver=' . self::WEB_CONSOLE_JS_VERSION . '\'></script>'; 445*531e725cSNickeau $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)) { 450*531e725cSNickeau $iframeSrcValue .= '<!-- The Javascript code -->'; 451*531e725cSNickeau $iframeSrcValue .= '<script type="text/javascript">' . $codes['javascript'] . '</script>'; 4525f891b7eSNickeau } 4535f891b7eSNickeau if (array_key_exists('babel', $codes)) { 454*531e725cSNickeau $iframeSrcValue .= '<!-- The Babel code -->'; 455*531e725cSNickeau $iframeSrcValue .= '<script type="text/babel">' . $codes['babel'] . '</script>'; 4565f891b7eSNickeau } 457*531e725cSNickeau $iframeSrcValue .= '</body></html>'; 458*531e725cSNickeau $iFrameAttributes->addHtmlAttributeValue("srcdoc", htmlentities($iframeSrcValue)); 4595f891b7eSNickeau 460*531e725cSNickeau // Code bar with button 461*531e725cSNickeau $bar .= '<div class="webcode-bar-item">' . PluginUtility::getUrl(self::TAG, "Rendered by WebCode", false) . '</div>'; 462*531e725cSNickeau $bar .= '<div class="webcode-bar-item">' . $this->addJsFiddleButton($codes, $externalResources, $useConsole, $iFrameAttributes->getValue("name")) . '</div>'; 4635f891b7eSNickeau 464*531e725cSNickeau 4655f891b7eSNickeau } 4665f891b7eSNickeau 467*531e725cSNickeau /** 468*531e725cSNickeau * If there is no height 469*531e725cSNickeau */ 470*531e725cSNickeau if (!$iFrameAttributes->hasComponentAttribute(TagAttributes::HEIGHT_KEY)) { 4715f891b7eSNickeau 472*531e725cSNickeau /** 473*531e725cSNickeau * Adjust the height attribute 474*531e725cSNickeau * of the iframe element 475*531e725cSNickeau * Any styling attribute would take over 476*531e725cSNickeau */ 477*531e725cSNickeau PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar(self::TAG); 478*531e725cSNickeau /** 479*531e725cSNickeau * CSS Height auto works when an image is loaded asynchronously but not 480*531e725cSNickeau * when there is only text in the iframe 481*531e725cSNickeau */ 482*531e725cSNickeau //$iFrameAttributes->addStyleDeclaration("height", "auto"); 483*531e725cSNickeau /** 484*531e725cSNickeau * Due to margin at the bottom with css height=auto, 485*531e725cSNickeau * we may see a scroll bar 486*531e725cSNickeau * This block of code is to avoid scrolling, 487*531e725cSNickeau * then scrolling = no if not set 488*531e725cSNickeau */ 489*531e725cSNickeau if (!$iFrameAttributes->hasComponentAttribute("scrolling")) { 490*531e725cSNickeau $iFrameAttributes->addHtmlAttributeValue("scrolling", "no"); 4915f891b7eSNickeau } 4925f891b7eSNickeau 4935f891b7eSNickeau } 4945f891b7eSNickeau 495*531e725cSNickeau 496*531e725cSNickeau PluginUtility::getSnippetManager()->attachCssSnippetForBar(self::TAG); 497*531e725cSNickeau 498*531e725cSNickeau $iFrameHtml = $iFrameAttributes->toHtmlEnterTag("iframe") . '</iframe>'; 499*531e725cSNickeau $bar .= '</div>'; // close the bar 500*531e725cSNickeau $renderer->doc .= "<div class=\"webcode-wrapper\">" . $iFrameHtml . $bar . '</div>'; 501*531e725cSNickeau 502*531e725cSNickeau 5035f891b7eSNickeau break; 5045f891b7eSNickeau } 5055f891b7eSNickeau 5065f891b7eSNickeau return true; 5075f891b7eSNickeau } 5085f891b7eSNickeau return false; 5095f891b7eSNickeau } 5105f891b7eSNickeau 5115f891b7eSNickeau /** 5125f891b7eSNickeau * @param array $codes the array containing the codes 513*531e725cSNickeau * @param array $externalResources the attributes of a call (for now the externalResources) 514*531e725cSNickeau * @param bool $useConsole 515*531e725cSNickeau * @param string $snippetTitle 5165f891b7eSNickeau * @return string the HTML form code 5175f891b7eSNickeau * 5185f891b7eSNickeau * Specification, see http://doc.jsfiddle.net/api/post.html 5195f891b7eSNickeau */ 520*531e725cSNickeau public function addJsFiddleButton($codes, $externalResources, $useConsole = false, $snippetTitle = null) 5215f891b7eSNickeau { 5225f891b7eSNickeau 5235f891b7eSNickeau $postURL = "https://jsfiddle.net/api/post/library/pure/"; //No Framework 5245f891b7eSNickeau 5255f891b7eSNickeau 526*531e725cSNickeau if ($useConsole) { 5275f891b7eSNickeau // If their is a console.log function, add the Firebug Lite support of JsFiddle 5285f891b7eSNickeau // Seems to work only with the Edge version of jQuery 5295f891b7eSNickeau // $postURL .= "edge/dependencies/Lite/"; 5305f891b7eSNickeau // The firebug logging is not working anymore because of 404 5315f891b7eSNickeau // Adding them here 5325f891b7eSNickeau $externalResources[] = 'The firebug resources for the console.log features'; 5335f891b7eSNickeau $externalResources[] = PluginUtility::getResourceBaseUrl() . '/firebug/firebug-lite.css'; 5345f891b7eSNickeau $externalResources[] = PluginUtility::getResourceBaseUrl() . '/firebug/firebug-lite-1.2.js'; 5355f891b7eSNickeau } 5365f891b7eSNickeau 5375f891b7eSNickeau // The below code is to prevent this JsFiddle bug: https://github.com/jsfiddle/jsfiddle-issues/issues/726 5385f891b7eSNickeau // The order of the resources is not guaranteed 5395f891b7eSNickeau // We pass then the resources only if their is one resources 5405f891b7eSNickeau // Otherwise we pass them as a script element in the HTML. 5415f891b7eSNickeau if (count($externalResources) <= 1) { 5425f891b7eSNickeau $externalResourcesInput = '<input type="hidden" name="resources" value="' . implode(",", $externalResources) . '">'; 5435f891b7eSNickeau } else { 5445f891b7eSNickeau $codes['html'] .= "\n\n\n\n\n<!-- The resources -->\n"; 5455f891b7eSNickeau $codes['html'] .= "<!-- They have been added here because their order is not guarantee through the API. -->\n"; 5465f891b7eSNickeau $codes['html'] .= "<!-- See: https://github.com/jsfiddle/jsfiddle-issues/issues/726 -->\n"; 5475f891b7eSNickeau foreach ($externalResources as $externalResource) { 5485f891b7eSNickeau if ($externalResource != "") { 5495f891b7eSNickeau $extension = pathinfo($externalResource)['extension']; 5505f891b7eSNickeau switch ($extension) { 5515f891b7eSNickeau case "css": 5525f891b7eSNickeau $codes['html'] .= "<link href=\"" . $externalResource . "\" rel=\"stylesheet\">\n"; 5535f891b7eSNickeau break; 5545f891b7eSNickeau case "js": 5555f891b7eSNickeau $codes['html'] .= "<script src=\"" . $externalResource . "\"></script>\n"; 5565f891b7eSNickeau break; 5575f891b7eSNickeau default: 5585f891b7eSNickeau $codes['html'] .= "<!-- " . $externalResource . " -->\n"; 5595f891b7eSNickeau } 5605f891b7eSNickeau } 5615f891b7eSNickeau } 5625f891b7eSNickeau } 5635f891b7eSNickeau 5645f891b7eSNickeau $jsCode = $codes['javascript']; 5655f891b7eSNickeau $jsPanel = 0; // language for the js specific panel (0 = JavaScript) 5665f891b7eSNickeau if (array_key_exists('babel', $codes)) { 5675f891b7eSNickeau $jsCode = $codes['babel']; 5685f891b7eSNickeau $jsPanel = 3; // 3 = Babel 5695f891b7eSNickeau } 5705f891b7eSNickeau 5715f891b7eSNickeau // Title and description 5725f891b7eSNickeau global $ID; 5735f891b7eSNickeau $pageTitle = tpl_pagetitle($ID, true); 574*531e725cSNickeau if (!$snippetTitle) { 5755f891b7eSNickeau 576*531e725cSNickeau $snippetTitle = "Code from " . $pageTitle; 5775f891b7eSNickeau } 5785f891b7eSNickeau $description = "Code from the page '" . $pageTitle . "' \n" . wl($ID, $absolute = true); 5795f891b7eSNickeau return '<form method="post" action="' . $postURL . '" target="_blank">' . 580*531e725cSNickeau '<input type="hidden" name="title" value="' . htmlentities($snippetTitle) . '">' . 5815f891b7eSNickeau '<input type="hidden" name="description" value="' . htmlentities($description) . '">' . 5825f891b7eSNickeau '<input type="hidden" name="css" value="' . htmlentities($codes['css']) . '">' . 5835f891b7eSNickeau '<input type="hidden" name="html" value="' . htmlentities("<!-- The HTML -->" . $codes['html']) . '">' . 5845f891b7eSNickeau '<input type="hidden" name="js" value="' . htmlentities($jsCode) . '">' . 5855f891b7eSNickeau '<input type="hidden" name="panel_js" value="' . htmlentities($jsPanel) . '">' . 5865f891b7eSNickeau '<input type="hidden" name="wrap" value="b">' . //javascript no wrap in body 5875f891b7eSNickeau $externalResourcesInput . 5885f891b7eSNickeau '<button>Try the code</button>' . 5895f891b7eSNickeau '</form>'; 5905f891b7eSNickeau 5915f891b7eSNickeau } 5925f891b7eSNickeau 5935f891b7eSNickeau /** 5945f891b7eSNickeau * @param $codes the array containing the codes 5955f891b7eSNickeau * @param $attributes the attributes of a call (for now the externalResources) 5965f891b7eSNickeau * @return string the HTML form code 5975f891b7eSNickeau */ 5985f891b7eSNickeau public function addCodePenButton($codes, $attributes) 5995f891b7eSNickeau { 6005f891b7eSNickeau // TODO 6015f891b7eSNickeau // http://blog.codepen.io/documentation/api/prefill/ 6025f891b7eSNickeau } 6035f891b7eSNickeau 6045f891b7eSNickeau 6055f891b7eSNickeau} 606