====== Extended Table Syntax 2 Plugin ======
---- plugin ----
description: Another Mediawiki style tables inside DokuWiki
author : disorder Chang
email : disorder.chang@gmail.com
type : Syntax
lastupdate : 2007-11-06
compatible : 2007-06-26b
depends :
conflicts :
similar : exttab1
tags : Mediawiki, tables
securityissue: XSS vulnerability allows arbitrary JavaScript insertion. Author informed on 2008-02-07.
----
===== About =====
I like the [[plugin:exttab1|exttab1]] plugin, which can handle Wikimedia way of table syntax. But exttab1 does not parse some dokuwiki syntax correctly, eg. footnote, image link, etc. So I decided to write a new one.
This plugin is still in its early stages, please feel free to modify the code.
===== Installation =====
* create the folder //lib/plugins/exttab2 //
* and copy the source code to //lib/plugins/exttab2/syntax.php//
===== Usage =====
Unlike exttab1 plugin, you don't need to put any markup to enclose the syntax, just draw table as [[http://www.mediawiki.org/wiki/Help:Tables|mediawiki]] do.
==== markup summary ====
%%*%% means that the markup must be on a new line
|* |%%{|%% | start table |
|* |%%{| para %% | start table with parameters|
|* |%%|+ caption%% | table caption; only one per table and between table start and first row((caption appears in other place will be echo out directly)) |
|* |%%|+ para | caption %% | table caption with parameters |
|* |%%|-%% | table row |
|* |%%|- para%% | table row with parameters |
|* |%%! header%% | table header cell |
|* |%%! para | header%% | table header cell with parameters |
| |%%!! header %% | consecutive table headers |
| |%%!! para | header %% | consecutive table headers with parameters |
|* |%%| cell%% | table data cell; Cell content may follow on same line or on following lines |
|* |%%| para | cell%% | table data cell with parameters |
| |%%|| cell %% | consecutive table data cell |
| |%%|| para | cell %% | consecutive table data cell with parameters |
|* |%%|}%% | end table; please add an additional empty line to end the whole table((no need for nested table)) |
please see [[http://meta.wikimedia.org/wiki/Help:Table]] for more detail.
====Examples====
===Simple table===
{|
|+ caption
! header 1
! header 2
|-
| cell A
|
cell B
|}
{|
|+ caption
! header 1 !! header 2
|-
| cell A || cell B
|}
=== Table with parameter and wiki markups (formatting, linking, etc.)===
{| border="1" style="width:300px"
|+ style="color:red"| //caption//
|- style="background:green;height:50px"
! style="color:white" |header 1
! header 2 !! style="color:yellow" | header 3
|-
| style="text-align:right" | **bold**, //italic//, ((footnote))
| [[link|a link]] || style="color:yellow" | {{pdf.pdf}}
|-
| text with \\ new line OK\\ [[http://www.google.com|google]] good\\
| colspan="2" | but ====Headline==== not working!
|}
=== Table with complex wiki markups (listblock, hr, preformatted, code, etc.) ===
{| border="1"
|-
|
* add an additional
* blank line
* after the last list
| as
many lines
as you like
but don't add any empty line
|-
|
add an additional blank line
after the preformatted text
| hr fine
----
|-
|
> quoting must
>> add a blank line too
|
file or code
goes
here
|}
=== Nested tables ===
{| border="1"
| you
| can use
{| border="2" style="background:#CCC;"
| nested
|-
| table
|}
now
| .
|}
===== Limits =====
I use the source of [[http://meta.wikimedia.org/wiki/Help:Table]] to test this plugin. It works fine, except:
====Column headings with double bar====
The following syntax will render heading 2 as normal cell
! Column heading 1 || Column heading 2
=====Revision History=====
* 2006-11-06 0.2.0 support nested table and many more...
* 2006-10-04 0.1.0 Initial release
=====Sources=====
* @date 2007-10-04
*/
// must be run within Dokuwiki
if(!defined('DOKU_INC')) die();
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'syntax.php');
/**
* All DokuWiki plugins to extend the parser/rendering mechanism
* need to inherit from this class
*/
class syntax_plugin_exttab2 extends DokuWiki_Syntax_Plugin {
var $stack = array();
function syntax_plugin_exttab2(){
define("EXTTAB2_TABLE", 0);
define("EXTTAB2_CAPTION", 1);
define("EXTTAB2_TR", 2);
define("EXTTAB2_TD", 3);
define("EXTTAB2_TH", 4);
$this->tagsmap = array(
EXTTAB2_TABLE=> array("table", "", "\n" ),
EXTTAB2_CAPTION=> array("caption", "\t", "\n" ),
EXTTAB2_TR=> array("tr", "\t", "\n" ),
EXTTAB2_TD=> array("td", "\t"."\t", "\n" ),
EXTTAB2_TH=> array("th", "\t"."\t", "\n" ),
/* // DOKU constant not work when preview
EXTTAB2_TABLE=> array("table", "", DOKU_LF ),
EXTTAB2_CAPTION=> array("caption", DOKU_TAB, DOKU_LF ),
EXTTAB2_TR=> array("tr", DOKU_TAB, DOKU_LF ),
EXTTAB2_TD=> array("td", DOKU_TAB.DOKU_TAB, DOKU_LF ),
EXTTAB2_TH=> array("th", DOKU_TAB.DOKU_TAB, DOKU_LF ),
*/ );
}
function getInfo(){
return array(
'author' => 'Disorder Chang',
'email' => 'disorder.chang@gmail.com',
'date' => '2007-11-06',
'name' => 'exttab2 Plugin',
'desc' => 'parses MediaWiki-like tables',
'url' => 'http://www.dokuwiki.org/plugin:exttab2',
);
}
function getType(){
return 'container';
}
function getPType(){
return 'block';
}
function getAllowedTypes() {
return array('container', 'formatting', 'substition', 'disabled', 'protected');
}
function getSort(){
return 50;
}
function connectTo($mode) {
$this->Lexer->addEntryPattern('\n\{\|[^\n]*',$mode,'plugin_exttab2');
}
function postConnect() {
$para = "[^\|\n\[\{\!]+"; // parametes
// caption: |+ params | caption
$this->Lexer->addPattern("\n\|\+(?:$para\|(?!\|))?",'plugin_exttab2');
// row: |- params
$this->Lexer->addPattern('\n\|\-[^\n]*','plugin_exttab2');
// table open
$this->Lexer->addPattern('\n\{\|[^\n]*','plugin_exttab2');
// table close
$this->Lexer->addPattern('\n\|\}','plugin_exttab2');
// header
$this->Lexer->addPattern("(?:\n|\!)\!(?:$para\|(?!\|))?",'plugin_exttab2');
//cell
$this->Lexer->addPattern("(?:\n|\|)\|(?:$para\|(?!\|))?",'plugin_exttab2');
//end
// $this->Lexer->addExitPattern('\n','plugin_exttab2');
// $this->Lexer->addExitPattern("(?Lexer->addExitPattern("\n(?=\n)",'plugin_exttab2');
}
/**
* Handle the match
*/
function handle($match, $state, $pos, &$handler){
if($state == DOKU_LEXER_EXIT) {
return array($state, "end");
}
else if($state == DOKU_LEXER_UNMATCHED){
return array($state, "", $match);
}
else{
$para = "[^\|\n]+"; // parametes
if(preg_match ( '/\{\|([^\n]*)/', $match, $m)){ // table open
$func = "table_open";
$params = $m[1];
return array($state, $func, $params);
}
else if($match == "\n|}"){ // table close
$func = "table_close";
$params = "";
return array($state, $func, $params);
}
else if(preg_match ("/^\n\|\+(?:(?:($para)\|)?)$/", $match, $m)){ // caption
$func = "caption";
$params = $m[1];
return array($state, $func, $params);
}
else if(preg_match ( '/\|-([^\n]*)/', $match, $m)){ // row
$func = "row";
$params = $m[1];
return array($state, $func, $params);
}
else if(preg_match("/^(?:\n|\!)\!(?:(?:([^\|\n\!]+)\|)?)$/", $match, $m)){ // header
$func = "header";
$params = $m[1];
return array($state, $func, $params);
}
else if(preg_match("/^(?:\n|\|)\|(?:(?:($para)\|)?)$/", $match, $m)){ // cell
$func = "cell";
$params = $m[1];
return array($state, $func, $params);
}
else{
die("what? ".$match); // for debugging
}
}
}
/**
* Create output
*/
function render($mode, &$renderer, $data) {
if($mode == 'xhtml'){
list($state, $func, $params) = $data;
switch ($state) {
case DOKU_LEXER_UNMATCHED :
$r = $renderer->_xmlEntities($params);
$renderer->doc .= $r;
break;
case DOKU_LEXER_ENTER :
case DOKU_LEXER_MATCHED:
$r = $this->$func($params);
$renderer->doc .= $r;
break;
case DOKU_LEXER_EXIT :
$r = $this->$func($params);
$renderer->doc .= $r;
break;
}
return true;
}
return false;
}
function _attrString($attr="", $before=" "){
if(is_null($attr) || trim($attr)=="") $attr = "";
else $attr = $before.trim($attr);
return $attr;
}
var $tagsmap = array();
function _starttag($tag, $params=NULL, $before="", $after=""){
$tagstr = $this->tagsmap[$tag][0];
$before = $this->tagsmap[$tag][1].$before;
$after = $this->tagsmap[$tag][2].$after;
$r = $before."<".$tagstr.$this->_attrString($params).">". $after;
return $r;
}
function _endtag($tag, $before="", $after=""){
$tagstr = $this->tagsmap[$tag][0];
$before = $this->tagsmap[$tag][1].$before;
$after = $this->tagsmap[$tag][2].$after;
$r = $before."".$tagstr.">". $after;
return $r;
}
function table_open($params=NULL){
$r .= $this->_closetags(EXTTAB2_TABLE);
$r .= $this->_starttag(EXTTAB2_TABLE, $params);
$this->stack[] = EXTTAB2_TABLE;
return $r;
}
function table_close($params=NULL){
$t = end($this->stack);
switch($t){
case EXTTAB2_TABLE:
array_push($this->stack, EXTTAB2_TR, EXTTAB2_TD);
$r .= $this->_starttag(EXTTAB2_TR, $params);
$r .= $this->_starttag(EXTTAB2_TD, $params);
break;
case EXTTAB2_CAPTION:
$r .= $this->_endtag(EXTTAB2_CAPTION);
array_pop($this->stack);
array_push($this->stack, EXTTAB2_TR, EXTTAB2_TD);
$r .= $this->_starttag(EXTTAB2_TR, $params);
$r .= $this->_starttag(EXTTAB2_TD, $params);
break;
case EXTTAB2_TR:
array_push($this->stack, EXTTAB2_TD);
$r = $this->_starttag(EXTTAB2_TD, $params);
break;
case EXTTAB2_TD:
case EXTTAB2_TH:
break;
}
while(($t = end($this->stack)) != EXTTAB2_TABLE){
$r .= $this->_endtag($t);
array_pop($this->stack);
}
array_pop($this->stack);
$r .= $this->_endtag(EXTTAB2_TABLE);
return $r;
}
function end($params=NULL){
while(!empty($this->stack)){
$r .= $this->table_close();
}
return $r;
}
function caption($params=NULL){
if(($r = $this->_closetags(EXTTAB2_CAPTION)) === FALSE){
return "";
}
$r .= $this->_starttag(EXTTAB2_CAPTION, $params);
$this->stack[] = EXTTAB2_CAPTION;
return $r;
}
function row($params=NULL){
$r .= $this->_closetags(EXTTAB2_TR);
$r .= $this->_starttag(EXTTAB2_TR, $params);
$this->stack[] = EXTTAB2_TR;
return $r;
}
function header($params=NULL){
$r .= $this->_closetags(EXTTAB2_TH);
$r .= $this->_starttag(EXTTAB2_TH, $params);
$this->stack[] = EXTTAB2_TH;
return $r;
}
function cell($params=NULL){
$r .= $this->_closetags(EXTTAB2_TD);
$r .= $this->_starttag(EXTTAB2_TD, $params);
$this->stack[] = EXTTAB2_TD;
return $r;
}
function _closetags($tag){
$r = "";
switch($tag){
case EXTTAB2_TD:
case EXTTAB2_TH:
$t = end($this->stack);
switch($t){
case EXTTAB2_TABLE:
array_push($this->stack, EXTTAB2_TR);
$r .= $this->_starttag(EXTTAB2_TR, $params);
break;
case EXTTAB2_CAPTION:
$r .= $this->_endtag(EXTTAB2_CAPTION);
array_pop($this->stack);
array_push($this->stack, EXTTAB2_TR);
$r .= $this->_starttag(EXTTAB2_TR, $params);
break;
case EXTTAB2_TR:
break;
case EXTTAB2_TD:
case EXTTAB2_TH:
$r .= $this->_endtag($t);
array_pop($this->stack);
break;
}
break;
case EXTTAB2_TR:
$t = end($this->stack);
switch($t){
case EXTTAB2_TABLE:
break;
case EXTTAB2_CAPTION:
$r .= $this->_endtag(EXTTAB2_CAPTION);
array_pop($this->stack);
break;
case EXTTAB2_TR:
$r .= $this->_starttag(EXTTAB2_TD);
$r .= $this->_endtag(EXTTAB2_TD);
$r .= $this->_endtag(EXTTAB2_TR);
array_pop($this->stack);
break;
case EXTTAB2_TD:
case EXTTAB2_TH:
$r .= $this->_endtag($t);
$r .= $this->_endtag(EXTTAB2_TR);
array_pop($this->stack);
array_pop($this->stack);
break;
}
break;
case EXTTAB2_TABLE:
$t = end($this->stack);
if($t === FALSE) break;
switch($t){
case EXTTAB2_TABLE:
array_push($this->stack, EXTTAB2_TR, EXTTAB2_TD);
$r .= $this->_starttag(EXTTAB2_TR, $params);
$r .= $this->_starttag(EXTTAB2_TD, $params);
break;
case EXTTAB2_CAPTION:
$r .= $this->_endtag(EXTTAB2_CAPTION);
array_pop($this->stack);
array_push($this->stack, EXTTAB2_TR, EXTTAB2_TD);
$r .= $this->_starttag(EXTTAB2_TR, $params);
$r .= $this->_starttag(EXTTAB2_TD, $params);
break;
case EXTTAB2_TR:
array_push($this->stack, EXTTAB2_TD);
$r = $this->_starttag(EXTTAB2_TD, $params);
break;
case EXTTAB2_TD:
case EXTTAB2_TH:
break;
}
break;
case EXTTAB2_CAPTION:
$t = end($this->stack);
if($t==EXTTAB2_TABLE){
}
else{
return false ; // ignore this, or should echo error?
}
break;
}
return $r;
}
}
?>
===== Discussion =====
Yup this is an awesome plug in I love it. One question. I tried using the [[http://meta.wikimedia.org/wiki/Help:Sorting |sorting feature]] but was unable to. Any hopes of getting this to work? --lenehey [Nov. 27, 2007]
Another question. Any hopes of doing Ettab3 where Ettab3 is Ettab2 + sorting table and a finished plugin รก non-programmer can use ?. Philip Hettel [feb. 17, 2008]
Found a bug which prevents you from coloring a cell anything brighter than #999999. For some reason if you include a letter such as "#AA88BB" in the hex-color, it won't parse.