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 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 175f891b7eSNickeau * @author Nicolas GERARD 185f891b7eSNickeau */ 195f891b7eSNickeau 205f891b7eSNickeau// must be run within Dokuwiki 215f891b7eSNickeauuse ComboStrap\SnippetManager; 225f891b7eSNickeauuse ComboStrap\LogUtility; 235f891b7eSNickeauuse ComboStrap\PluginUtility; 245f891b7eSNickeauuse ComboStrap\Tag; 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 */ 475f891b7eSNickeau const CODE_TAGS = array("code", "plugin_combo_code"); 485f891b7eSNickeau 495f891b7eSNickeau /** 505f891b7eSNickeau * The attribute names in the array 515f891b7eSNickeau */ 525f891b7eSNickeau const CODES_ATTRIBUTE = "codes"; 535f891b7eSNickeau const USE_CONSOLE_ATTRIBUTE = "useConsole"; 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 { 895f891b7eSNickeau if (!$this->getConf(syntax_plugin_combo_preformatted::CONF_PREFORMATTED_ENABLE)) { 905f891b7eSNickeau return PluginUtility::disablePreformatted($mode); 915f891b7eSNickeau } else { 925f891b7eSNickeau return true; 935f891b7eSNickeau } 945f891b7eSNickeau } 955f891b7eSNickeau 965f891b7eSNickeau /** 975f891b7eSNickeau * @see Doku_Parser_Mode::getSort() 985f891b7eSNickeau * The mode (plugin) with the lowest sort number will win out 995f891b7eSNickeau * 1005f891b7eSNickeau * See {@link Doku_Parser_Mode_code} 1015f891b7eSNickeau */ 1025f891b7eSNickeau public function getSort() 1035f891b7eSNickeau { 1045f891b7eSNickeau return 99; 1055f891b7eSNickeau } 1065f891b7eSNickeau 1075f891b7eSNickeau /** 1085f891b7eSNickeau * Called before any calls to ConnectTo 1095f891b7eSNickeau * @return void 1105f891b7eSNickeau */ 1115f891b7eSNickeau function preConnect() 1125f891b7eSNickeau { 1135f891b7eSNickeau } 1145f891b7eSNickeau 1155f891b7eSNickeau /** 1165f891b7eSNickeau * Create a pattern that will called this plugin 1175f891b7eSNickeau * 1185f891b7eSNickeau * @param string $mode 1195f891b7eSNickeau * 1205f891b7eSNickeau * All dokuwiki mode can be seen in the parser.php file 1215f891b7eSNickeau * @see Doku_Parser_Mode::connectTo() 1225f891b7eSNickeau */ 1235f891b7eSNickeau public function connectTo($mode) 1245f891b7eSNickeau { 1255f891b7eSNickeau 1265f891b7eSNickeau $pattern = PluginUtility::getContainerTagPattern(self::TAG); 1275f891b7eSNickeau $this->Lexer->addEntryPattern($pattern, $mode, PluginUtility::getModeForComponent($this->getPluginComponent())); 1285f891b7eSNickeau 1295f891b7eSNickeau } 1305f891b7eSNickeau 1315f891b7eSNickeau 1325f891b7eSNickeau // This where the addPattern and addExitPattern are defined 1335f891b7eSNickeau public function postConnect() 1345f891b7eSNickeau { 1355f891b7eSNickeau $this->Lexer->addExitPattern('</' . self::TAG . '>', PluginUtility::getModeForComponent($this->getPluginComponent())); 1365f891b7eSNickeau } 1375f891b7eSNickeau 1385f891b7eSNickeau 1395f891b7eSNickeau /** 1405f891b7eSNickeau * Handle the match 1415f891b7eSNickeau * You get the match for each pattern in the $match variable 1425f891b7eSNickeau * $state says if it's an entry, exit or match pattern 1435f891b7eSNickeau * 1445f891b7eSNickeau * This is an instruction block and is cached apart from the rendering output 1455f891b7eSNickeau * There is two caches levels 1465f891b7eSNickeau * This cache may be suppressed with the url parameters ?purge=true 1475f891b7eSNickeau * 1485f891b7eSNickeau * The returned values are cached in an array that will be passed to the render method 1495f891b7eSNickeau * The handle function goal is to parse the matched syntax through the pattern function 1505f891b7eSNickeau * and to return the result for use in the renderer 1515f891b7eSNickeau * This result is always cached until the page is modified. 1525f891b7eSNickeau * @param string $match 1535f891b7eSNickeau * @param int $state 1545f891b7eSNickeau * @param int $pos 1555f891b7eSNickeau * @param Doku_Handler $handler 1565f891b7eSNickeau * @return array|bool 1575f891b7eSNickeau * @throws Exception 1585f891b7eSNickeau * @see DokuWiki_Syntax_Plugin::handle() 1595f891b7eSNickeau * 1605f891b7eSNickeau */ 1615f891b7eSNickeau public function handle($match, $state, $pos, Doku_Handler $handler) 1625f891b7eSNickeau { 1635f891b7eSNickeau switch ($state) { 1645f891b7eSNickeau 1655f891b7eSNickeau case DOKU_LEXER_ENTER : 1665f891b7eSNickeau 1675f891b7eSNickeau // We got the first webcode tag and its attributes 1685f891b7eSNickeau 1695f891b7eSNickeau $match = substr($match, 8, -1); //9 = strlen("<webcode") 1705f891b7eSNickeau 1715f891b7eSNickeau // Reset of the attributes 1725f891b7eSNickeau // With some framework the php object may be still persisted in memory 1735f891b7eSNickeau // And you may get some attributes from other page 1745f891b7eSNickeau $attributes = array(); 1755f891b7eSNickeau $attributes['frameborder'] = 1; 1765f891b7eSNickeau $attributes['width'] = '100%'; 1775f891b7eSNickeau 1785f891b7eSNickeau $renderingModeKey = 'renderingmode'; 1795f891b7eSNickeau $attributes[$renderingModeKey] = 'story'; 1805f891b7eSNickeau 1815f891b7eSNickeau // config Parameters will get their value in lowercase 1825f891b7eSNickeau $configAttributes = [$renderingModeKey]; 1835f891b7eSNickeau 1845f891b7eSNickeau // /i not case sensitive 1855f891b7eSNickeau $attributePattern = "\s*(\w+)\s*=\s*\"?([^\"\s]+)\"?\\s*"; 1865f891b7eSNickeau $result = preg_match_all('/' . $attributePattern . '/i', $match, $matches); 1875f891b7eSNickeau 1885f891b7eSNickeau 1895f891b7eSNickeau if ($result != 0) { 1905f891b7eSNickeau foreach ($matches[1] as $key => $lang) { 1915f891b7eSNickeau $attributeKey = strtolower($lang); 1925f891b7eSNickeau $attributeValue = $matches[2][$key]; 1935f891b7eSNickeau if (in_array($attributeKey, $configAttributes)) { 1945f891b7eSNickeau $attributeValue = strtolower($attributeValue); 1955f891b7eSNickeau } 1965f891b7eSNickeau $attributes[$attributeKey] = $attributeValue; 1975f891b7eSNickeau } 1985f891b7eSNickeau } 1995f891b7eSNickeau 2005f891b7eSNickeau // We set the attributes on a class scope 2015f891b7eSNickeau // to be used in the DOKU_LEXER_UNMATCHED step 2025f891b7eSNickeau $this->attributes = $attributes; 2035f891b7eSNickeau 2045f891b7eSNickeau // Cache the values to be used by the render method 2055f891b7eSNickeau return array( 2065f891b7eSNickeau PluginUtility::STATE => $state, 2075f891b7eSNickeau PluginUtility::ATTRIBUTES => $attributes 2085f891b7eSNickeau ); 2095f891b7eSNickeau 2105f891b7eSNickeau 2115f891b7eSNickeau case DOKU_LEXER_UNMATCHED : 2125f891b7eSNickeau 213*32b85071SNickeau return PluginUtility::handleAndReturnUnmatchedData(self::TAG,$match,$handler); 214*32b85071SNickeau 2155f891b7eSNickeau 2165f891b7eSNickeau case DOKU_LEXER_EXIT: 2175f891b7eSNickeau 2185f891b7eSNickeau /** 2195f891b7eSNickeau * Capture all codes 2205f891b7eSNickeau */ 2215f891b7eSNickeau $codes = array(); 2225f891b7eSNickeau /** 2235f891b7eSNickeau * Does the javascript contains a console statement 2245f891b7eSNickeau */ 2255f891b7eSNickeau $useConsole = false; 2265f891b7eSNickeau $exitTag = new Tag(self::TAG, array(), $state, $handler); 2275f891b7eSNickeau $openingTag = $exitTag->getOpeningTag(); 2285f891b7eSNickeau if ($openingTag->hasDescendants()) { 2295f891b7eSNickeau $tags = $openingTag->getDescendants(); 2305f891b7eSNickeau /** 2315f891b7eSNickeau * Mime and code content are in two differents 2325f891b7eSNickeau * tag. To be able to set the content to the good type 2335f891b7eSNickeau * we keep a trace of it 2345f891b7eSNickeau */ 2355f891b7eSNickeau $actualCodeType = ""; 2365f891b7eSNickeau foreach ($tags as $tag) { 2375f891b7eSNickeau if (in_array($tag->getName(), self::CODE_TAGS)) { 2385f891b7eSNickeau 2395f891b7eSNickeau if ($tag->getState() == DOKU_LEXER_ENTER) { 2405f891b7eSNickeau // Get the code (The content between the code nodes) 2415f891b7eSNickeau // We ltrim because the match gives us the \n at the beginning and at the end 2425f891b7eSNickeau $actualCodeType = strtolower(trim($tag->getType())); 2435f891b7eSNickeau 2445f891b7eSNickeau // Xml is html 2455f891b7eSNickeau if ($actualCodeType == 'xml') { 2465f891b7eSNickeau $actualCodeType = 'html'; 2475f891b7eSNickeau } 2485f891b7eSNickeau // The code for a language may be scattered in mutliple block 2495f891b7eSNickeau if (!isset($codes[$actualCodeType])) { 2505f891b7eSNickeau $codes[$actualCodeType] = ""; 2515f891b7eSNickeau } 2525f891b7eSNickeau continue; 2535f891b7eSNickeau } 2545f891b7eSNickeau 2555f891b7eSNickeau if ($tag->getState() == DOKU_LEXER_UNMATCHED) { 2565f891b7eSNickeau 2575f891b7eSNickeau $codeContent = $tag->getData()[PluginUtility::PAYLOAD]; 2585f891b7eSNickeau 2595f891b7eSNickeau if (empty($actualCodeType)) { 2605f891b7eSNickeau LogUtility::msg("The type of the code should not be null for the code content " . $codeContent, LogUtility::LVL_MSG_WARNING, self::TAG); 2615f891b7eSNickeau continue; 2625f891b7eSNickeau } 2635f891b7eSNickeau 2645f891b7eSNickeau // Append it 2655f891b7eSNickeau $codes[$actualCodeType] = $codes[$actualCodeType] . $codeContent; 2665f891b7eSNickeau 2675f891b7eSNickeau // Check if a javascript console function is used, only if the flag is not set to true 2685f891b7eSNickeau if (!$useConsole == true) { 2695f891b7eSNickeau if (in_array($actualCodeType, array('babel', 'javascript', 'html', 'xml'))) { 2705f891b7eSNickeau // if the code contains 'console.' 2715f891b7eSNickeau $result = preg_match('/' . 'console\.' . '/is', $codeContent); 2725f891b7eSNickeau if ($result) { 2735f891b7eSNickeau $useConsole = true; 2745f891b7eSNickeau } 2755f891b7eSNickeau } 2765f891b7eSNickeau } 2775f891b7eSNickeau // Reset 2785f891b7eSNickeau $actualCodeType = ""; 2795f891b7eSNickeau } 2805f891b7eSNickeau } 2815f891b7eSNickeau } 2825f891b7eSNickeau } 2835f891b7eSNickeau return array( 2845f891b7eSNickeau PluginUtility::STATE => $state, 2855f891b7eSNickeau self::CODES_ATTRIBUTE => $codes, 2865f891b7eSNickeau self::USE_CONSOLE_ATTRIBUTE => $useConsole 2875f891b7eSNickeau ); 2885f891b7eSNickeau 2895f891b7eSNickeau } 2905f891b7eSNickeau return false; 2915f891b7eSNickeau 2925f891b7eSNickeau } 2935f891b7eSNickeau 2945f891b7eSNickeau /** 2955f891b7eSNickeau * Render the output 2965f891b7eSNickeau * @param string $mode 2975f891b7eSNickeau * @param Doku_Renderer $renderer 2985f891b7eSNickeau * @param array $data - what the function handle() return'ed 2995f891b7eSNickeau * @return bool - rendered correctly (not used) 3005f891b7eSNickeau * 3015f891b7eSNickeau * The rendering process 3025f891b7eSNickeau * @see DokuWiki_Syntax_Plugin::render() 3035f891b7eSNickeau * 3045f891b7eSNickeau */ 3055f891b7eSNickeau public function render($mode, Doku_Renderer $renderer, $data) 3065f891b7eSNickeau { 3075f891b7eSNickeau // The $data variable comes from the handle() function 3085f891b7eSNickeau // 3095f891b7eSNickeau // $mode = 'xhtml' means that we output html 3105f891b7eSNickeau // There is other mode such as metadata where you can output data for the headers (Not 100% sure) 3115f891b7eSNickeau if ($mode == 'xhtml') { 3125f891b7eSNickeau 3135f891b7eSNickeau 3145f891b7eSNickeau /** @var Doku_Renderer_xhtml $renderer */ 3155f891b7eSNickeau 3165f891b7eSNickeau $state = $data[PluginUtility::STATE]; 3175f891b7eSNickeau switch ($state) { 3185f891b7eSNickeau 3195f891b7eSNickeau case DOKU_LEXER_ENTER : 3205f891b7eSNickeau 3218aa9d0e6Sgerardnico PluginUtility::getSnippetManager()->upsertJavascriptForBar(self::TAG); 3225f891b7eSNickeau 3235f891b7eSNickeau // The extracted data are the attribute of the webcode tag 3245f891b7eSNickeau // We put in a class variable so that we can use in the last step (DOKU_LEXER_EXIT) 3255f891b7eSNickeau $this->attributes = $data[PluginUtility::ATTRIBUTES]; 3265f891b7eSNickeau 3275f891b7eSNickeau break; 3285f891b7eSNickeau 3295f891b7eSNickeau case DOKU_LEXER_UNMATCHED : 3305f891b7eSNickeau 331*32b85071SNickeau $renderer->doc .= PluginUtility::renderUnmatched($data); 3325f891b7eSNickeau break; 3335f891b7eSNickeau 3345f891b7eSNickeau case DOKU_LEXER_EXIT : 3355f891b7eSNickeau $codes = $data[self::CODES_ATTRIBUTE]; 3365f891b7eSNickeau // Create the real output of webcode 3375f891b7eSNickeau if (sizeof($codes) == 0) { 3385f891b7eSNickeau return false; 3395f891b7eSNickeau } 3405f891b7eSNickeau 3418aa9d0e6Sgerardnico PluginUtility::getSnippetManager()->upsertCssSnippetForBar(self::TAG); 3425f891b7eSNickeau 3435f891b7eSNickeau // Dokuwiki Code ? 3445f891b7eSNickeau if (array_key_exists('dw', $codes)) { 3455f891b7eSNickeau 3465f891b7eSNickeau $renderer->doc .= PluginUtility::render($codes['dw']); 3475f891b7eSNickeau 3485f891b7eSNickeau } else { 3495f891b7eSNickeau 3505f891b7eSNickeau 3515f891b7eSNickeau // Js, Html, Css 3525f891b7eSNickeau $iframeHtml = '<html><head>'; 3535f891b7eSNickeau $iframeHtml .= '<meta http-equiv="content-type" content="text/html; charset=UTF-8">'; 3545f891b7eSNickeau $iframeHtml .= '<title>Made by Webcode</title>'; 3555f891b7eSNickeau $iframeHtml .= '<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css">'; 3565f891b7eSNickeau 3575f891b7eSNickeau 3585f891b7eSNickeau // External Resources such as css stylesheet or js 3595f891b7eSNickeau $externalResources = array(); 3605f891b7eSNickeau if (array_key_exists(self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY, $this->attributes)) { 3615f891b7eSNickeau $externalResources = explode(",", $this->attributes[self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY]); 3625f891b7eSNickeau } 3635f891b7eSNickeau 3645f891b7eSNickeau // Babel Preprocessor, if babel is used, add it to the external resources 3655f891b7eSNickeau if (array_key_exists('babel', $codes)) { 3665f891b7eSNickeau $babelMin = "https://unpkg.com/babel-standalone@6/babel.min.js"; 3675f891b7eSNickeau // a load of babel invoke it (be sure to not have it twice 3685f891b7eSNickeau if (!(array_key_exists($babelMin, $externalResources))) { 3695f891b7eSNickeau $externalResources[] = $babelMin; 3705f891b7eSNickeau } 3715f891b7eSNickeau } 3725f891b7eSNickeau 3735f891b7eSNickeau // Add the external resources 3745f891b7eSNickeau foreach ($externalResources as $externalResource) { 3755f891b7eSNickeau $pathInfo = pathinfo($externalResource); 3765f891b7eSNickeau $fileExtension = $pathInfo['extension']; 3775f891b7eSNickeau switch ($fileExtension) { 3785f891b7eSNickeau case 'css': 3795f891b7eSNickeau $iframeHtml .= '<link rel="stylesheet" type="text/css" href="' . $externalResource . '">'; 3805f891b7eSNickeau break; 3815f891b7eSNickeau case 'js': 3825f891b7eSNickeau $iframeHtml .= '<script type="text/javascript" src="' . $externalResource . '"></script>'; 3835f891b7eSNickeau break; 3845f891b7eSNickeau } 3855f891b7eSNickeau } 3865f891b7eSNickeau 3875f891b7eSNickeau 3885f891b7eSNickeau // WebConsole style sheet 3895f891b7eSNickeau $iframeHtml .= '<link rel="stylesheet" type="text/css" href="' . PluginUtility::getResourceBaseUrl() . '/webcode/webcode-iframe.css?ver=' . self::WEB_CSS_VERSION . '"/>'; 3905f891b7eSNickeau 3915f891b7eSNickeau if (array_key_exists('css', $codes)) { 3925f891b7eSNickeau $iframeHtml .= '<!-- The CSS code -->'; 3935f891b7eSNickeau $iframeHtml .= '<style>' . $codes['css'] . '</style>'; 3945f891b7eSNickeau }; 3955f891b7eSNickeau $iframeHtml .= '</head><body style="margin:10px">'; 3965f891b7eSNickeau if (array_key_exists('html', $codes)) { 3975f891b7eSNickeau $iframeHtml .= '<!-- The HTML code -->'; 3985f891b7eSNickeau $iframeHtml .= $codes['html']; 3995f891b7eSNickeau } 4005f891b7eSNickeau // The javascript console area is based at the end of the HTML document 4015f891b7eSNickeau $useConsole = $data[self::USE_CONSOLE_ATTRIBUTE]; 4025f891b7eSNickeau if ($useConsole) { 4035f891b7eSNickeau $iframeHtml .= '<!-- WebCode Console -->'; 4045f891b7eSNickeau $iframeHtml .= '<div><p class=\'webConsoleTitle\'>Console Output:</p>'; 4055f891b7eSNickeau $iframeHtml .= '<div id=\'webCodeConsole\'></div>'; 4065f891b7eSNickeau $iframeHtml .= '<script type=\'text/javascript\' src=\'' . PluginUtility::getResourceBaseUrl() . '/webcode/webcode-console.js?ver=' . self::WEB_CONSOLE_JS_VERSION . '\'></script>'; 4075f891b7eSNickeau $iframeHtml .= '</div>'; 4085f891b7eSNickeau } 4095f891b7eSNickeau // The javascript comes at the end because it may want to be applied on previous HTML element 4105f891b7eSNickeau // as the page load in the IO order, javascript must be placed at the end 4115f891b7eSNickeau if (array_key_exists('javascript', $codes)) { 4125f891b7eSNickeau $iframeHtml .= '<!-- The Javascript code -->'; 4135f891b7eSNickeau $iframeHtml .= '<script type="text/javascript">' . $codes['javascript'] . '</script>'; 4145f891b7eSNickeau } 4155f891b7eSNickeau if (array_key_exists('babel', $codes)) { 4165f891b7eSNickeau $iframeHtml .= '<!-- The Babel code -->'; 4175f891b7eSNickeau $iframeHtml .= '<script type="text/babel">' . $codes['babel'] . '</script>'; 4185f891b7eSNickeau } 4195f891b7eSNickeau $iframeHtml .= '</body></html>'; 4205f891b7eSNickeau 4215f891b7eSNickeau // Here the magic from the plugin happens 4225f891b7eSNickeau // We add the Iframe and the JsFiddleButton 4235f891b7eSNickeau $iFrameHtml = '<iframe '; 4245f891b7eSNickeau 4255f891b7eSNickeau // We add the name HTML attribute 4265f891b7eSNickeau $name = "WebCode iFrame"; 4275f891b7eSNickeau if (array_key_exists('name', $this->attributes)) { 4285f891b7eSNickeau $name .= ' ' . $this->attributes['name']; 4295f891b7eSNickeau } 4305f891b7eSNickeau $iFrameHtml .= ' name="' . $name . '" '; 4315f891b7eSNickeau 4325f891b7eSNickeau // The class to be able to select them 4335f891b7eSNickeau $iFrameHtml .= ' class="webCode" '; 4345f891b7eSNickeau 4355f891b7eSNickeau // We add the others HTML attributes 4365f891b7eSNickeau $iFrameHtmlAttributes = array('width', 'height', 'frameborder', 'scrolling'); 4375f891b7eSNickeau foreach ($this->attributes as $attribute => $value) { 4385f891b7eSNickeau if (in_array($attribute, $iFrameHtmlAttributes)) { 4395f891b7eSNickeau $iFrameHtml .= ' ' . $attribute . '=' . $value; 4405f891b7eSNickeau } 4415f891b7eSNickeau } 4425f891b7eSNickeau $iFrameHtml .= ' srcdoc="' . htmlentities($iframeHtml) . '" ></iframe>';// 4435f891b7eSNickeau 4445f891b7eSNickeau // Credits bar 4455f891b7eSNickeau $bar = '<div class="webcode-bar">'; 4465f891b7eSNickeau $bar .= '<div class="webcode-bar-item">' . PluginUtility::getUrl(self::TAG, "Rendered by Webcode",false) . '</div>'; 4475f891b7eSNickeau $bar .= '<div class="webcode-bar-item">' . $this->addJsFiddleButton($codes, $this->attributes) . '</div>'; 4485f891b7eSNickeau $bar .= '</div>'; 4495f891b7eSNickeau $renderer->doc .= '<div class="webcode">' . $iFrameHtml . $bar . '</div>'; 4505f891b7eSNickeau } 4515f891b7eSNickeau 4525f891b7eSNickeau break; 4535f891b7eSNickeau } 4545f891b7eSNickeau 4555f891b7eSNickeau return true; 4565f891b7eSNickeau } 4575f891b7eSNickeau return false; 4585f891b7eSNickeau } 4595f891b7eSNickeau 4605f891b7eSNickeau /** 4615f891b7eSNickeau * @param array $codes the array containing the codes 4625f891b7eSNickeau * @param array $attributes the attributes of a call (for now the externalResources) 4635f891b7eSNickeau * @return string the HTML form code 4645f891b7eSNickeau * 4655f891b7eSNickeau * Specification, see http://doc.jsfiddle.net/api/post.html 4665f891b7eSNickeau */ 4675f891b7eSNickeau public function addJsFiddleButton($codes, $attributes) 4685f891b7eSNickeau { 4695f891b7eSNickeau 4705f891b7eSNickeau $postURL = "https://jsfiddle.net/api/post/library/pure/"; //No Framework 4715f891b7eSNickeau 4725f891b7eSNickeau $externalResources = array(); 4735f891b7eSNickeau if (array_key_exists(self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY, $attributes)) { 4745f891b7eSNickeau $externalResources = explode(",", $attributes[self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY]); 4755f891b7eSNickeau } 4765f891b7eSNickeau 4775f891b7eSNickeau 4785f891b7eSNickeau if ($this->useConsole) { 4795f891b7eSNickeau // If their is a console.log function, add the Firebug Lite support of JsFiddle 4805f891b7eSNickeau // Seems to work only with the Edge version of jQuery 4815f891b7eSNickeau // $postURL .= "edge/dependencies/Lite/"; 4825f891b7eSNickeau // The firebug logging is not working anymore because of 404 4835f891b7eSNickeau // Adding them here 4845f891b7eSNickeau $externalResources[] = 'The firebug resources for the console.log features'; 4855f891b7eSNickeau $externalResources[] = PluginUtility::getResourceBaseUrl() . '/firebug/firebug-lite.css'; 4865f891b7eSNickeau $externalResources[] = PluginUtility::getResourceBaseUrl() . '/firebug/firebug-lite-1.2.js'; 4875f891b7eSNickeau } 4885f891b7eSNickeau 4895f891b7eSNickeau // The below code is to prevent this JsFiddle bug: https://github.com/jsfiddle/jsfiddle-issues/issues/726 4905f891b7eSNickeau // The order of the resources is not guaranteed 4915f891b7eSNickeau // We pass then the resources only if their is one resources 4925f891b7eSNickeau // Otherwise we pass them as a script element in the HTML. 4935f891b7eSNickeau if (count($externalResources) <= 1) { 4945f891b7eSNickeau $externalResourcesInput = '<input type="hidden" name="resources" value="' . implode(",", $externalResources) . '">'; 4955f891b7eSNickeau } else { 4965f891b7eSNickeau $codes['html'] .= "\n\n\n\n\n<!-- The resources -->\n"; 4975f891b7eSNickeau $codes['html'] .= "<!-- They have been added here because their order is not guarantee through the API. -->\n"; 4985f891b7eSNickeau $codes['html'] .= "<!-- See: https://github.com/jsfiddle/jsfiddle-issues/issues/726 -->\n"; 4995f891b7eSNickeau foreach ($externalResources as $externalResource) { 5005f891b7eSNickeau if ($externalResource != "") { 5015f891b7eSNickeau $extension = pathinfo($externalResource)['extension']; 5025f891b7eSNickeau switch ($extension) { 5035f891b7eSNickeau case "css": 5045f891b7eSNickeau $codes['html'] .= "<link href=\"" . $externalResource . "\" rel=\"stylesheet\">\n"; 5055f891b7eSNickeau break; 5065f891b7eSNickeau case "js": 5075f891b7eSNickeau $codes['html'] .= "<script src=\"" . $externalResource . "\"></script>\n"; 5085f891b7eSNickeau break; 5095f891b7eSNickeau default: 5105f891b7eSNickeau $codes['html'] .= "<!-- " . $externalResource . " -->\n"; 5115f891b7eSNickeau } 5125f891b7eSNickeau } 5135f891b7eSNickeau } 5145f891b7eSNickeau } 5155f891b7eSNickeau 5165f891b7eSNickeau $jsCode = $codes['javascript']; 5175f891b7eSNickeau $jsPanel = 0; // language for the js specific panel (0 = JavaScript) 5185f891b7eSNickeau if (array_key_exists('babel', $codes)) { 5195f891b7eSNickeau $jsCode = $codes['babel']; 5205f891b7eSNickeau $jsPanel = 3; // 3 = Babel 5215f891b7eSNickeau } 5225f891b7eSNickeau 5235f891b7eSNickeau // Title and description 5245f891b7eSNickeau global $ID; 5255f891b7eSNickeau $title = $attributes['name']; 5265f891b7eSNickeau $pageTitle = tpl_pagetitle($ID, true); 5275f891b7eSNickeau if (!$title) { 5285f891b7eSNickeau 5295f891b7eSNickeau $title = "Code from " . $pageTitle; 5305f891b7eSNickeau } 5315f891b7eSNickeau $description = "Code from the page '" . $pageTitle . "' \n" . wl($ID, $absolute = true); 5325f891b7eSNickeau return '<form method="post" action="' . $postURL . '" target="_blank">' . 5335f891b7eSNickeau '<input type="hidden" name="title" value="' . htmlentities($title) . '">' . 5345f891b7eSNickeau '<input type="hidden" name="description" value="' . htmlentities($description) . '">' . 5355f891b7eSNickeau '<input type="hidden" name="css" value="' . htmlentities($codes['css']) . '">' . 5365f891b7eSNickeau '<input type="hidden" name="html" value="' . htmlentities("<!-- The HTML -->" . $codes['html']) . '">' . 5375f891b7eSNickeau '<input type="hidden" name="js" value="' . htmlentities($jsCode) . '">' . 5385f891b7eSNickeau '<input type="hidden" name="panel_js" value="' . htmlentities($jsPanel) . '">' . 5395f891b7eSNickeau '<input type="hidden" name="wrap" value="b">' . //javascript no wrap in body 5405f891b7eSNickeau $externalResourcesInput . 5415f891b7eSNickeau '<button>Try the code</button>' . 5425f891b7eSNickeau '</form>'; 5435f891b7eSNickeau 5445f891b7eSNickeau } 5455f891b7eSNickeau 5465f891b7eSNickeau /** 5475f891b7eSNickeau * @param $codes the array containing the codes 5485f891b7eSNickeau * @param $attributes the attributes of a call (for now the externalResources) 5495f891b7eSNickeau * @return string the HTML form code 5505f891b7eSNickeau */ 5515f891b7eSNickeau public function addCodePenButton($codes, $attributes) 5525f891b7eSNickeau { 5535f891b7eSNickeau // TODO 5545f891b7eSNickeau // http://blog.codepen.io/documentation/api/prefill/ 5555f891b7eSNickeau } 5565f891b7eSNickeau 5575f891b7eSNickeau 5585f891b7eSNickeau} 559