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 195f891b7eSNickeauuse ComboStrap\LogUtility; 205f891b7eSNickeauuse ComboStrap\PluginUtility; 215f891b7eSNickeauuse ComboStrap\Tag; 22*21913ab3SNickeauuse ComboStrap\TagAttributes; 235f891b7eSNickeau 245f891b7eSNickeauif (!defined('DOKU_INC')) die(); 255f891b7eSNickeau 265f891b7eSNickeau/** 275f891b7eSNickeau * Webcode 285f891b7eSNickeau */ 295f891b7eSNickeauclass syntax_plugin_combo_webcode extends DokuWiki_Syntax_Plugin 305f891b7eSNickeau{ 315f891b7eSNickeau 325f891b7eSNickeau const EXTERNAL_RESOURCES_ATTRIBUTE_DISPLAY = 'externalResources'; // In the action bar 335f891b7eSNickeau const EXTERNAL_RESOURCES_ATTRIBUTE_KEY = 'externalresources'; // In the code 345f891b7eSNickeau 355f891b7eSNickeau // Simple cache bursting implementation for the webCodeConsole.(js|css) file 365f891b7eSNickeau // They must be incremented manually when they changed 375f891b7eSNickeau const WEB_CSS_VERSION = 1.1; 385f891b7eSNickeau const WEB_CONSOLE_JS_VERSION = 2.1; 395f891b7eSNickeau 405f891b7eSNickeau const TAG = 'webcode'; 415f891b7eSNickeau 425f891b7eSNickeau /** 435f891b7eSNickeau * The tag that have codes 445f891b7eSNickeau */ 455f891b7eSNickeau const CODE_TAGS = array("code", "plugin_combo_code"); 465f891b7eSNickeau 475f891b7eSNickeau /** 485f891b7eSNickeau * The attribute names in the array 495f891b7eSNickeau */ 505f891b7eSNickeau const CODES_ATTRIBUTE = "codes"; 515f891b7eSNickeau const USE_CONSOLE_ATTRIBUTE = "useConsole"; 52*21913ab3SNickeau const RENDERINGMODE_ATTRIBUTE = 'renderingmode'; 53*21913ab3SNickeau const RENDERING_ONLY_RESULT = "onlyresult"; 545f891b7eSNickeau 555f891b7eSNickeau /** 565f891b7eSNickeau * @var array that holds the iframe attributes 575f891b7eSNickeau */ 585f891b7eSNickeau private $attributes = array(); 595f891b7eSNickeau 605f891b7eSNickeau 615f891b7eSNickeau /** 625f891b7eSNickeau * Syntax Type. 635f891b7eSNickeau * 645f891b7eSNickeau * Needs to return one of the mode types defined in $PARSER_MODES in parser.php 655f891b7eSNickeau * @see https://www.dokuwiki.org/devel:syntax_plugins#syntax_types 665f891b7eSNickeau * 675f891b7eSNickeau * container because it may contain header in case of how to 685f891b7eSNickeau */ 695f891b7eSNickeau public function getType() 705f891b7eSNickeau { 715f891b7eSNickeau return 'container'; 725f891b7eSNickeau } 735f891b7eSNickeau 745f891b7eSNickeau /** 755f891b7eSNickeau * @return array 765f891b7eSNickeau * Allow which kind of plugin inside 775f891b7eSNickeau * 785f891b7eSNickeau * array('container', 'baseonly','formatting', 'substition', 'protected', 'disabled', 'paragraphs') 795f891b7eSNickeau * 805f891b7eSNickeau */ 815f891b7eSNickeau public function getAllowedTypes() 825f891b7eSNickeau { 835f891b7eSNickeau return array('container', 'baseonly', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs'); 845f891b7eSNickeau } 855f891b7eSNickeau 865f891b7eSNickeau 875f891b7eSNickeau public function accepts($mode) 885f891b7eSNickeau { 89*21913ab3SNickeau 90*21913ab3SNickeau return syntax_plugin_combo_preformatted::disablePreformatted($mode); 91*21913ab3SNickeau 925f891b7eSNickeau } 935f891b7eSNickeau 945f891b7eSNickeau /** 955f891b7eSNickeau * @see Doku_Parser_Mode::getSort() 965f891b7eSNickeau * The mode (plugin) with the lowest sort number will win out 975f891b7eSNickeau * 985f891b7eSNickeau * See {@link Doku_Parser_Mode_code} 995f891b7eSNickeau */ 1005f891b7eSNickeau public function getSort() 1015f891b7eSNickeau { 1025f891b7eSNickeau return 99; 1035f891b7eSNickeau } 1045f891b7eSNickeau 1055f891b7eSNickeau /** 1065f891b7eSNickeau * Called before any calls to ConnectTo 1075f891b7eSNickeau * @return void 1085f891b7eSNickeau */ 1095f891b7eSNickeau function preConnect() 1105f891b7eSNickeau { 1115f891b7eSNickeau } 1125f891b7eSNickeau 1135f891b7eSNickeau /** 1145f891b7eSNickeau * Create a pattern that will called this plugin 1155f891b7eSNickeau * 1165f891b7eSNickeau * @param string $mode 1175f891b7eSNickeau * 1185f891b7eSNickeau * All dokuwiki mode can be seen in the parser.php file 1195f891b7eSNickeau * @see Doku_Parser_Mode::connectTo() 1205f891b7eSNickeau */ 1215f891b7eSNickeau public function connectTo($mode) 1225f891b7eSNickeau { 1235f891b7eSNickeau 1245f891b7eSNickeau $pattern = PluginUtility::getContainerTagPattern(self::TAG); 1255f891b7eSNickeau $this->Lexer->addEntryPattern($pattern, $mode, PluginUtility::getModeForComponent($this->getPluginComponent())); 1265f891b7eSNickeau 1275f891b7eSNickeau } 1285f891b7eSNickeau 1295f891b7eSNickeau 1305f891b7eSNickeau // This where the addPattern and addExitPattern are defined 1315f891b7eSNickeau public function postConnect() 1325f891b7eSNickeau { 1335f891b7eSNickeau $this->Lexer->addExitPattern('</' . self::TAG . '>', PluginUtility::getModeForComponent($this->getPluginComponent())); 1345f891b7eSNickeau } 1355f891b7eSNickeau 1365f891b7eSNickeau 1375f891b7eSNickeau /** 1385f891b7eSNickeau * Handle the match 1395f891b7eSNickeau * You get the match for each pattern in the $match variable 1405f891b7eSNickeau * $state says if it's an entry, exit or match pattern 1415f891b7eSNickeau * 1425f891b7eSNickeau * This is an instruction block and is cached apart from the rendering output 1435f891b7eSNickeau * There is two caches levels 1445f891b7eSNickeau * This cache may be suppressed with the url parameters ?purge=true 1455f891b7eSNickeau * 1465f891b7eSNickeau * The returned values are cached in an array that will be passed to the render method 1475f891b7eSNickeau * The handle function goal is to parse the matched syntax through the pattern function 1485f891b7eSNickeau * and to return the result for use in the renderer 1495f891b7eSNickeau * This result is always cached until the page is modified. 1505f891b7eSNickeau * @param string $match 1515f891b7eSNickeau * @param int $state 1525f891b7eSNickeau * @param int $pos 1535f891b7eSNickeau * @param Doku_Handler $handler 1545f891b7eSNickeau * @return array|bool 1555f891b7eSNickeau * @throws Exception 1565f891b7eSNickeau * @see DokuWiki_Syntax_Plugin::handle() 1575f891b7eSNickeau * 1585f891b7eSNickeau */ 1595f891b7eSNickeau public function handle($match, $state, $pos, Doku_Handler $handler) 1605f891b7eSNickeau { 1615f891b7eSNickeau switch ($state) { 1625f891b7eSNickeau 1635f891b7eSNickeau case DOKU_LEXER_ENTER : 1645f891b7eSNickeau 1655f891b7eSNickeau // We got the first webcode tag and its attributes 1665f891b7eSNickeau 1675f891b7eSNickeau $match = substr($match, 8, -1); //9 = strlen("<webcode") 1685f891b7eSNickeau 1695f891b7eSNickeau // Reset of the attributes 1705f891b7eSNickeau // With some framework the php object may be still persisted in memory 1715f891b7eSNickeau // And you may get some attributes from other page 1725f891b7eSNickeau $attributes = array(); 1735f891b7eSNickeau $attributes['frameborder'] = 1; 1745f891b7eSNickeau $attributes['width'] = '100%'; 1755f891b7eSNickeau 176*21913ab3SNickeau $renderingModeKey = self::RENDERINGMODE_ATTRIBUTE; 1775f891b7eSNickeau $attributes[$renderingModeKey] = 'story'; 1785f891b7eSNickeau 1795f891b7eSNickeau // config Parameters will get their value in lowercase 1805f891b7eSNickeau $configAttributes = [$renderingModeKey]; 1815f891b7eSNickeau 1825f891b7eSNickeau // /i not case sensitive 1835f891b7eSNickeau $attributePattern = "\s*(\w+)\s*=\s*\"?([^\"\s]+)\"?\\s*"; 1845f891b7eSNickeau $result = preg_match_all('/' . $attributePattern . '/i', $match, $matches); 1855f891b7eSNickeau 1865f891b7eSNickeau 1875f891b7eSNickeau if ($result != 0) { 1885f891b7eSNickeau foreach ($matches[1] as $key => $lang) { 1895f891b7eSNickeau $attributeKey = strtolower($lang); 1905f891b7eSNickeau $attributeValue = $matches[2][$key]; 1915f891b7eSNickeau if (in_array($attributeKey, $configAttributes)) { 1925f891b7eSNickeau $attributeValue = strtolower($attributeValue); 1935f891b7eSNickeau } 1945f891b7eSNickeau $attributes[$attributeKey] = $attributeValue; 1955f891b7eSNickeau } 1965f891b7eSNickeau } 1975f891b7eSNickeau 1985f891b7eSNickeau // We set the attributes on a class scope 1995f891b7eSNickeau // to be used in the DOKU_LEXER_UNMATCHED step 2005f891b7eSNickeau $this->attributes = $attributes; 2015f891b7eSNickeau 2025f891b7eSNickeau // Cache the values to be used by the render method 2035f891b7eSNickeau return array( 2045f891b7eSNickeau PluginUtility::STATE => $state, 2055f891b7eSNickeau PluginUtility::ATTRIBUTES => $attributes 2065f891b7eSNickeau ); 2075f891b7eSNickeau 2085f891b7eSNickeau 2095f891b7eSNickeau case DOKU_LEXER_UNMATCHED : 2105f891b7eSNickeau 21132b85071SNickeau return PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler); 21232b85071SNickeau 2135f891b7eSNickeau 2145f891b7eSNickeau case DOKU_LEXER_EXIT: 2155f891b7eSNickeau 2165f891b7eSNickeau /** 2175f891b7eSNickeau * Capture all codes 2185f891b7eSNickeau */ 2195f891b7eSNickeau $codes = array(); 2205f891b7eSNickeau /** 2215f891b7eSNickeau * Does the javascript contains a console statement 2225f891b7eSNickeau */ 2235f891b7eSNickeau $useConsole = false; 2245f891b7eSNickeau $exitTag = new Tag(self::TAG, array(), $state, $handler); 2255f891b7eSNickeau $openingTag = $exitTag->getOpeningTag(); 226*21913ab3SNickeau $renderingMode = strtolower($openingTag->getAttribute(self::RENDERINGMODE_ATTRIBUTE)); 2275f891b7eSNickeau if ($openingTag->hasDescendants()) { 2285f891b7eSNickeau $tags = $openingTag->getDescendants(); 2295f891b7eSNickeau /** 2305f891b7eSNickeau * Mime and code content are in two differents 2315f891b7eSNickeau * tag. To be able to set the content to the good type 2325f891b7eSNickeau * we keep a trace of it 2335f891b7eSNickeau */ 2345f891b7eSNickeau $actualCodeType = ""; 2355f891b7eSNickeau foreach ($tags as $tag) { 2365f891b7eSNickeau if (in_array($tag->getName(), self::CODE_TAGS)) { 2375f891b7eSNickeau 238*21913ab3SNickeau /** 239*21913ab3SNickeau * Only rendering mode 240*21913ab3SNickeau * on all node (unmatched also) 241*21913ab3SNickeau */ 242*21913ab3SNickeau if ($renderingMode == self::RENDERING_ONLY_RESULT) { 243*21913ab3SNickeau $tag->addAttribute(TagAttributes::DISPLAY, "none"); 244*21913ab3SNickeau } 245*21913ab3SNickeau 2465f891b7eSNickeau if ($tag->getState() == 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 2495f891b7eSNickeau $actualCodeType = strtolower(trim($tag->getType())); 2505f891b7eSNickeau 2515f891b7eSNickeau // Xml is html 2525f891b7eSNickeau if ($actualCodeType == 'xml') { 2535f891b7eSNickeau $actualCodeType = 'html'; 2545f891b7eSNickeau } 255*21913ab3SNickeau // The code for a language may be scattered in multiple block 2565f891b7eSNickeau if (!isset($codes[$actualCodeType])) { 2575f891b7eSNickeau $codes[$actualCodeType] = ""; 2585f891b7eSNickeau } 259*21913ab3SNickeau 2605f891b7eSNickeau continue; 2615f891b7eSNickeau } 2625f891b7eSNickeau 2635f891b7eSNickeau if ($tag->getState() == DOKU_LEXER_UNMATCHED) { 2645f891b7eSNickeau 2655f891b7eSNickeau $codeContent = $tag->getData()[PluginUtility::PAYLOAD]; 2665f891b7eSNickeau 2675f891b7eSNickeau if (empty($actualCodeType)) { 2685f891b7eSNickeau LogUtility::msg("The type of the code should not be null for the code content " . $codeContent, LogUtility::LVL_MSG_WARNING, self::TAG); 2695f891b7eSNickeau continue; 2705f891b7eSNickeau } 2715f891b7eSNickeau 2725f891b7eSNickeau // Append it 2735f891b7eSNickeau $codes[$actualCodeType] = $codes[$actualCodeType] . $codeContent; 2745f891b7eSNickeau 2755f891b7eSNickeau // Check if a javascript console function is used, only if the flag is not set to true 2765f891b7eSNickeau if (!$useConsole == true) { 2775f891b7eSNickeau if (in_array($actualCodeType, array('babel', 'javascript', 'html', 'xml'))) { 2785f891b7eSNickeau // if the code contains 'console.' 2795f891b7eSNickeau $result = preg_match('/' . 'console\.' . '/is', $codeContent); 2805f891b7eSNickeau if ($result) { 2815f891b7eSNickeau $useConsole = true; 2825f891b7eSNickeau } 2835f891b7eSNickeau } 2845f891b7eSNickeau } 2855f891b7eSNickeau // Reset 2865f891b7eSNickeau $actualCodeType = ""; 2875f891b7eSNickeau } 2885f891b7eSNickeau } 2895f891b7eSNickeau } 2905f891b7eSNickeau } 291*21913ab3SNickeau// if(isset($codes["dw"])){ 292*21913ab3SNickeau// // http://php.net/manual/en/function.stream-context-create.php 293*21913ab3SNickeau// $dw = $codes["dw"]; 294*21913ab3SNickeau// 295*21913ab3SNickeau// $url = Site::getAjaxUrl(); 296*21913ab3SNickeau// $data = array( 297*21913ab3SNickeau// action_plugin_combo_webcode::DW_PARAM => $dw, 298*21913ab3SNickeau// action_plugin_combo_webcode::CALL_PARAM => action_plugin_combo_webcode::CALL_ID 299*21913ab3SNickeau// ); 300*21913ab3SNickeau// 301*21913ab3SNickeau// // use key 'http' even if you send the request to https://... 302*21913ab3SNickeau// $options = array( 303*21913ab3SNickeau// 'http' => array( 304*21913ab3SNickeau// 'method' => 'POST', 305*21913ab3SNickeau// 'content' => http_build_query($data) 306*21913ab3SNickeau// ) 307*21913ab3SNickeau// ); 308*21913ab3SNickeau// $context = stream_context_create($options); 309*21913ab3SNickeau// $result = file_get_contents($url, false, $context); 310*21913ab3SNickeau// if ($result === FALSE) { /* Handle error */ } 311*21913ab3SNickeau// 312*21913ab3SNickeau// } 3135f891b7eSNickeau return array( 3145f891b7eSNickeau PluginUtility::STATE => $state, 3155f891b7eSNickeau self::CODES_ATTRIBUTE => $codes, 3165f891b7eSNickeau self::USE_CONSOLE_ATTRIBUTE => $useConsole 3175f891b7eSNickeau ); 3185f891b7eSNickeau 3195f891b7eSNickeau } 3205f891b7eSNickeau return false; 3215f891b7eSNickeau 3225f891b7eSNickeau } 3235f891b7eSNickeau 3245f891b7eSNickeau /** 3255f891b7eSNickeau * Render the output 3265f891b7eSNickeau * @param string $mode 3275f891b7eSNickeau * @param Doku_Renderer $renderer 3285f891b7eSNickeau * @param array $data - what the function handle() return'ed 3295f891b7eSNickeau * @return bool - rendered correctly (not used) 3305f891b7eSNickeau * 3315f891b7eSNickeau * The rendering process 3325f891b7eSNickeau * @see DokuWiki_Syntax_Plugin::render() 3335f891b7eSNickeau * 3345f891b7eSNickeau */ 3355f891b7eSNickeau public function render($mode, Doku_Renderer $renderer, $data) 3365f891b7eSNickeau { 3375f891b7eSNickeau // The $data variable comes from the handle() function 3385f891b7eSNickeau // 3395f891b7eSNickeau // $mode = 'xhtml' means that we output html 3405f891b7eSNickeau // There is other mode such as metadata where you can output data for the headers (Not 100% sure) 3415f891b7eSNickeau if ($mode == 'xhtml') { 3425f891b7eSNickeau 3435f891b7eSNickeau 3445f891b7eSNickeau /** @var Doku_Renderer_xhtml $renderer */ 3455f891b7eSNickeau 3465f891b7eSNickeau $state = $data[PluginUtility::STATE]; 3475f891b7eSNickeau switch ($state) { 3485f891b7eSNickeau 3495f891b7eSNickeau case DOKU_LEXER_ENTER : 3505f891b7eSNickeau 3518aa9d0e6Sgerardnico PluginUtility::getSnippetManager()->upsertJavascriptForBar(self::TAG); 3525f891b7eSNickeau 3535f891b7eSNickeau // The extracted data are the attribute of the webcode tag 3545f891b7eSNickeau // We put in a class variable so that we can use in the last step (DOKU_LEXER_EXIT) 3555f891b7eSNickeau $this->attributes = $data[PluginUtility::ATTRIBUTES]; 3565f891b7eSNickeau 3575f891b7eSNickeau break; 3585f891b7eSNickeau 3595f891b7eSNickeau case DOKU_LEXER_UNMATCHED : 3605f891b7eSNickeau 36132b85071SNickeau $renderer->doc .= PluginUtility::renderUnmatched($data); 3625f891b7eSNickeau break; 3635f891b7eSNickeau 3645f891b7eSNickeau case DOKU_LEXER_EXIT : 3655f891b7eSNickeau $codes = $data[self::CODES_ATTRIBUTE]; 3665f891b7eSNickeau // Create the real output of webcode 3675f891b7eSNickeau if (sizeof($codes) == 0) { 3685f891b7eSNickeau return false; 3695f891b7eSNickeau } 3705f891b7eSNickeau 3718aa9d0e6Sgerardnico PluginUtility::getSnippetManager()->upsertCssSnippetForBar(self::TAG); 3725f891b7eSNickeau 3735f891b7eSNickeau // Dokuwiki Code ? 3745f891b7eSNickeau if (array_key_exists('dw', $codes)) { 3755f891b7eSNickeau 3765f891b7eSNickeau $renderer->doc .= PluginUtility::render($codes['dw']); 3775f891b7eSNickeau 3785f891b7eSNickeau } else { 3795f891b7eSNickeau 3805f891b7eSNickeau 3815f891b7eSNickeau // Js, Html, Css 3825f891b7eSNickeau $iframeHtml = '<html><head>'; 3835f891b7eSNickeau $iframeHtml .= '<meta http-equiv="content-type" content="text/html; charset=UTF-8">'; 3845f891b7eSNickeau $iframeHtml .= '<title>Made by Webcode</title>'; 3855f891b7eSNickeau $iframeHtml .= '<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css">'; 3865f891b7eSNickeau 3875f891b7eSNickeau 3885f891b7eSNickeau // External Resources such as css stylesheet or js 3895f891b7eSNickeau $externalResources = array(); 3905f891b7eSNickeau if (array_key_exists(self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY, $this->attributes)) { 3915f891b7eSNickeau $externalResources = explode(",", $this->attributes[self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY]); 3925f891b7eSNickeau } 3935f891b7eSNickeau 3945f891b7eSNickeau // Babel Preprocessor, if babel is used, add it to the external resources 3955f891b7eSNickeau if (array_key_exists('babel', $codes)) { 3965f891b7eSNickeau $babelMin = "https://unpkg.com/babel-standalone@6/babel.min.js"; 3975f891b7eSNickeau // a load of babel invoke it (be sure to not have it twice 3985f891b7eSNickeau if (!(array_key_exists($babelMin, $externalResources))) { 3995f891b7eSNickeau $externalResources[] = $babelMin; 4005f891b7eSNickeau } 4015f891b7eSNickeau } 4025f891b7eSNickeau 4035f891b7eSNickeau // Add the external resources 4045f891b7eSNickeau foreach ($externalResources as $externalResource) { 4055f891b7eSNickeau $pathInfo = pathinfo($externalResource); 4065f891b7eSNickeau $fileExtension = $pathInfo['extension']; 4075f891b7eSNickeau switch ($fileExtension) { 4085f891b7eSNickeau case 'css': 4095f891b7eSNickeau $iframeHtml .= '<link rel="stylesheet" type="text/css" href="' . $externalResource . '">'; 4105f891b7eSNickeau break; 4115f891b7eSNickeau case 'js': 4125f891b7eSNickeau $iframeHtml .= '<script type="text/javascript" src="' . $externalResource . '"></script>'; 4135f891b7eSNickeau break; 4145f891b7eSNickeau } 4155f891b7eSNickeau } 4165f891b7eSNickeau 4175f891b7eSNickeau 4185f891b7eSNickeau // WebConsole style sheet 4195f891b7eSNickeau $iframeHtml .= '<link rel="stylesheet" type="text/css" href="' . PluginUtility::getResourceBaseUrl() . '/webcode/webcode-iframe.css?ver=' . self::WEB_CSS_VERSION . '"/>'; 4205f891b7eSNickeau 421*21913ab3SNickeau // A little margin to make it neater 422*21913ab3SNickeau // that can be overwritten via cascade 423*21913ab3SNickeau $iframeHtml .= '<style>body { margin:10px } /* default margin */</style>'; 424*21913ab3SNickeau 425*21913ab3SNickeau // The css 4265f891b7eSNickeau if (array_key_exists('css', $codes)) { 4275f891b7eSNickeau $iframeHtml .= '<!-- The CSS code -->'; 4285f891b7eSNickeau $iframeHtml .= '<style>' . $codes['css'] . '</style>'; 4295f891b7eSNickeau }; 430*21913ab3SNickeau $iframeHtml .= '</head><body>'; 4315f891b7eSNickeau if (array_key_exists('html', $codes)) { 4325f891b7eSNickeau $iframeHtml .= '<!-- The HTML code -->'; 4335f891b7eSNickeau $iframeHtml .= $codes['html']; 4345f891b7eSNickeau } 4355f891b7eSNickeau // The javascript console area is based at the end of the HTML document 4365f891b7eSNickeau $useConsole = $data[self::USE_CONSOLE_ATTRIBUTE]; 4375f891b7eSNickeau if ($useConsole) { 4385f891b7eSNickeau $iframeHtml .= '<!-- WebCode Console -->'; 4395f891b7eSNickeau $iframeHtml .= '<div><p class=\'webConsoleTitle\'>Console Output:</p>'; 4405f891b7eSNickeau $iframeHtml .= '<div id=\'webCodeConsole\'></div>'; 4415f891b7eSNickeau $iframeHtml .= '<script type=\'text/javascript\' src=\'' . PluginUtility::getResourceBaseUrl() . '/webcode/webcode-console.js?ver=' . self::WEB_CONSOLE_JS_VERSION . '\'></script>'; 4425f891b7eSNickeau $iframeHtml .= '</div>'; 4435f891b7eSNickeau } 4445f891b7eSNickeau // The javascript comes at the end because it may want to be applied on previous HTML element 4455f891b7eSNickeau // as the page load in the IO order, javascript must be placed at the end 4465f891b7eSNickeau if (array_key_exists('javascript', $codes)) { 4475f891b7eSNickeau $iframeHtml .= '<!-- The Javascript code -->'; 4485f891b7eSNickeau $iframeHtml .= '<script type="text/javascript">' . $codes['javascript'] . '</script>'; 4495f891b7eSNickeau } 4505f891b7eSNickeau if (array_key_exists('babel', $codes)) { 4515f891b7eSNickeau $iframeHtml .= '<!-- The Babel code -->'; 4525f891b7eSNickeau $iframeHtml .= '<script type="text/babel">' . $codes['babel'] . '</script>'; 4535f891b7eSNickeau } 4545f891b7eSNickeau $iframeHtml .= '</body></html>'; 4555f891b7eSNickeau 4565f891b7eSNickeau // Here the magic from the plugin happens 4575f891b7eSNickeau // We add the Iframe and the JsFiddleButton 4585f891b7eSNickeau $iFrameHtml = '<iframe '; 4595f891b7eSNickeau 4605f891b7eSNickeau // We add the name HTML attribute 4615f891b7eSNickeau $name = "WebCode iFrame"; 4625f891b7eSNickeau if (array_key_exists('name', $this->attributes)) { 4635f891b7eSNickeau $name .= ' ' . $this->attributes['name']; 4645f891b7eSNickeau } 4655f891b7eSNickeau $iFrameHtml .= ' name="' . $name . '" '; 4665f891b7eSNickeau 4675f891b7eSNickeau // The class to be able to select them 4685f891b7eSNickeau $iFrameHtml .= ' class="webCode" '; 4695f891b7eSNickeau 4705f891b7eSNickeau // We add the others HTML attributes 4715f891b7eSNickeau $iFrameHtmlAttributes = array('width', 'height', 'frameborder', 'scrolling'); 4725f891b7eSNickeau foreach ($this->attributes as $attribute => $value) { 4735f891b7eSNickeau if (in_array($attribute, $iFrameHtmlAttributes)) { 4745f891b7eSNickeau $iFrameHtml .= ' ' . $attribute . '=' . $value; 4755f891b7eSNickeau } 4765f891b7eSNickeau } 4775f891b7eSNickeau $iFrameHtml .= ' srcdoc="' . htmlentities($iframeHtml) . '" ></iframe>';// 4785f891b7eSNickeau 4795f891b7eSNickeau // Credits bar 4805f891b7eSNickeau $bar = '<div class="webcode-bar">'; 4815f891b7eSNickeau $bar .= '<div class="webcode-bar-item">' . PluginUtility::getUrl(self::TAG, "Rendered by Webcode", false) . '</div>'; 4825f891b7eSNickeau $bar .= '<div class="webcode-bar-item">' . $this->addJsFiddleButton($codes, $this->attributes) . '</div>'; 4835f891b7eSNickeau $bar .= '</div>'; 4845f891b7eSNickeau $renderer->doc .= '<div class="webcode">' . $iFrameHtml . $bar . '</div>'; 4855f891b7eSNickeau } 4865f891b7eSNickeau 4875f891b7eSNickeau break; 4885f891b7eSNickeau } 4895f891b7eSNickeau 4905f891b7eSNickeau return true; 4915f891b7eSNickeau } 4925f891b7eSNickeau return false; 4935f891b7eSNickeau } 4945f891b7eSNickeau 4955f891b7eSNickeau /** 4965f891b7eSNickeau * @param array $codes the array containing the codes 4975f891b7eSNickeau * @param array $attributes the attributes of a call (for now the externalResources) 4985f891b7eSNickeau * @return string the HTML form code 4995f891b7eSNickeau * 5005f891b7eSNickeau * Specification, see http://doc.jsfiddle.net/api/post.html 5015f891b7eSNickeau */ 5025f891b7eSNickeau public function addJsFiddleButton($codes, $attributes) 5035f891b7eSNickeau { 5045f891b7eSNickeau 5055f891b7eSNickeau $postURL = "https://jsfiddle.net/api/post/library/pure/"; //No Framework 5065f891b7eSNickeau 5075f891b7eSNickeau $externalResources = array(); 5085f891b7eSNickeau if (array_key_exists(self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY, $attributes)) { 5095f891b7eSNickeau $externalResources = explode(",", $attributes[self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY]); 5105f891b7eSNickeau } 5115f891b7eSNickeau 5125f891b7eSNickeau 5135f891b7eSNickeau if ($this->useConsole) { 5145f891b7eSNickeau // If their is a console.log function, add the Firebug Lite support of JsFiddle 5155f891b7eSNickeau // Seems to work only with the Edge version of jQuery 5165f891b7eSNickeau // $postURL .= "edge/dependencies/Lite/"; 5175f891b7eSNickeau // The firebug logging is not working anymore because of 404 5185f891b7eSNickeau // Adding them here 5195f891b7eSNickeau $externalResources[] = 'The firebug resources for the console.log features'; 5205f891b7eSNickeau $externalResources[] = PluginUtility::getResourceBaseUrl() . '/firebug/firebug-lite.css'; 5215f891b7eSNickeau $externalResources[] = PluginUtility::getResourceBaseUrl() . '/firebug/firebug-lite-1.2.js'; 5225f891b7eSNickeau } 5235f891b7eSNickeau 5245f891b7eSNickeau // The below code is to prevent this JsFiddle bug: https://github.com/jsfiddle/jsfiddle-issues/issues/726 5255f891b7eSNickeau // The order of the resources is not guaranteed 5265f891b7eSNickeau // We pass then the resources only if their is one resources 5275f891b7eSNickeau // Otherwise we pass them as a script element in the HTML. 5285f891b7eSNickeau if (count($externalResources) <= 1) { 5295f891b7eSNickeau $externalResourcesInput = '<input type="hidden" name="resources" value="' . implode(",", $externalResources) . '">'; 5305f891b7eSNickeau } else { 5315f891b7eSNickeau $codes['html'] .= "\n\n\n\n\n<!-- The resources -->\n"; 5325f891b7eSNickeau $codes['html'] .= "<!-- They have been added here because their order is not guarantee through the API. -->\n"; 5335f891b7eSNickeau $codes['html'] .= "<!-- See: https://github.com/jsfiddle/jsfiddle-issues/issues/726 -->\n"; 5345f891b7eSNickeau foreach ($externalResources as $externalResource) { 5355f891b7eSNickeau if ($externalResource != "") { 5365f891b7eSNickeau $extension = pathinfo($externalResource)['extension']; 5375f891b7eSNickeau switch ($extension) { 5385f891b7eSNickeau case "css": 5395f891b7eSNickeau $codes['html'] .= "<link href=\"" . $externalResource . "\" rel=\"stylesheet\">\n"; 5405f891b7eSNickeau break; 5415f891b7eSNickeau case "js": 5425f891b7eSNickeau $codes['html'] .= "<script src=\"" . $externalResource . "\"></script>\n"; 5435f891b7eSNickeau break; 5445f891b7eSNickeau default: 5455f891b7eSNickeau $codes['html'] .= "<!-- " . $externalResource . " -->\n"; 5465f891b7eSNickeau } 5475f891b7eSNickeau } 5485f891b7eSNickeau } 5495f891b7eSNickeau } 5505f891b7eSNickeau 5515f891b7eSNickeau $jsCode = $codes['javascript']; 5525f891b7eSNickeau $jsPanel = 0; // language for the js specific panel (0 = JavaScript) 5535f891b7eSNickeau if (array_key_exists('babel', $codes)) { 5545f891b7eSNickeau $jsCode = $codes['babel']; 5555f891b7eSNickeau $jsPanel = 3; // 3 = Babel 5565f891b7eSNickeau } 5575f891b7eSNickeau 5585f891b7eSNickeau // Title and description 5595f891b7eSNickeau global $ID; 5605f891b7eSNickeau $title = $attributes['name']; 5615f891b7eSNickeau $pageTitle = tpl_pagetitle($ID, true); 5625f891b7eSNickeau if (!$title) { 5635f891b7eSNickeau 5645f891b7eSNickeau $title = "Code from " . $pageTitle; 5655f891b7eSNickeau } 5665f891b7eSNickeau $description = "Code from the page '" . $pageTitle . "' \n" . wl($ID, $absolute = true); 5675f891b7eSNickeau return '<form method="post" action="' . $postURL . '" target="_blank">' . 5685f891b7eSNickeau '<input type="hidden" name="title" value="' . htmlentities($title) . '">' . 5695f891b7eSNickeau '<input type="hidden" name="description" value="' . htmlentities($description) . '">' . 5705f891b7eSNickeau '<input type="hidden" name="css" value="' . htmlentities($codes['css']) . '">' . 5715f891b7eSNickeau '<input type="hidden" name="html" value="' . htmlentities("<!-- The HTML -->" . $codes['html']) . '">' . 5725f891b7eSNickeau '<input type="hidden" name="js" value="' . htmlentities($jsCode) . '">' . 5735f891b7eSNickeau '<input type="hidden" name="panel_js" value="' . htmlentities($jsPanel) . '">' . 5745f891b7eSNickeau '<input type="hidden" name="wrap" value="b">' . //javascript no wrap in body 5755f891b7eSNickeau $externalResourcesInput . 5765f891b7eSNickeau '<button>Try the code</button>' . 5775f891b7eSNickeau '</form>'; 5785f891b7eSNickeau 5795f891b7eSNickeau } 5805f891b7eSNickeau 5815f891b7eSNickeau /** 5825f891b7eSNickeau * @param $codes the array containing the codes 5835f891b7eSNickeau * @param $attributes the attributes of a call (for now the externalResources) 5845f891b7eSNickeau * @return string the HTML form code 5855f891b7eSNickeau */ 5865f891b7eSNickeau public function addCodePenButton($codes, $attributes) 5875f891b7eSNickeau { 5885f891b7eSNickeau // TODO 5895f891b7eSNickeau // http://blog.codepen.io/documentation/api/prefill/ 5905f891b7eSNickeau } 5915f891b7eSNickeau 5925f891b7eSNickeau 5935f891b7eSNickeau} 594