1<?php
2/**
3 * Plugin calc : petite calculatrice.
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Etienne Mauvais <emauvaisfr@yahoo.fr>
7 */
8
9// based on http://wiki.splitbrain.org/plugin:tutorial
10
11// must be run within Dokuwiki
12if (!defined('DOKU_INC')) die();
13
14if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/');
15require_once(DOKU_PLUGIN . 'syntax.php');
16
17/**
18 * All DokuWiki plugins to extend the parser/rendering mechanism
19 * need to inherit from this class
20 */
21class syntax_plugin_calc extends DokuWiki_Syntax_Plugin {
22    function getInfo() {
23        return array(
24        'author'  => 'Etienne Mauvais',
25        'email'   => 'emauvaisfr@yahoo.fr',
26        'date'    => @file_get_contents(DOKU_PLUGIN.'calc/VERSION'),
27        'name'    => 'Calc Plugin',
28        'desc'    => $this->getLang('calc_description'),
29        'url'     => 'http://www.dokuwiki.org/plugin:calc'
30        );
31    }
32
33    function connectTo($mode) {
34        $this->Lexer->addSpecialPattern('calc:.*?=', $mode, 'plugin_calc');
35    }
36
37    //function getType() { return 'substition'; }
38    function getType() { return 'disabled'; }
39
40    function getSort() { return 667; }
41
42    function handle($match, $state, $pos, &$handler) {
43        return array($match, $state, $pos);
44    }
45
46    function render($mode, &$renderer, $data) {
47        if ($mode == 'xhtml') {
48	    //On recupere l'expression
49	    $calc=split("calc:",$data[0]);
50	    if (isset($calc) && isset($calc[1])) $calc=$calc[1];
51
52	    //On enleve le "=" final
53	    if ($calc[strlen($calc)-1]=='=') $calc[strlen($calc)-1]=" ";
54	    $calc=rtrim($calc);
55
56	    //Si on a un ">" final on l'enleve et on affichera l'expression avant le resultat
57	    if ($calc[strlen($calc)-1]=='>') {
58	      $calc[strlen($calc)-1]=" ";
59	      $affiche=true;
60	    }
61	    $calc=rtrim($calc);
62
63	    //On retire les fonctions interdites
64            $encore=1;
65            while($encore) {
66  	      $encore=0;
67	      $calc=preg_replace_callback("/([a-z].+?\(.*?\))/",
68	                                  create_function('$f',
69					                  'GLOBAL $encore;
70							   $autorisees=Array("abs", "acos", "acosh", "asin", "asinh", "atan", "atan2", "atanh",
71							                     "base_convert", "bindec",
72							                     "ceil", "cos", "cosh",
73							                     "decbin", "dechex", "decoct", "deg2rad",
74							                     "exp", "expm1",
75							                     "floor", "fmod",
76							                     "getrandmax",
77							                     "hexdec", "hypot",
78							                     "is_finite", "is_infinite", "is_nan",
79							                     "lcg_value", "log", "log10", "log1p",
80							                     "max", "min", "mt_getrandmax", "mt_rand", "mt_srand",
81							                     "octdec",
82							                     "pi", "pow",
83							                     "rad2deg", "rand", "round",
84							                     "sin", "sinh", "sqrt", "srand",
85							                     "tan", "tanh");
86
87							   //print "  Trouve : ".$f[1];
88
89							   $nomF=explode("(",$f[1]);
90							   $nomF=$nomF[0];
91
92							   if(in_array($nomF, $autorisees)) {
93							     //print " -> autorisee\n";
94							     return $f[1];
95							   }
96							   else {
97							     //print " -> interdite !!!\n";
98							     $encore=1;
99							     return "";
100							   }'
101					                  ),
102					  $calc);
103	    }
104	    //On retire les ";" et les "$"
105	    $calc=str_replace(";","",$calc);
106            $calc=str_replace("\$","",$calc);
107
108            //On affiche un commentaire dans le source de la page
109            $renderer->doc .= "\n<!-- Calc : $calc -->\n";
110	    $calc_propre=$calc;
111	    $calc="\$n=$calc;";
112
113	    //On passe en mode track_errors (apres avoir recupere l'ancien mode)
114	    $track=ini_get('track_errors');
115	    ini_set('track_errors', 'true');
116	    $php_errormsg="";
117	    try {
118	      eval($calc);
119	    }
120	    catch (Exception $e) {
121	      $renderer->doc .= "\"<b>".$this->getLang('calc_erreur')."</b> ".$e->getMessage()."\"";
122	      return true;
123	    }
124	    //On repasse dans l'ancien mode track_error
125	    ini_set('track_error', $track);
126
127            //S'il y a eu une erreur (non catchee par try)
128            if ($php_errormsg) {
129	      $renderer->doc .= "\"<b>".$this->getLang('calc_erreur')."</b> ".$php_errormsg."\"";
130	      return true;
131	    }
132
133            //Si la valeur retournee n'est pas un nombre (ex : sqrt(-1)
134            if (is_nan($n)) {
135	      $renderer->doc .= "\"".$this->getLang('calc_valeurincorrecte')."\"";
136	      return true;
137	    }
138
139            //Mise en forme du resultat
140	    //Si on a obtenu un nombre (et pas une chaine, suite a base_convert, par exemple)
141	    if ($n*1===$n) {
142              $tmp=explode('.',$n);
143	      $out=number_format($tmp[0], 0, $this->getLang('calc_sepdec'), $this->getLang('calc_sepmil'));
144	      if (isset($tmp[1])) $out.= $this->getLang('calc_sepdec').$tmp[1];
145	    }
146	    else $out=$n;
147
148	    $out=preg_replace("/ /","&nbsp;", $out);
149
150            //Si on est en mode affichage, on affiche d'abord l'expression avant le resultat
151            if ($affiche) $renderer->doc .= $calc_propre." = ";
152	    $renderer->doc .= $out;
153
154            return true;
155        }
156        return false;
157    }
158}
159?>
160