1*5f891b7eSNickeau<?php 2*5f891b7eSNickeau/** 3*5f891b7eSNickeau * Copyright (c) 2021. ComboStrap, Inc. and its affiliates. All Rights Reserved. 4*5f891b7eSNickeau * 5*5f891b7eSNickeau * This source code is licensed under the GPL license found in the 6*5f891b7eSNickeau * COPYING file in the root directory of this source tree. 7*5f891b7eSNickeau * 8*5f891b7eSNickeau * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 9*5f891b7eSNickeau * @author ComboStrap <support@combostrap.com> 10*5f891b7eSNickeau * 11*5f891b7eSNickeau */ 12*5f891b7eSNickeau 13*5f891b7eSNickeau/** 14*5f891b7eSNickeau * Plugin Webcode: Show webcode (Css, HTML) in a iframe 15*5f891b7eSNickeau * 16*5f891b7eSNickeau * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 17*5f891b7eSNickeau * @author Nicolas GERARD 18*5f891b7eSNickeau */ 19*5f891b7eSNickeau 20*5f891b7eSNickeau// must be run within Dokuwiki 21*5f891b7eSNickeauuse ComboStrap\SnippetManager; 22*5f891b7eSNickeauuse ComboStrap\LogUtility; 23*5f891b7eSNickeauuse ComboStrap\PluginUtility; 24*5f891b7eSNickeauuse ComboStrap\Tag; 25*5f891b7eSNickeau 26*5f891b7eSNickeauif (!defined('DOKU_INC')) die(); 27*5f891b7eSNickeau 28*5f891b7eSNickeau/** 29*5f891b7eSNickeau * Webcode 30*5f891b7eSNickeau */ 31*5f891b7eSNickeauclass syntax_plugin_combo_webcode extends DokuWiki_Syntax_Plugin 32*5f891b7eSNickeau{ 33*5f891b7eSNickeau 34*5f891b7eSNickeau const EXTERNAL_RESOURCES_ATTRIBUTE_DISPLAY = 'externalResources'; // In the action bar 35*5f891b7eSNickeau const EXTERNAL_RESOURCES_ATTRIBUTE_KEY = 'externalresources'; // In the code 36*5f891b7eSNickeau 37*5f891b7eSNickeau // Simple cache bursting implementation for the webCodeConsole.(js|css) file 38*5f891b7eSNickeau // They must be incremented manually when they changed 39*5f891b7eSNickeau const WEB_CSS_VERSION = 1.1; 40*5f891b7eSNickeau const WEB_CONSOLE_JS_VERSION = 2.1; 41*5f891b7eSNickeau 42*5f891b7eSNickeau const TAG = 'webcode'; 43*5f891b7eSNickeau 44*5f891b7eSNickeau /** 45*5f891b7eSNickeau * The tag that have codes 46*5f891b7eSNickeau */ 47*5f891b7eSNickeau const CODE_TAGS = array("code", "plugin_combo_code"); 48*5f891b7eSNickeau 49*5f891b7eSNickeau /** 50*5f891b7eSNickeau * The attribute names in the array 51*5f891b7eSNickeau */ 52*5f891b7eSNickeau const CODES_ATTRIBUTE = "codes"; 53*5f891b7eSNickeau const USE_CONSOLE_ATTRIBUTE = "useConsole"; 54*5f891b7eSNickeau 55*5f891b7eSNickeau /** 56*5f891b7eSNickeau * @var array that holds the iframe attributes 57*5f891b7eSNickeau */ 58*5f891b7eSNickeau private $attributes = array(); 59*5f891b7eSNickeau 60*5f891b7eSNickeau 61*5f891b7eSNickeau /** 62*5f891b7eSNickeau * Syntax Type. 63*5f891b7eSNickeau * 64*5f891b7eSNickeau * Needs to return one of the mode types defined in $PARSER_MODES in parser.php 65*5f891b7eSNickeau * @see https://www.dokuwiki.org/devel:syntax_plugins#syntax_types 66*5f891b7eSNickeau * 67*5f891b7eSNickeau * container because it may contain header in case of how to 68*5f891b7eSNickeau */ 69*5f891b7eSNickeau public function getType() 70*5f891b7eSNickeau { 71*5f891b7eSNickeau return 'container'; 72*5f891b7eSNickeau } 73*5f891b7eSNickeau 74*5f891b7eSNickeau /** 75*5f891b7eSNickeau * @return array 76*5f891b7eSNickeau * Allow which kind of plugin inside 77*5f891b7eSNickeau * 78*5f891b7eSNickeau * array('container', 'baseonly','formatting', 'substition', 'protected', 'disabled', 'paragraphs') 79*5f891b7eSNickeau * 80*5f891b7eSNickeau */ 81*5f891b7eSNickeau public function getAllowedTypes() 82*5f891b7eSNickeau { 83*5f891b7eSNickeau return array('container', 'baseonly', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs'); 84*5f891b7eSNickeau } 85*5f891b7eSNickeau 86*5f891b7eSNickeau 87*5f891b7eSNickeau public function accepts($mode) 88*5f891b7eSNickeau { 89*5f891b7eSNickeau if (!$this->getConf(syntax_plugin_combo_preformatted::CONF_PREFORMATTED_ENABLE)) { 90*5f891b7eSNickeau return PluginUtility::disablePreformatted($mode); 91*5f891b7eSNickeau } else { 92*5f891b7eSNickeau return true; 93*5f891b7eSNickeau } 94*5f891b7eSNickeau } 95*5f891b7eSNickeau 96*5f891b7eSNickeau /** 97*5f891b7eSNickeau * @see Doku_Parser_Mode::getSort() 98*5f891b7eSNickeau * The mode (plugin) with the lowest sort number will win out 99*5f891b7eSNickeau * 100*5f891b7eSNickeau * See {@link Doku_Parser_Mode_code} 101*5f891b7eSNickeau */ 102*5f891b7eSNickeau public function getSort() 103*5f891b7eSNickeau { 104*5f891b7eSNickeau return 99; 105*5f891b7eSNickeau } 106*5f891b7eSNickeau 107*5f891b7eSNickeau /** 108*5f891b7eSNickeau * Called before any calls to ConnectTo 109*5f891b7eSNickeau * @return void 110*5f891b7eSNickeau */ 111*5f891b7eSNickeau function preConnect() 112*5f891b7eSNickeau { 113*5f891b7eSNickeau } 114*5f891b7eSNickeau 115*5f891b7eSNickeau /** 116*5f891b7eSNickeau * Create a pattern that will called this plugin 117*5f891b7eSNickeau * 118*5f891b7eSNickeau * @param string $mode 119*5f891b7eSNickeau * 120*5f891b7eSNickeau * All dokuwiki mode can be seen in the parser.php file 121*5f891b7eSNickeau * @see Doku_Parser_Mode::connectTo() 122*5f891b7eSNickeau */ 123*5f891b7eSNickeau public function connectTo($mode) 124*5f891b7eSNickeau { 125*5f891b7eSNickeau 126*5f891b7eSNickeau $pattern = PluginUtility::getContainerTagPattern(self::TAG); 127*5f891b7eSNickeau $this->Lexer->addEntryPattern($pattern, $mode, PluginUtility::getModeForComponent($this->getPluginComponent())); 128*5f891b7eSNickeau 129*5f891b7eSNickeau } 130*5f891b7eSNickeau 131*5f891b7eSNickeau 132*5f891b7eSNickeau // This where the addPattern and addExitPattern are defined 133*5f891b7eSNickeau public function postConnect() 134*5f891b7eSNickeau { 135*5f891b7eSNickeau $this->Lexer->addExitPattern('</' . self::TAG . '>', PluginUtility::getModeForComponent($this->getPluginComponent())); 136*5f891b7eSNickeau } 137*5f891b7eSNickeau 138*5f891b7eSNickeau 139*5f891b7eSNickeau /** 140*5f891b7eSNickeau * Handle the match 141*5f891b7eSNickeau * You get the match for each pattern in the $match variable 142*5f891b7eSNickeau * $state says if it's an entry, exit or match pattern 143*5f891b7eSNickeau * 144*5f891b7eSNickeau * This is an instruction block and is cached apart from the rendering output 145*5f891b7eSNickeau * There is two caches levels 146*5f891b7eSNickeau * This cache may be suppressed with the url parameters ?purge=true 147*5f891b7eSNickeau * 148*5f891b7eSNickeau * The returned values are cached in an array that will be passed to the render method 149*5f891b7eSNickeau * The handle function goal is to parse the matched syntax through the pattern function 150*5f891b7eSNickeau * and to return the result for use in the renderer 151*5f891b7eSNickeau * This result is always cached until the page is modified. 152*5f891b7eSNickeau * @param string $match 153*5f891b7eSNickeau * @param int $state 154*5f891b7eSNickeau * @param int $pos 155*5f891b7eSNickeau * @param Doku_Handler $handler 156*5f891b7eSNickeau * @return array|bool 157*5f891b7eSNickeau * @throws Exception 158*5f891b7eSNickeau * @see DokuWiki_Syntax_Plugin::handle() 159*5f891b7eSNickeau * 160*5f891b7eSNickeau */ 161*5f891b7eSNickeau public function handle($match, $state, $pos, Doku_Handler $handler) 162*5f891b7eSNickeau { 163*5f891b7eSNickeau switch ($state) { 164*5f891b7eSNickeau 165*5f891b7eSNickeau case DOKU_LEXER_ENTER : 166*5f891b7eSNickeau 167*5f891b7eSNickeau // We got the first webcode tag and its attributes 168*5f891b7eSNickeau 169*5f891b7eSNickeau $match = substr($match, 8, -1); //9 = strlen("<webcode") 170*5f891b7eSNickeau 171*5f891b7eSNickeau // Reset of the attributes 172*5f891b7eSNickeau // With some framework the php object may be still persisted in memory 173*5f891b7eSNickeau // And you may get some attributes from other page 174*5f891b7eSNickeau $attributes = array(); 175*5f891b7eSNickeau $attributes['frameborder'] = 1; 176*5f891b7eSNickeau $attributes['width'] = '100%'; 177*5f891b7eSNickeau 178*5f891b7eSNickeau $renderingModeKey = 'renderingmode'; 179*5f891b7eSNickeau $attributes[$renderingModeKey] = 'story'; 180*5f891b7eSNickeau 181*5f891b7eSNickeau // config Parameters will get their value in lowercase 182*5f891b7eSNickeau $configAttributes = [$renderingModeKey]; 183*5f891b7eSNickeau 184*5f891b7eSNickeau // /i not case sensitive 185*5f891b7eSNickeau $attributePattern = "\s*(\w+)\s*=\s*\"?([^\"\s]+)\"?\\s*"; 186*5f891b7eSNickeau $result = preg_match_all('/' . $attributePattern . '/i', $match, $matches); 187*5f891b7eSNickeau 188*5f891b7eSNickeau 189*5f891b7eSNickeau if ($result != 0) { 190*5f891b7eSNickeau foreach ($matches[1] as $key => $lang) { 191*5f891b7eSNickeau $attributeKey = strtolower($lang); 192*5f891b7eSNickeau $attributeValue = $matches[2][$key]; 193*5f891b7eSNickeau if (in_array($attributeKey, $configAttributes)) { 194*5f891b7eSNickeau $attributeValue = strtolower($attributeValue); 195*5f891b7eSNickeau } 196*5f891b7eSNickeau $attributes[$attributeKey] = $attributeValue; 197*5f891b7eSNickeau } 198*5f891b7eSNickeau } 199*5f891b7eSNickeau 200*5f891b7eSNickeau // We set the attributes on a class scope 201*5f891b7eSNickeau // to be used in the DOKU_LEXER_UNMATCHED step 202*5f891b7eSNickeau $this->attributes = $attributes; 203*5f891b7eSNickeau 204*5f891b7eSNickeau // Cache the values to be used by the render method 205*5f891b7eSNickeau return array( 206*5f891b7eSNickeau PluginUtility::STATE => $state, 207*5f891b7eSNickeau PluginUtility::ATTRIBUTES => $attributes 208*5f891b7eSNickeau ); 209*5f891b7eSNickeau 210*5f891b7eSNickeau 211*5f891b7eSNickeau case DOKU_LEXER_UNMATCHED : 212*5f891b7eSNickeau 213*5f891b7eSNickeau // Cache the values 214*5f891b7eSNickeau return array( 215*5f891b7eSNickeau PluginUtility::STATE => $state, 216*5f891b7eSNickeau PluginUtility::PAYLOAD => $match 217*5f891b7eSNickeau ); 218*5f891b7eSNickeau 219*5f891b7eSNickeau case DOKU_LEXER_EXIT: 220*5f891b7eSNickeau 221*5f891b7eSNickeau /** 222*5f891b7eSNickeau * Capture all codes 223*5f891b7eSNickeau */ 224*5f891b7eSNickeau $codes = array(); 225*5f891b7eSNickeau /** 226*5f891b7eSNickeau * Does the javascript contains a console statement 227*5f891b7eSNickeau */ 228*5f891b7eSNickeau $useConsole = false; 229*5f891b7eSNickeau $exitTag = new Tag(self::TAG, array(), $state, $handler); 230*5f891b7eSNickeau $openingTag = $exitTag->getOpeningTag(); 231*5f891b7eSNickeau if ($openingTag->hasDescendants()) { 232*5f891b7eSNickeau $tags = $openingTag->getDescendants(); 233*5f891b7eSNickeau /** 234*5f891b7eSNickeau * Mime and code content are in two differents 235*5f891b7eSNickeau * tag. To be able to set the content to the good type 236*5f891b7eSNickeau * we keep a trace of it 237*5f891b7eSNickeau */ 238*5f891b7eSNickeau $actualCodeType = ""; 239*5f891b7eSNickeau foreach ($tags as $tag) { 240*5f891b7eSNickeau if (in_array($tag->getName(), self::CODE_TAGS)) { 241*5f891b7eSNickeau 242*5f891b7eSNickeau if ($tag->getState() == DOKU_LEXER_ENTER) { 243*5f891b7eSNickeau // Get the code (The content between the code nodes) 244*5f891b7eSNickeau // We ltrim because the match gives us the \n at the beginning and at the end 245*5f891b7eSNickeau $actualCodeType = strtolower(trim($tag->getType())); 246*5f891b7eSNickeau 247*5f891b7eSNickeau // Xml is html 248*5f891b7eSNickeau if ($actualCodeType == 'xml') { 249*5f891b7eSNickeau $actualCodeType = 'html'; 250*5f891b7eSNickeau } 251*5f891b7eSNickeau // The code for a language may be scattered in mutliple block 252*5f891b7eSNickeau if (!isset($codes[$actualCodeType])) { 253*5f891b7eSNickeau $codes[$actualCodeType] = ""; 254*5f891b7eSNickeau } 255*5f891b7eSNickeau continue; 256*5f891b7eSNickeau } 257*5f891b7eSNickeau 258*5f891b7eSNickeau if ($tag->getState() == DOKU_LEXER_UNMATCHED) { 259*5f891b7eSNickeau 260*5f891b7eSNickeau $codeContent = $tag->getData()[PluginUtility::PAYLOAD]; 261*5f891b7eSNickeau 262*5f891b7eSNickeau if (empty($actualCodeType)) { 263*5f891b7eSNickeau LogUtility::msg("The type of the code should not be null for the code content " . $codeContent, LogUtility::LVL_MSG_WARNING, self::TAG); 264*5f891b7eSNickeau continue; 265*5f891b7eSNickeau } 266*5f891b7eSNickeau 267*5f891b7eSNickeau // Append it 268*5f891b7eSNickeau $codes[$actualCodeType] = $codes[$actualCodeType] . $codeContent; 269*5f891b7eSNickeau 270*5f891b7eSNickeau // Check if a javascript console function is used, only if the flag is not set to true 271*5f891b7eSNickeau if (!$useConsole == true) { 272*5f891b7eSNickeau if (in_array($actualCodeType, array('babel', 'javascript', 'html', 'xml'))) { 273*5f891b7eSNickeau // if the code contains 'console.' 274*5f891b7eSNickeau $result = preg_match('/' . 'console\.' . '/is', $codeContent); 275*5f891b7eSNickeau if ($result) { 276*5f891b7eSNickeau $useConsole = true; 277*5f891b7eSNickeau } 278*5f891b7eSNickeau } 279*5f891b7eSNickeau } 280*5f891b7eSNickeau // Reset 281*5f891b7eSNickeau $actualCodeType = ""; 282*5f891b7eSNickeau } 283*5f891b7eSNickeau } 284*5f891b7eSNickeau } 285*5f891b7eSNickeau } 286*5f891b7eSNickeau return array( 287*5f891b7eSNickeau PluginUtility::STATE => $state, 288*5f891b7eSNickeau self::CODES_ATTRIBUTE => $codes, 289*5f891b7eSNickeau self::USE_CONSOLE_ATTRIBUTE => $useConsole 290*5f891b7eSNickeau ); 291*5f891b7eSNickeau 292*5f891b7eSNickeau } 293*5f891b7eSNickeau return false; 294*5f891b7eSNickeau 295*5f891b7eSNickeau } 296*5f891b7eSNickeau 297*5f891b7eSNickeau /** 298*5f891b7eSNickeau * Render the output 299*5f891b7eSNickeau * @param string $mode 300*5f891b7eSNickeau * @param Doku_Renderer $renderer 301*5f891b7eSNickeau * @param array $data - what the function handle() return'ed 302*5f891b7eSNickeau * @return bool - rendered correctly (not used) 303*5f891b7eSNickeau * 304*5f891b7eSNickeau * The rendering process 305*5f891b7eSNickeau * @see DokuWiki_Syntax_Plugin::render() 306*5f891b7eSNickeau * 307*5f891b7eSNickeau */ 308*5f891b7eSNickeau public function render($mode, Doku_Renderer $renderer, $data) 309*5f891b7eSNickeau { 310*5f891b7eSNickeau // The $data variable comes from the handle() function 311*5f891b7eSNickeau // 312*5f891b7eSNickeau // $mode = 'xhtml' means that we output html 313*5f891b7eSNickeau // There is other mode such as metadata where you can output data for the headers (Not 100% sure) 314*5f891b7eSNickeau if ($mode == 'xhtml') { 315*5f891b7eSNickeau 316*5f891b7eSNickeau 317*5f891b7eSNickeau /** @var Doku_Renderer_xhtml $renderer */ 318*5f891b7eSNickeau 319*5f891b7eSNickeau $state = $data[PluginUtility::STATE]; 320*5f891b7eSNickeau switch ($state) { 321*5f891b7eSNickeau 322*5f891b7eSNickeau case DOKU_LEXER_ENTER : 323*5f891b7eSNickeau 324*5f891b7eSNickeau PluginUtility::getSnippetManager()->addJavascriptSnippetIfNeeded(self::TAG); 325*5f891b7eSNickeau 326*5f891b7eSNickeau // The extracted data are the attribute of the webcode tag 327*5f891b7eSNickeau // We put in a class variable so that we can use in the last step (DOKU_LEXER_EXIT) 328*5f891b7eSNickeau $this->attributes = $data[PluginUtility::ATTRIBUTES]; 329*5f891b7eSNickeau 330*5f891b7eSNickeau break; 331*5f891b7eSNickeau 332*5f891b7eSNickeau case DOKU_LEXER_UNMATCHED : 333*5f891b7eSNickeau 334*5f891b7eSNickeau // Render and escape 335*5f891b7eSNickeau $renderer->doc .= $renderer->_xmlEntities($data[PluginUtility::PAYLOAD]); 336*5f891b7eSNickeau break; 337*5f891b7eSNickeau 338*5f891b7eSNickeau case DOKU_LEXER_EXIT : 339*5f891b7eSNickeau $codes = $data[self::CODES_ATTRIBUTE]; 340*5f891b7eSNickeau // Create the real output of webcode 341*5f891b7eSNickeau if (sizeof($codes) == 0) { 342*5f891b7eSNickeau return false; 343*5f891b7eSNickeau } 344*5f891b7eSNickeau 345*5f891b7eSNickeau PluginUtility::getSnippetManager()->addCssSnippetOnlyOnce(self::TAG); 346*5f891b7eSNickeau 347*5f891b7eSNickeau // Dokuwiki Code ? 348*5f891b7eSNickeau if (array_key_exists('dw', $codes)) { 349*5f891b7eSNickeau 350*5f891b7eSNickeau $renderer->doc .= PluginUtility::render($codes['dw']); 351*5f891b7eSNickeau 352*5f891b7eSNickeau } else { 353*5f891b7eSNickeau 354*5f891b7eSNickeau 355*5f891b7eSNickeau // Js, Html, Css 356*5f891b7eSNickeau $iframeHtml = '<html><head>'; 357*5f891b7eSNickeau $iframeHtml .= '<meta http-equiv="content-type" content="text/html; charset=UTF-8">'; 358*5f891b7eSNickeau $iframeHtml .= '<title>Made by Webcode</title>'; 359*5f891b7eSNickeau $iframeHtml .= '<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css">'; 360*5f891b7eSNickeau 361*5f891b7eSNickeau 362*5f891b7eSNickeau // External Resources such as css stylesheet or js 363*5f891b7eSNickeau $externalResources = array(); 364*5f891b7eSNickeau if (array_key_exists(self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY, $this->attributes)) { 365*5f891b7eSNickeau $externalResources = explode(",", $this->attributes[self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY]); 366*5f891b7eSNickeau } 367*5f891b7eSNickeau 368*5f891b7eSNickeau // Babel Preprocessor, if babel is used, add it to the external resources 369*5f891b7eSNickeau if (array_key_exists('babel', $codes)) { 370*5f891b7eSNickeau $babelMin = "https://unpkg.com/babel-standalone@6/babel.min.js"; 371*5f891b7eSNickeau // a load of babel invoke it (be sure to not have it twice 372*5f891b7eSNickeau if (!(array_key_exists($babelMin, $externalResources))) { 373*5f891b7eSNickeau $externalResources[] = $babelMin; 374*5f891b7eSNickeau } 375*5f891b7eSNickeau } 376*5f891b7eSNickeau 377*5f891b7eSNickeau // Add the external resources 378*5f891b7eSNickeau foreach ($externalResources as $externalResource) { 379*5f891b7eSNickeau $pathInfo = pathinfo($externalResource); 380*5f891b7eSNickeau $fileExtension = $pathInfo['extension']; 381*5f891b7eSNickeau switch ($fileExtension) { 382*5f891b7eSNickeau case 'css': 383*5f891b7eSNickeau $iframeHtml .= '<link rel="stylesheet" type="text/css" href="' . $externalResource . '">'; 384*5f891b7eSNickeau break; 385*5f891b7eSNickeau case 'js': 386*5f891b7eSNickeau $iframeHtml .= '<script type="text/javascript" src="' . $externalResource . '"></script>'; 387*5f891b7eSNickeau break; 388*5f891b7eSNickeau } 389*5f891b7eSNickeau } 390*5f891b7eSNickeau 391*5f891b7eSNickeau 392*5f891b7eSNickeau // WebConsole style sheet 393*5f891b7eSNickeau $iframeHtml .= '<link rel="stylesheet" type="text/css" href="' . PluginUtility::getResourceBaseUrl() . '/webcode/webcode-iframe.css?ver=' . self::WEB_CSS_VERSION . '"/>'; 394*5f891b7eSNickeau 395*5f891b7eSNickeau if (array_key_exists('css', $codes)) { 396*5f891b7eSNickeau $iframeHtml .= '<!-- The CSS code -->'; 397*5f891b7eSNickeau $iframeHtml .= '<style>' . $codes['css'] . '</style>'; 398*5f891b7eSNickeau }; 399*5f891b7eSNickeau $iframeHtml .= '</head><body style="margin:10px">'; 400*5f891b7eSNickeau if (array_key_exists('html', $codes)) { 401*5f891b7eSNickeau $iframeHtml .= '<!-- The HTML code -->'; 402*5f891b7eSNickeau $iframeHtml .= $codes['html']; 403*5f891b7eSNickeau } 404*5f891b7eSNickeau // The javascript console area is based at the end of the HTML document 405*5f891b7eSNickeau $useConsole = $data[self::USE_CONSOLE_ATTRIBUTE]; 406*5f891b7eSNickeau if ($useConsole) { 407*5f891b7eSNickeau $iframeHtml .= '<!-- WebCode Console -->'; 408*5f891b7eSNickeau $iframeHtml .= '<div><p class=\'webConsoleTitle\'>Console Output:</p>'; 409*5f891b7eSNickeau $iframeHtml .= '<div id=\'webCodeConsole\'></div>'; 410*5f891b7eSNickeau $iframeHtml .= '<script type=\'text/javascript\' src=\'' . PluginUtility::getResourceBaseUrl() . '/webcode/webcode-console.js?ver=' . self::WEB_CONSOLE_JS_VERSION . '\'></script>'; 411*5f891b7eSNickeau $iframeHtml .= '</div>'; 412*5f891b7eSNickeau } 413*5f891b7eSNickeau // The javascript comes at the end because it may want to be applied on previous HTML element 414*5f891b7eSNickeau // as the page load in the IO order, javascript must be placed at the end 415*5f891b7eSNickeau if (array_key_exists('javascript', $codes)) { 416*5f891b7eSNickeau $iframeHtml .= '<!-- The Javascript code -->'; 417*5f891b7eSNickeau $iframeHtml .= '<script type="text/javascript">' . $codes['javascript'] . '</script>'; 418*5f891b7eSNickeau } 419*5f891b7eSNickeau if (array_key_exists('babel', $codes)) { 420*5f891b7eSNickeau $iframeHtml .= '<!-- The Babel code -->'; 421*5f891b7eSNickeau $iframeHtml .= '<script type="text/babel">' . $codes['babel'] . '</script>'; 422*5f891b7eSNickeau } 423*5f891b7eSNickeau $iframeHtml .= '</body></html>'; 424*5f891b7eSNickeau 425*5f891b7eSNickeau // Here the magic from the plugin happens 426*5f891b7eSNickeau // We add the Iframe and the JsFiddleButton 427*5f891b7eSNickeau $iFrameHtml = '<iframe '; 428*5f891b7eSNickeau 429*5f891b7eSNickeau // We add the name HTML attribute 430*5f891b7eSNickeau $name = "WebCode iFrame"; 431*5f891b7eSNickeau if (array_key_exists('name', $this->attributes)) { 432*5f891b7eSNickeau $name .= ' ' . $this->attributes['name']; 433*5f891b7eSNickeau } 434*5f891b7eSNickeau $iFrameHtml .= ' name="' . $name . '" '; 435*5f891b7eSNickeau 436*5f891b7eSNickeau // The class to be able to select them 437*5f891b7eSNickeau $iFrameHtml .= ' class="webCode" '; 438*5f891b7eSNickeau 439*5f891b7eSNickeau // We add the others HTML attributes 440*5f891b7eSNickeau $iFrameHtmlAttributes = array('width', 'height', 'frameborder', 'scrolling'); 441*5f891b7eSNickeau foreach ($this->attributes as $attribute => $value) { 442*5f891b7eSNickeau if (in_array($attribute, $iFrameHtmlAttributes)) { 443*5f891b7eSNickeau $iFrameHtml .= ' ' . $attribute . '=' . $value; 444*5f891b7eSNickeau } 445*5f891b7eSNickeau } 446*5f891b7eSNickeau $iFrameHtml .= ' srcdoc="' . htmlentities($iframeHtml) . '" ></iframe>';// 447*5f891b7eSNickeau 448*5f891b7eSNickeau // Credits bar 449*5f891b7eSNickeau $bar = '<div class="webcode-bar">'; 450*5f891b7eSNickeau $bar .= '<div class="webcode-bar-item">' . PluginUtility::getUrl(self::TAG, "Rendered by Webcode",false) . '</div>'; 451*5f891b7eSNickeau $bar .= '<div class="webcode-bar-item">' . $this->addJsFiddleButton($codes, $this->attributes) . '</div>'; 452*5f891b7eSNickeau $bar .= '</div>'; 453*5f891b7eSNickeau $renderer->doc .= '<div class="webcode">' . $iFrameHtml . $bar . '</div>'; 454*5f891b7eSNickeau } 455*5f891b7eSNickeau 456*5f891b7eSNickeau break; 457*5f891b7eSNickeau } 458*5f891b7eSNickeau 459*5f891b7eSNickeau return true; 460*5f891b7eSNickeau } 461*5f891b7eSNickeau return false; 462*5f891b7eSNickeau } 463*5f891b7eSNickeau 464*5f891b7eSNickeau /** 465*5f891b7eSNickeau * @param array $codes the array containing the codes 466*5f891b7eSNickeau * @param array $attributes the attributes of a call (for now the externalResources) 467*5f891b7eSNickeau * @return string the HTML form code 468*5f891b7eSNickeau * 469*5f891b7eSNickeau * Specification, see http://doc.jsfiddle.net/api/post.html 470*5f891b7eSNickeau */ 471*5f891b7eSNickeau public function addJsFiddleButton($codes, $attributes) 472*5f891b7eSNickeau { 473*5f891b7eSNickeau 474*5f891b7eSNickeau $postURL = "https://jsfiddle.net/api/post/library/pure/"; //No Framework 475*5f891b7eSNickeau 476*5f891b7eSNickeau $externalResources = array(); 477*5f891b7eSNickeau if (array_key_exists(self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY, $attributes)) { 478*5f891b7eSNickeau $externalResources = explode(",", $attributes[self::EXTERNAL_RESOURCES_ATTRIBUTE_KEY]); 479*5f891b7eSNickeau } 480*5f891b7eSNickeau 481*5f891b7eSNickeau 482*5f891b7eSNickeau if ($this->useConsole) { 483*5f891b7eSNickeau // If their is a console.log function, add the Firebug Lite support of JsFiddle 484*5f891b7eSNickeau // Seems to work only with the Edge version of jQuery 485*5f891b7eSNickeau // $postURL .= "edge/dependencies/Lite/"; 486*5f891b7eSNickeau // The firebug logging is not working anymore because of 404 487*5f891b7eSNickeau // Adding them here 488*5f891b7eSNickeau $externalResources[] = 'The firebug resources for the console.log features'; 489*5f891b7eSNickeau $externalResources[] = PluginUtility::getResourceBaseUrl() . '/firebug/firebug-lite.css'; 490*5f891b7eSNickeau $externalResources[] = PluginUtility::getResourceBaseUrl() . '/firebug/firebug-lite-1.2.js'; 491*5f891b7eSNickeau } 492*5f891b7eSNickeau 493*5f891b7eSNickeau // The below code is to prevent this JsFiddle bug: https://github.com/jsfiddle/jsfiddle-issues/issues/726 494*5f891b7eSNickeau // The order of the resources is not guaranteed 495*5f891b7eSNickeau // We pass then the resources only if their is one resources 496*5f891b7eSNickeau // Otherwise we pass them as a script element in the HTML. 497*5f891b7eSNickeau if (count($externalResources) <= 1) { 498*5f891b7eSNickeau $externalResourcesInput = '<input type="hidden" name="resources" value="' . implode(",", $externalResources) . '">'; 499*5f891b7eSNickeau } else { 500*5f891b7eSNickeau $codes['html'] .= "\n\n\n\n\n<!-- The resources -->\n"; 501*5f891b7eSNickeau $codes['html'] .= "<!-- They have been added here because their order is not guarantee through the API. -->\n"; 502*5f891b7eSNickeau $codes['html'] .= "<!-- See: https://github.com/jsfiddle/jsfiddle-issues/issues/726 -->\n"; 503*5f891b7eSNickeau foreach ($externalResources as $externalResource) { 504*5f891b7eSNickeau if ($externalResource != "") { 505*5f891b7eSNickeau $extension = pathinfo($externalResource)['extension']; 506*5f891b7eSNickeau switch ($extension) { 507*5f891b7eSNickeau case "css": 508*5f891b7eSNickeau $codes['html'] .= "<link href=\"" . $externalResource . "\" rel=\"stylesheet\">\n"; 509*5f891b7eSNickeau break; 510*5f891b7eSNickeau case "js": 511*5f891b7eSNickeau $codes['html'] .= "<script src=\"" . $externalResource . "\"></script>\n"; 512*5f891b7eSNickeau break; 513*5f891b7eSNickeau default: 514*5f891b7eSNickeau $codes['html'] .= "<!-- " . $externalResource . " -->\n"; 515*5f891b7eSNickeau } 516*5f891b7eSNickeau } 517*5f891b7eSNickeau } 518*5f891b7eSNickeau } 519*5f891b7eSNickeau 520*5f891b7eSNickeau $jsCode = $codes['javascript']; 521*5f891b7eSNickeau $jsPanel = 0; // language for the js specific panel (0 = JavaScript) 522*5f891b7eSNickeau if (array_key_exists('babel', $codes)) { 523*5f891b7eSNickeau $jsCode = $codes['babel']; 524*5f891b7eSNickeau $jsPanel = 3; // 3 = Babel 525*5f891b7eSNickeau } 526*5f891b7eSNickeau 527*5f891b7eSNickeau // Title and description 528*5f891b7eSNickeau global $ID; 529*5f891b7eSNickeau $title = $attributes['name']; 530*5f891b7eSNickeau $pageTitle = tpl_pagetitle($ID, true); 531*5f891b7eSNickeau if (!$title) { 532*5f891b7eSNickeau 533*5f891b7eSNickeau $title = "Code from " . $pageTitle; 534*5f891b7eSNickeau } 535*5f891b7eSNickeau $description = "Code from the page '" . $pageTitle . "' \n" . wl($ID, $absolute = true); 536*5f891b7eSNickeau return '<form method="post" action="' . $postURL . '" target="_blank">' . 537*5f891b7eSNickeau '<input type="hidden" name="title" value="' . htmlentities($title) . '">' . 538*5f891b7eSNickeau '<input type="hidden" name="description" value="' . htmlentities($description) . '">' . 539*5f891b7eSNickeau '<input type="hidden" name="css" value="' . htmlentities($codes['css']) . '">' . 540*5f891b7eSNickeau '<input type="hidden" name="html" value="' . htmlentities("<!-- The HTML -->" . $codes['html']) . '">' . 541*5f891b7eSNickeau '<input type="hidden" name="js" value="' . htmlentities($jsCode) . '">' . 542*5f891b7eSNickeau '<input type="hidden" name="panel_js" value="' . htmlentities($jsPanel) . '">' . 543*5f891b7eSNickeau '<input type="hidden" name="wrap" value="b">' . //javascript no wrap in body 544*5f891b7eSNickeau $externalResourcesInput . 545*5f891b7eSNickeau '<button>Try the code</button>' . 546*5f891b7eSNickeau '</form>'; 547*5f891b7eSNickeau 548*5f891b7eSNickeau } 549*5f891b7eSNickeau 550*5f891b7eSNickeau /** 551*5f891b7eSNickeau * @param $codes the array containing the codes 552*5f891b7eSNickeau * @param $attributes the attributes of a call (for now the externalResources) 553*5f891b7eSNickeau * @return string the HTML form code 554*5f891b7eSNickeau */ 555*5f891b7eSNickeau public function addCodePenButton($codes, $attributes) 556*5f891b7eSNickeau { 557*5f891b7eSNickeau // TODO 558*5f891b7eSNickeau // http://blog.codepen.io/documentation/api/prefill/ 559*5f891b7eSNickeau } 560*5f891b7eSNickeau 561*5f891b7eSNickeau 562*5f891b7eSNickeau} 563