* @author Emmanuel Hidalgo * @author Benoit Moreau * * Based on the dokuwiki-plugin-patchpanel plugin (https://github.com/grantemsley/dokuwiki-plugin-patchpanel) by Grant Emsley */ // must be run within Dokuwiki if(!defined('DOKU_INC')) die(); /** * All DokuWiki plugins to extend the parser/rendering mechanism * need to inherit from this class */ class syntax_plugin_switchpanel extends DokuWiki_Syntax_Plugin { private $_sName = "switchpanel"; private $_oTagsContent = array( 'line'=>array( 'number', 'color', 'case', 'labelLeft', 'colorLabelLeft', 'labelRight', 'colorLabelRight', 'labelBgColor', 'labelTxtColor', 'leftLedColor', 'rightLedColor' ), 'text'=>array( 'bgColor', 'color', 'size', 'brColor', 'brRadius' ), 'heightBar'=>array( 'height' ) ); private $_oTagsItemsContent = array( 'line_items'=>array( 'color', 'text', 'link', 'case', 'target', 'textlink' , 'labelBgColor', 'labelTxtColor', 'leftLedColor', 'rightLedColor' ) ); function getType(){ return 'substition'; } function getSort(){ return 155; } function getPType(){ return 'block'; } function connectTo($mode){ $this->Lexer->addSpecialPattern( "]*>.*?(?:<\/switchpanel>)", $mode, 'plugin_switchpanel' ); } /** * Handle the match * * @param string $match The text matched by the patterns * @param int $state The lexer state for the match * @param int $pos The character position of the matched text * @param Doku_Handler $handler The Doku_Handler object * @return array Return an array with all data you want to use in render */ function handle($match, $state, $pos, Doku_Handler $handler){ // remove "" from the match $match = trim( substr( $match, 0, ( strlen( $this->_sName ) + 3 ) * -1 ) ); // default options $opt = array( 'logo'=>DOKU_BASE.'lib/plugins/switchpanel/images/greenIt.svg', 'logoLink'=>'http://www.greenitsolutions.fr/', 'labelBgColor'=>'#fff', 'labelTxtColor'=>'#000', 'leftLedColor'=>'#666666', 'rightLedColor'=>'#666666', 'target'=>'_blank', 'showEars'=>true, 'labelLeft'=>'', 'labelRight'=>'', 'colorLabelLeft'=>'#fff', 'colorLabelRight'=>'#fff', 'case'=>'rj45', 'group'=>0, 'groupSeparatorWidth'=>18, 'color'=>'#ccc', 'elementWidth'=>36, 'elementHeight'=>45, 'elementSeparatorWidth'=>5, 'elementSeparatorHeight'=>5, 'textSize'=>20, 'textColor'=>'#fff', 'textBgColor'=>'', 'textBrColor'=>'', 'textBrRadius'=>'', 'barHeight'=>5, 'screwHeightSpace'=>60, 'screwHeight'=>15, 'screwWidth'=>20, 'screwColor'=>'#fff', 'switchColor'=>'#808080', 'content'=>array() ); // recovered the first line and content $iPosFirstLine = stripos( $match, "\n" ); $sFirstLines = substr( $match, 0, $iPosFirstLine ); $sContent = trim( substr( $match, $iPosFirstLine ) ); unset( $match ); // treatment of first-line $sFirstLines = trim( substr( $sFirstLines, strlen( $this->_sName ) + 1 ) ); $sFirstLines = trim( rtrim( $sFirstLines, '>' ) ); $oAttributs = explode( ' ', $sFirstLines ); foreach($oAttributs as $sKeyVal ){ if( trim( $sKeyVal ) == '' || stripos( $sKeyVal, '=' ) === false ){ continue; } list( $sKey, $sVal ) = explode( '=', $sKeyVal ); $sVal = trim( $sVal, '"' ); if( $sKey == 'content' || !array_key_exists( $sKey, $opt ) ){ continue; } // change a default value if( is_bool( $opt[ $sKey ] ) ){ $opt[ $sKey ] = ( strtolower( $sVal ) == 'true' ); }else if( is_int( $opt[ $sKey ] ) ){ $opt[ $sKey ] = intval( $sVal ); }else{ $opt[ $sKey ] = $sVal; } } // anonymous function recovery options $fGetOptions = function( $sOptions, $oFilters = NULL ){ $oOptions = array(); do{ $sOptions = trim( $sOptions, ',' ); if( $sOptions == '' ){ break; } $iPosStop = stripos( $sOptions, '=' ); if( $sOptions === false ){ break; } $sKey = trim( substr( $sOptions, 0, $iPosStop ) ); $sOptions = trim( substr( $sOptions, $iPosStop + 1 ) ); $iPosStop = stripos( $sOptions, ',' ); if( $iPosStop === false ){ $iPosStop = strlen( $sOptions ); } $sValue = trim( substr( $sOptions, 0, $iPosStop ) ); if( substr( $sOptions, 0, 1 ) == '"' ){ $iPosStop = stripos( $sOptions, '"', 1 ); $sValue = trim( substr( $sOptions, 0, $iPosStop ), '"' ); $iPosStop++; } // control of coherence options if( !is_null( $oFilters ) && !in_array( $sKey, $oFilters ) ){ // error, the option is not found echo 'Syntax error : the option is not found :
 key : '.$sKey.', value : '.$sOptions."
\n"; $sOptions = trim( substr( $sOptions, $iPosStop ) ); continue; } // recording option $oOptions[ $sKey ] = $sValue; $sOptions = trim( substr( $sOptions, $iPosStop ) ); }while( true ); return $oOptions; }; // analysis and processing of content $oContent = array(); $oLines = explode( "\n", $sContent ); $sContext = ''; foreach( $oLines as $sLine ){ // recovery of the line $sLine = trim( $sLine ); if( $sLine == '' || substr( $sLine, 0, 1 ) == '#' ){ continue; } // determine if the context has to be taken into account if( strlen( $sLine ) > 2 && substr( $sLine, 0, 2 ) == '==' ){ // recovered and the control context $sContext = trim( substr( $sLine, 2 ) ); $iPosSep = stripos( $sLine, ':' ); if( $iPosSep !== false ){ $sContext = trim( substr( $sLine, 2, $iPosSep - 2 ) ); } if( !array_key_exists( $sContext, $this->_oTagsContent ) ){ // error, the context was not found echo 'Syntax error : the context was not found :
 context : '.$sContext.', line : '.$sLine."
\n"; continue; } // if there are options $oOptions = array(); if( $iPosSep !== false ){ $sOptions = substr( $sLine, $iPosSep + 1 ); $oOptions = $fGetOptions( $sOptions, $this->_oTagsContent[ $sContext ] ); } // adding the new context $oContent[] = array( 'type'=>$sContext, 'options'=>$oOptions, 'data'=>NULL ); continue; } // recovery of the element $oElement = &$oContent[ count( $oContent ) - 1 ]; // if the line contains options $oOptions = NULL; $iPosSep = stripos( $sLine, ':' ); if( $iPosSep !== false && array_key_exists( $oElement[ 'type' ].'_items', $this->_oTagsItemsContent ) ){ $sOptions = trim( trim( substr( $sLine, $iPosSep ), ':' ) ); $sLine = substr( $sLine, 0, $iPosSep ); $oOptions = $fGetOptions( $sOptions, $this->_oTagsItemsContent[ $oElement[ 'type' ].'_items' ] ); } // get last context if( $oElement[ 'type' ] == 'line' ){ if( $oElement[ 'data' ] == NULL ){ $oElement[ 'data' ] = array(); } $oInfos = explode( ',', $sLine ); $oLine = array( 'number'=>$oInfos[ 0 ] ); if( count( $oInfos ) > 1 ){ $oLine[ 'label' ] = $oInfos[ 1 ]; } if( count( $oInfos ) > 2 ){ $oLine[ 'title' ] = $oInfos[ 2 ]; } // propagation properties $oLine[ 'options' ] = array(); foreach( array( 'color', 'case', 'labelLeft', 'colorLabelLeft', 'labelRight', 'colorLabelRight', 'labelBgColor', 'labelTxtColor', 'rightLedColor', 'leftLedColor' ) as $sProp ){ if( !isset( $oElement[ 'options' ][ $sProp ] ) ){ $oElement[ 'options' ][ $sProp ] = $opt[ $sProp ]; $oLine[ 'options' ][ $sProp ] = $oElement[ 'options' ][ $sProp ]; } $oLine[ 'options' ][ $sProp ] = $oElement[ 'options' ][ $sProp ]; } if( !is_null( $oOptions ) ){ foreach( $oOptions as $sKey=>$oValue ){ $oLine[ 'options' ][ $sKey ] = $oValue; } } $oElement[ 'data' ][ intval( $oInfos[ 0 ] ) ] = $oLine; }else if( $oElement[ 'type' ] == 'text' ){ $oElement[ 'data' ] .= $sLine; } } // update content $opt[ 'content' ] = $oContent; return $opt; } /* * Create output */ function render($mode, Doku_Renderer $renderer, $opt) { if( $mode == 'metadata' ){ return false; } // determines the maximum number of elements in width & // determine the position of the minimum and maximum index $iNbrElementsWidth = 0; $oElements = $opt[ 'content' ]; foreach( $oElements as &$oElement ){ if( $oElement[ 'type' ] != 'line' ){ continue; } $MinIndex = 1000; $iMaxIndex = 0; if( isset( $oElement[ 'data' ] ) ){ foreach( $oElement[ 'data' ] as $iLine=>$oLine ){ if( $MinIndex > $iLine ){ $MinIndex = $iLine; } if( $iMaxIndex < $iLine ){ $iMaxIndex = $iLine; } } } $iDiff = ( $iMaxIndex - $MinIndex ) + 1; if( $iNbrElementsWidth < $iDiff ){ $iNbrElementsWidth = $iDiff; } if( isset( $oElement[ 'options' ][ 'number' ] ) && $oElement[ 'options' ][ 'number' ] > $iNbrElementsWidth ){ $iNbrElementsWidth = $oElement[ 'options' ][ 'number' ]; } // re-index elements if( isset( $oElement[ 'data' ] ) ){ ksort( $oElement[ 'data' ] ); $oTmpData = array(); for( $i=$MinIndex; $i<=$iMaxIndex; $i++ ){ $oTmpData[ $i ] = isset( $oElement[ 'data' ][ $i ] ) ? $oElement[ 'data' ][ $i ] : array( 'number'=>$i, 'label'=>'', 'options'=>array( 'color'=>$oElement[ 'options' ][ 'color' ], 'case'=>$oElement[ 'options' ][ 'case' ], 'labelLeft'=>$oElement[ 'options' ][ 'labelLeft' ], 'colorLabelLeft'=>$oElement[ 'options' ][ 'colorLabelLeft' ], 'labelRight'=>$oElement[ 'options' ][ 'labelRight' ], 'colorLabelRight'=>$oElement[ 'options' ][ 'colorLabelRight' ], 'labelBgColor'=>$oElement[ 'options' ][ 'labelBgColor' ], 'labelTxTColor'=>$oElement[ 'options' ][ 'labelTxtColor' ], 'leftLedColor'=>$oElement[ 'options' ][ 'leftLedColor' ], 'rightLedColor'=>$oElement[ 'options' ][ 'rightLedColor' ] ) ); } $oData = array(); foreach( $oTmpData as $oLine ){ $oData[ count( $oData ) ] = $oLine; } $oElement[ 'data' ] = $oData; } } // if there are groups $iWidthGroup = 0; if( $opt[ 'group' ] > 0 ){ $iWidthGroup = floor( $iNbrElementsWidth / $opt[ 'group' ] ) * $opt[ 'groupSeparatorWidth' ]; if( $iNbrElementsWidth % $opt[ 'group' ] == 0 ){ $iWidthGroup -= $opt[ 'groupSeparatorWidth' ]; } } // calculates the width $iGroup = $opt[ 'group' ]; $iWidthSvg = $iWidthGroup + ( $opt[ 'showEars' ] ? ( $opt[ 'elementWidth' ] * 4 ) : ( $opt[ 'elementSeparatorWidth' ] * 2 ) ) + // if show Ears ( $iNbrElementsWidth * $opt[ 'elementWidth' ] ) + ( $iNbrElementsWidth > 1 ? ( ( $iNbrElementsWidth - 1 ) * $opt[ 'elementSeparatorWidth' ] ) : 0 ); // calculates the height $iHeightSvg = 0; foreach( $oElements as &$oElement ){ $iHeightSvg += $opt[ 'elementSeparatorHeight' ]; if( $oElement[ 'type' ] == 'line' ){ $iHeightSvg += $opt[ 'elementHeight' ]; }else if( $oElement[ 'type' ] == 'text' ){ if( isset( $oElement[ 'options' ][ 'size' ] ) ){ $iHeightSvg += $oElement[ 'options' ][ 'size' ]; }else{ $iHeightSvg += $opt[ 'textSize' ]; } }else if( $oElement[ 'type' ] == 'heightBar' ){ $iBarHeight = $opt[ 'barHeight' ]; if( isset( $oElement[ 'options' ][ 'height' ] ) ){ $iBarHeight = $oElement[ 'options' ][ 'height' ]; } $iHeightSvg += $iBarHeight; } } // the last element if( count( $oElements ) > 0 ){ $iHeightSvg += $opt[ 'elementSeparatorHeight' ]; } $sPathTemplateClass = dirname( __FILE__ ).DIRECTORY_SEPARATOR.'tpl'.DIRECTORY_SEPARATOR;; $fDrawCase = function( $oCase, $iX, $iY ) use ( $opt, $sPathTemplateClass ){ // search the associated class $sCase = $oCase[ 'options' ][ 'case' ]; if( !file_exists( $sPathTemplateClass.'switchpanel.case.'.$sCase.'.class.php' ) ){ $sCase = $opt[ 'case' ]; } require_once( $sPathTemplateClass.'switchpanel.case.'.$sCase.'.class.php' ); $sClassName = 'switchpanel_case_'.$sCase; return $sClassName::getSvg( $oCase, $iX, $iY, $opt ); }; // construction of SVG $sSvg = ''. 'image/svg+xml'. ''; // inclusion of the logo and bolts if( $opt[ 'showEars' ] ){ require_once( $sPathTemplateClass.'switchpanel.screw.none.class.php' ); if( !in_array( $opt[ 'logo' ], array( '', 'none' ), true ) ){ if( $opt[ 'logoLink' ] != '' ){ $sSvg .= ''; } $sSvg .= ''; if( $opt[ 'logoLink' ] != '' ){ $sSvg .= ''; } } $iHeightScrew = $iHeightSvg - ( ( $opt[ 'elementSeparatorHeight' ] * 2 ) + $opt[ 'screwHeight' ] ); $iNbrScrews = floor( $iHeightScrew / $opt[ 'screwHeightSpace' ] ); if( $iNbrScrews == 0 ){ $iNbrScrews++; } $iHeightScrew = $iHeightScrew / $iNbrScrews; $iNbrScrews++; if( $iNbrScrews == 1 ){ $iNbrScrews++; $iHeightScrew = $iHeightSvg - ( ( $opt[ 'elementSeparatorHeight' ] * 2 ) + $opt[ 'screwHeight' ] ); } $iPosHeightScrew = $opt[ 'elementSeparatorHeight' ]; for( $i=1; $i<=$iNbrScrews; $i++ ){ $sSvg .= switchpanel_screw_none::getSvg( $opt[ 'elementSeparatorWidth' ], $iPosHeightScrew, $opt ). switchpanel_screw_none::getSvg( ( $iWidthSvg - $opt[ 'elementSeparatorWidth' ] - $opt[ 'screwWidth' ] ), $iPosHeightScrew, $opt ); $iPosHeightScrew += $iHeightScrew; } } // drawing of the elements $iIndexY = 0; $bFirstLine = true; foreach( $oElements as &$oElement ){ $iIndexX = $opt[ 'showEars' ] ? ( $opt[ 'elementWidth' ] * 2 ) : $opt[ 'elementSeparatorWidth' ]; $iIndexY += $opt[ 'elementSeparatorHeight' ]; if( $oElement[ 'type' ] == 'line' ){ $oCases = $oElement[ 'data' ]; for( $i=0; $i<$iNbrElementsWidth; $i++ ){ $oCase = array( 'case'=>'none' ); if( isset( $oCases[ $i ] ) ){ $oCase = $oCases[ $i ]; } $sSvg .= $fDrawCase( $oCase, $iIndexX, $iIndexY ); if( $i - 1 < $iNbrElementsWidth ){ $iIndexX += $opt[ 'elementSeparatorWidth' ]; } $iIndexX += $opt[ 'elementWidth' ]; // if there are groups if( $opt[ 'group' ] > 0 && ( $i + 1 < $iNbrElementsWidth ) && ( ( $i + 1 ) % $iGroup ) == 0 ){ $iIndexX += $opt[ 'groupSeparatorWidth' ]; } } if( $opt[ 'showEars' ] ){ if( isset( $oElement[ 'options' ][ 'labelLeft' ] ) && trim( $oElement[ 'options' ][ 'labelLeft' ] ) != '' ){ $iMoveX = 0; if( $bFirstLine && !in_array( $opt[ 'logo' ], array( '', 'none' ), true ) && $iIndexY == $opt[ 'elementSeparatorHeight' ] ){ $iMoveX = $opt[ 'elementWidth' ] - $opt[ 'elementSeparatorWidth' ]; } $sSvg .= ''.$oElement[ 'options' ][ 'labelLeft' ].''; } if( isset( $oElement[ 'options' ][ 'labelRight' ] ) && trim( $oElement[ 'options' ][ 'labelRight' ] ) != '' ){ $sSvg .= ''.$oElement[ 'options' ][ 'labelRight' ].''; } } $iIndexY += $opt[ 'elementHeight' ]; $bFirstLine = false; }else if( $oElement[ 'type' ] == 'text' ){ require_once( $sPathTemplateClass.'switchpanel.text.none.class.php' ); $sSvg .= switchpanel_text_none::getSvg( $oElement, $iIndexX, $iIndexY, $opt, $iWidthSvg ); $iHeightText = $opt[ 'textSize' ]; if( isset( $oElement[ 'options' ][ 'size' ] ) ){ $iHeightText = $oElement[ 'options' ][ 'size' ]; } $iIndexY += $iHeightText; }else if( $oElement[ 'type' ] == 'heightBar' ){ $iBarHeight = $opt[ 'barHeight' ]; if( isset( $oElement[ 'options' ][ 'height' ] ) ){ $iBarHeight = $oElement[ 'options' ][ 'height' ]; } $iIndexY += $iBarHeight; } } $sSvg .= ''; // generation rendering if ($mode != 'odt') { $renderer->doc .= '
'.$sSvg.'
'; } else { // When exporting to ODT format always make the switchpannel as wide // as the whole page without margins (but keep the width/height relation!). $widthInCm = $renderer->_getAbsWidthMindMargins(); $heightInCm = $widthInCm * ($iHeightSvg/$iWidthSvg); $renderer->_addStringAsSVGImage($sSvg, $widthInCm.'cm', $heightInCm.'cm'); } return true; } }