PluginProbe
E2Pdf – Export Pdf Tool for WordPress / 1.32.53
E2Pdf – Export Pdf Tool for WordPress v1.32.53
1.32.53 1.32.51 1.32.49 1.32.48 1.32.43 1.32.40 1.32.34 1.32.32 1.32.31 1.32.26 1.32.22 1.32.23 1.32.18 1.32.17 1.32.15 trunk 1.00.00 1.00.13 1.01.01 1.02.02 1.03.07 1.04.07 1.05.03 1.06.02 1.07.11 All 74 releases
e2pdf / vendors / svggraph / Javascript.php

Javascript.php in E2Pdf – Export Pdf Tool for WordPress 1.32.53, at vendors/svggraph/Javascript.php

691 lines 20.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Copyright (C) 2012-2023 Graham Breach
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 /**
19 * For more information, please contact <[email protected]>
20 */
21
22 namespace Goat1000\SVGGraph;
23
24 class Javascript {
25
26 private $graph;
27 protected $functions = [];
28 protected $variables = [];
29 protected $comments = [];
30 protected $init_functions = [];
31 protected $fader_enabled = false;
32 protected $clickshow_enabled = false;
33
34 private $namespace = '';
35
36 public function __construct(&$graph)
37 {
38 $this->graph =& $graph;
39
40 if($graph->getOption('namespace'))
41 $this->namespace = 'svg:';
42 }
43
44 /**
45 * Adds any number of functions by name
46 */
47 public function addFuncs()
48 {
49 $fns = func_get_args();
50 foreach($fns as $fn) {
51 if(!isset($this->functions[$fn]))
52 $this->addFunction($fn);
53 }
54 }
55
56 /**
57 * Adds a javascript function
58 */
59 public function addFunction($name, $realname = null)
60 {
61 if($realname === null)
62 $realname = $name;
63
64 if(isset($this->functions[$realname]))
65 return true;
66
67 // functions that fit on one line
68 $simple_functions = [
69 'setattr' =>
70 "function setattr(i,a,v){i.setAttributeNS(null,a,v);return v}\n",
71 'getE' =>
72 "function getE(i){return document.getElementById(i)}\n",
73 'newtext' =>
74 "function newtext(c){return document.createTextNode(c)}\n",
75 'textAttr' =>
76 "function textAttr(e,a){var s=e.getAttributeNS(null,a);return s?s:'';}\n",
77 // round to nearest whole number
78 'kround' => "function kround(v){return Math.round(v)|0;}\n",
79 // floor function
80 'kroundDown' => "function kroundDown(v){return v|0;}\n",
81 ];
82 if(isset($simple_functions[$name]))
83 return $this->insertFunction($name, $simple_functions[$name]);
84
85 // functions that only use a template and other functions
86 $template_functions = [
87 'dateFormat' => [],
88 'dateStrValueX' => ['dateFormat'],
89 'dateStrValueY' => ['dateFormat'],
90 'fading' => [],
91 'finditem' => [],
92 'fitRect' => ['setattr'],
93 'getData' => [],
94 'keyStrValueX' => [],
95 'keyStrValueY' => [],
96 'logStrValueX' => [],
97 'logStrValueY' => [],
98 'newel' => ['setattr'],
99 'strValueX' => [],
100 'strValueY' => [],
101 'svgCursorCoords' => ['svgNode'],
102 ];
103 if(isset($template_functions[$name])) {
104 foreach($template_functions[$name] as $dependency)
105 $this->addFunction($dependency);
106 return $this->insertTemplate($name);
107 }
108
109 switch($name)
110 {
111 case 'showhide' :
112 $this->addFunction('setattr');
113 $fn = "function showhide(e,h){setattr(e,'visibility',h?'visible':'hidden');}\n";
114 break;
115
116 case 'tooltip' :
117 $this->addFuncs('getE', 'setattr', 'newel', 'showhide', 'svgNode',
118 'svgCursorCoords');
119 $this->insertVariable('tooltipOn', '');
120 $opts = ['stroke_width', 'shadow_opacity', 'round', 'padding',
121 'back_colour', 'offset', 'align'];
122 $vars = [];
123 foreach($opts as $opt) {
124 $vars[$opt] = $this->graph->getOption('tooltip_' . $opt);
125 }
126
127 $round_part = '';
128 $shadow_part = '';
129 $vars['edge_space'] = $vars['stroke_width'];
130 $vars['stroke'] = $this->graph->getOption('tooltip_stroke_colour',
131 'tooltip_colour', ['@','#000']);
132 if($vars['round'] > 0) {
133 $round = new Number($vars['round'], 'px');
134 $round_part = ',rx:"' . $round . '",ry:"' . $round . '"';
135 }
136
137 if(is_numeric($vars['shadow_opacity'])) {
138 $ttoffs = 2 - $vars['stroke_width'] / 2;
139 $vars['edge_space'] += $ttoffs;
140 $ttoffs = new Number($ttoffs, 'px');
141 $shadow_part = 'shadow = newel("rect",{id:"ttshdw",fill:"#000",' .
142 'width:"10px",height:"10px",opacity:' .
143 new Number($vars['shadow_opacity']) .
144 ',x:"' . $ttoffs . '",y:"' . $ttoffs . '"';
145 if($round_part !== '')
146 $shadow_part .= $round_part;
147 $shadow_part .= '});';
148 $shadow_part .= 'tt.appendChild(shadow);';
149 }
150
151 $vars['transform_part'] = "setattr(inner, 'transform', 'translate(";
152 switch($vars['align']) {
153 case 'left' :
154 $vars['transform_part'] .= new Number($vars['padding']) . ",0)');";
155 break;
156 case 'right' :
157 $vars['transform_part'] .= "' + (bw - " . new Number($vars['padding']) . ") + ',0)');";
158 break;
159 default:
160 $vars['transform_part'] .= "' + (bw / 2) + ',0)');";
161 }
162
163 $vars['round_part'] = $round_part;
164 $vars['shadow_part'] = $shadow_part;
165 $vars['dpad'] = 2 * $vars['padding'];
166 $vars['back_colour'] = new Colour($this->graph, $vars['back_colour']);
167 $vars['stroke'] = new Colour($this->graph, $vars['stroke']);
168 return $this->insertTemplate('tooltip', $vars);
169
170 case 'texttt' :
171 $this->addFuncs('getE', 'setattr', 'newel', 'newtext');
172 $opts = ['padding', 'colour', 'font', 'font_weight', 'align'];
173 $vars = [];
174 foreach($opts as $opt)
175 $vars[$opt] = $this->graph->getOption('tooltip_' . $opt);
176 $vars['font_size'] = Number::units($this->graph->getOption('tooltip_font_size'));
177 $vars['line_spacing'] = Number::units($this->graph->getOption('tooltip_line_spacing'));
178 $vars['colour'] = new Colour($this->graph, $vars['colour'], false, false);
179 $vars['ttoffset'] = $vars['font_size'] + $vars['padding'];
180 if($vars['line_spacing'] === null || $vars['line_spacing'] < 1)
181 $vars['tty'] = $vars['ttoffset'];
182 else
183 $vars['tty'] = $vars['line_spacing'];
184
185 switch($vars['align'])
186 {
187 case 'left': $vars['anchor'] = 'start'; break;
188 case 'right': $vars['anchor'] = 'end'; break;
189 default:
190 $vars['anchor'] = 'middle';
191 }
192 return $this->insertTemplate('texttt', $vars);
193
194 case 'ttEvent' :
195 $this->addFuncs('finditem');
196 $this->addInitFunction('ttEvent');
197 return $this->insertTemplate('ttEvent');
198
199 case 'popFront' :
200 $this->addFuncs('getE', 'finditem');
201 $this->addInitFunction('popFront');
202 return $this->insertTemplate('popFront');
203
204 case 'clickShowEvent' :
205 if($this->fader_enabled)
206 return $this->fadeAndClick();
207
208 $this->addFuncs('getE', 'finditem', 'setattr');
209 $this->addInitFunction('clickShowEvent');
210 return $this->insertTemplate('clickShowEvent');
211
212 case 'fade' :
213 if($this->clickshow_enabled)
214 return $this->fadeAndClick();
215
216 $this->addFuncs('getE', 'setattr', 'textAttr');
217 $this->addInitFunction('fade');
218 return $this->insertTemplate('fade');
219
220 case 'fadeEventIn' :
221 $this->addFuncs('finditem');
222 $this->addInitFunction('fadeEventIn');
223 return $this->insertTemplate('fadeEventIn');
224
225 case 'fadeEventOut' :
226 $this->addFuncs('finditem');
227 $this->addInitFunction('fadeEventOut');
228 return $this->insertTemplate('fadeEventOut');
229
230 case 'duplicate' :
231 $this->addFuncs('getE', 'newel', 'setattr');
232 $this->addInitFunction('initDups');
233 return $this->insertTemplate('duplicate', ['namespace' => $this->namespace]);
234
235 case 'svgNode' :
236 return $this->insertTemplate('svgNode', ['namespace' => $this->namespace]);
237
238 case 'autoHide' :
239 $this->addFuncs('getE', 'setattr', 'finditem');
240 $this->addInitFunction('autoHide');
241 return $this->insertTemplate('autoHide');
242
243 case 'chEvt' :
244 $this->addInitFunction('chEvt');
245 return $this->insertTemplate('chEvt');
246
247 case 'showCoords' :
248 $this->addFuncs('getE', 'newel', 'newtext', 'getData', 'showhide',
249 'fitRect', 'textAttr', 'strValueX', 'strValueY');
250
251 // format text for assoc X, assoc Y or x,y
252 $text_format_x = 'fnx(de,x,bb.width,gx,' .
253 'textAttr(ti,"unitsbx"),textAttr(ti,"unitsx"))';
254 $text_format_y = 'fny(de,bb.height-y,bb.height,gy,' .
255 'textAttr(ti,"unitsby"),textAttr(ti,"unitsy"))';
256
257 if(!$this->graph->getOption('crosshairs_show_h'))
258 $text_format = $text_format_x;
259 elseif(!$this->graph->getOption('crosshairs_show_v'))
260 $text_format = $text_format_y;
261 else
262 $text_format = $text_format_x . ' + ", " + ' . $text_format_y;
263
264 $pad = max(0, (int)$this->graph->getOption('crosshairs_text_padding'));
265 $space = max(0, (int)$this->graph->getOption('crosshairs_text_space'));
266 $vars = [
267 'text_format' => $text_format,
268 'pad' => $pad,
269 // calculate these here to save doing it in JS
270 'pad_space' => $pad + $space,
271 'space2' => $space * 2,
272 ];
273 return $this->insertTemplate('showCoords', $vars);
274
275 case 'crosshairs' :
276 $this->addFuncs('chEvt', 'setattr', 'svgNode', 'svgCursorCoords',
277 'showhide');
278
279 $vars = [ 'show_x' => '', 'show_y' => '', 'show_text' => ''];
280 if($this->graph->getOption('crosshairs_show_text')) {
281 $this->addFunction('showCoords');
282 $vars['show_text'] = 'showCoords(de, x - bb.x, y - bb.y, bb, on);';
283 }
284 if($this->graph->getOption('crosshairs_show_h'))
285 $vars['show_x'] = 'showhide(xc,on);';
286 if($this->graph->getOption('crosshairs_show_v'))
287 $vars['show_y'] = 'showhide(yc,on);';
288 return $this->insertTemplate('crosshairs', $vars);
289
290 case 'dragEvent' :
291 $this->addFuncs('newel', 'getE', 'setattr', 'finditem', 'svgCursorCoords');
292 $this->addInitFunction('dragEvent');
293 return $this->insertTemplate('dragEvent');
294
295 case 'magEvt' :
296 $this->addInitFunction('magEvt');
297 $vars = ['namespace' => $this->namespace];
298 return $this->insertTemplate('magEvt', $vars);
299
300 default :
301 // Trying to add a function that doesn't exist?
302 throw new \Exception('Unknown function "' . $name . '"');
303 }
304
305 $this->insertFunction($realname, $fn);
306 }
307
308 /**
309 * Inserts a Javascript function into the list
310 */
311 public function insertFunction($name, $fn)
312 {
313 $this->functions[$name] = $fn;
314 }
315
316 /**
317 * Inserts a function from a template
318 */
319 public function insertTemplate($name, $vars = null, $realname = null)
320 {
321 if($realname === null)
322 $realname = $name;
323 $file_path = __DIR__ . '/templates/' . $name . '.txt';
324 if(!file_exists($file_path))
325 throw new \Exception('Template [' . $name . '.txt] not found.');
326 $content = file_get_contents($file_path);
327
328 if($vars !== null) {
329 // insert variables into template
330 $content = preg_replace_callback('/{\$([a-z]+):([a-z0-9_]+)}/',
331 function($m) use($vars) {
332 list(, $type, $var) = $m;
333 if(!isset($vars[$var]))
334 throw new \Exception('Variable [' . $var . '] not defined.');
335
336 $value = $vars[$var];
337 if('number' === $type) {
338 if(is_numeric($value))
339 return new Number($value);
340
341 if(is_object($value) && get_class($value) === 'Goat1000\\SVGGraph\\Number')
342 return $value;
343
344 throw new \Exception('Variable [' . $var . '] not numeric, value "' .
345 $value . '".');
346 }
347 return $value; }, $content);
348 }
349
350 $this->insertFunction($realname, $content);
351 }
352
353 /**
354 * Convert hex from regex matched entity to javascript escape sequence
355 */
356 public static function hex2js($m)
357 {
358 return sprintf('\u%04x', base_convert($m[1], 16, 10));
359 }
360
361 /**
362 * Convert decimal from regex matched entity to javascript escape sequence
363 */
364 public static function dec2js($m)
365 {
366 return sprintf('\u%04x', $m[1]);
367 }
368
369 public static function reEscape($string)
370 {
371 // convert XML char entities to JS unicode
372 $string = preg_replace_callback('/&#x([a-f0-9]+);/',
373 'Goat1000\\SVGGraph\\Javascript::hex2js', $string);
374 $string = preg_replace_callback('/&#([0-9]+);/',
375 'Goat1000\\SVGGraph\\Javascript::dec2js', $string);
376 return $string;
377 }
378
379 /**
380 * Adds a Javascript variable
381 * - use $value:$more for assoc
382 * - use NULL:$more for array
383 */
384 public function insertVariable($var, $value, $more = null, $quote = true)
385 {
386 $q = $quote ? '"' : '';
387 if($more === null)
388 $this->variables[$var] = $q . $this->reEscape($value) . $q;
389 elseif($value === null)
390 $this->variables[$var][] = $q . $this->reEscape($more) . $q;
391 else
392 $this->variables[$var][$value] = $q . $this->reEscape($more) . $q;
393 }
394
395 /**
396 * Insert a numeric variable
397 */
398 public function insertNumberVar($var, $value)
399 {
400 $this->variables[$var] = new Number($value);
401 }
402
403 /**
404 * Adds an init function to the list
405 */
406 public function addInitFunction($name)
407 {
408 $this->init_functions[$name] = 1;
409 }
410
411 /**
412 * Insert a comment into the Javascript section - handy for debugging!
413 */
414 public function insertComment($details)
415 {
416 $this->comments[] = $details;
417 }
418
419 /**
420 * Fade and click at the same time requires different functions
421 */
422 private function fadeAndClick()
423 {
424 $this->addFuncs('getE', 'finditem', 'fading', 'textAttr', 'setattr');
425 $this->addInitFunction('clickShowEvent');
426 $this->addInitFunction('fade');
427 $this->insertTemplate('clickShowEvent_fade', null, 'clickShowEvent');
428 $this->insertTemplate('fade_clickShow', null, 'fade');
429 }
430
431 /**
432 * Sets the tooltip for an element
433 */
434 public function setTooltip(&$element, $text, $duplicate = false)
435 {
436 $this->addFuncs('tooltip', 'texttt', 'ttEvent');
437 if(!isset($element['id']))
438 $element['id'] = $this->graph->newID();
439 $this->insertVariable('tips', $element['id'], $text);
440
441 if($duplicate) {
442 $this->addOverlay($element['id'], $this->graph->newID());
443 }
444 }
445
446 /**
447 * Sets click show/hide for an element
448 * If using with fading, this must be used first
449 */
450 public function setClickShow(&$element, $target, $hidden, $duplicate = false)
451 {
452 if(!isset($element['id']))
453 $element['id'] = $this->graph->newID();
454 $id = $duplicate ? $this->graph->newID() : $element['id'];
455 if($duplicate)
456 $this->addOverlay($element['id'], $id);
457
458 $this->addFunction('clickShowEvent');
459 $show = $hidden ? 0 : 1;
460 $this->insertVariable('clickElements', $element['id'], $target);
461 $this->insertVariable('clickMap', $target, $show, false);
462 $this->clickshow_enabled = true;
463 }
464
465 /**
466 * Sets pop to front for $target when mouse over $element
467 */
468 public function setPopFront(&$element, $target, $duplicate = false)
469 {
470 if(!isset($element['id']))
471 $element['id'] = $this->graph->newID();
472 $id = $duplicate ? $this->graph->newID() : $element['id'];
473 if($duplicate)
474 $this->addOverlay($element['id'], $id);
475
476 $this->addFunction('popFront');
477 $this->insertVariable('popfronts', $element['id'], $target);
478 }
479
480 /**
481 * Sets the fader for an element
482 * If using with clickShow, that must be used first
483 */
484 public function setFader(&$element, $in, $out, $target = null,
485 $duplicate = false)
486 {
487 if(!isset($element['id']))
488 $element['id'] = $this->graph->newID();
489 if($target === null)
490 $target = $element['id'];
491 $id = $duplicate ? $this->graph->newID() : $element['id'];
492
493 $this->addFunction('fade');
494 if($in) {
495 $this->addFunction('fadeEventIn');
496 $this->insertNumberVar('fistep', $in);
497 }
498 if($out) {
499 $this->addFunction('fadeEventOut');
500 $this->insertNumberVar('fostep', -$out);
501 }
502 $this->insertVariable('fades', $element['id'],
503 '{id:"' . $target . '",dir:0}', false);
504 $this->insertNumberVar('fstart', $in ? 0 : 1);
505
506 if($duplicate)
507 $this->addOverlay($element['id'], $id);
508 $this->fader_enabled = true;
509 }
510
511 /**
512 * Makes an item draggable
513 */
514 public function setDraggable(&$element)
515 {
516 if(!isset($element['id']))
517 $element['id'] = $this->graph->newID();
518 $this->addFunction('dragEvent');
519 $this->insertVariable('draggable', $element['id'], 0);
520 }
521
522 /**
523 * Makes something auto-hide
524 */
525 public function autoHide(&$element, $hidden_opacity = 0, $full_opacity = 1)
526 {
527 if(!isset($element['id']))
528 $element['id'] = $this->graph->newID();
529 $this->addFunction('autoHide');
530 $this->insertVariable('autohide', $element['id'], 0);
531 $this->insertVariable('ah_opacity', $element['id'], '[' .
532 new Number($hidden_opacity) . ',' .
533 new Number($full_opacity) . ']', false);
534 }
535
536 /**
537 * Adds magnifier
538 */
539 public function magnifier()
540 {
541 $max_mag = 10.0;
542 $min_mag = 1.1;
543 $max_pan = 10.0;
544 $min_pan = 1.1;
545 $mag = (float)$this->graph->getOption('magnify');
546 $pan = (float)$this->graph->getOption('magnify_pan');
547 if($mag <= $min_mag)
548 $mag = $min_mag;
549 elseif($mag > $max_mag)
550 $mag = $max_mag;
551 if($pan <= $min_pan)
552 $pan = $min_pan;
553 elseif($pan > $max_pan)
554 $pan = $max_pan;
555
556 $vars = [
557 'magnification' => $mag,
558 'sensitivity' => $pan,
559 'namespace' => $this->namespace,
560 ];
561 $this->addFuncs('magEvt','svgNode','svgCursorCoords','setattr');
562 $this->insertTemplate('magnifier', $vars);
563 }
564
565 /**
566 * Add an overlaid copy of an element, with opacity of 0
567 */
568 public function addOverlay($from, $to)
569 {
570 $this->addFunction('duplicate');
571
572 // order matters, so clear previous value
573 if(isset($this->variables['dups'][$from]))
574 unset($this->variables['dups'][$from]);
575 $this->insertVariable('dups', $from, $to);
576 }
577
578 /**
579 * Returns the variables (and comments) as Javascript code
580 */
581 public function getVariables()
582 {
583 $variables = '';
584 if(count($this->variables)) {
585 $vlist = [];
586 foreach($this->variables as $name => $value) {
587 $var = $name;
588 if(is_array($value)) {
589 if(isset($value[0]) && isset($value[count($value)-1])) {
590 $var .= '=[' . implode(',', $value) . ']';
591 } else {
592 $vs = [];
593 foreach($value as $k => $v)
594 if($k)
595 $vs[] = $k . ':' . $v;
596
597 $var .= '={' . implode(',', $vs) . '}';
598 }
599 } elseif($value !== null) {
600 $var .= '=' . $value;
601 }
602 $vlist[] = $var;
603 }
604 $variables = 'var ' . implode(', ', $vlist) . ';';
605 }
606 // comments can be stuck with the variables
607 if(count($this->comments)) {
608 foreach($this->comments as $c) {
609 if(!is_string($c))
610 $c = print_r($c, true);
611 $variables .= "\n// " . str_replace("\n", "\n// ", $c);
612 }
613 }
614 return $variables;
615 }
616
617 /**
618 * Returns the functions as Javascript code
619 */
620 public function getFunctions()
621 {
622 $functions = '';
623 if(count($this->functions)) {
624 if(count($this->init_functions)) {
625 $vars = [
626 'init_funcs' => implode('();', array_keys($this->init_functions)) . '();'
627 ];
628 $this->insertTemplate('init', $vars);
629 }
630 $functions = implode('', $this->functions);
631 }
632
633 return $functions;
634 }
635
636 /**
637 * Returns the onload code to use for the SVG
638 */
639 public function getOnload()
640 {
641 if(count($this->init_functions))
642 return 'init()';
643 return '';
644 }
645
646 /**
647 * Returns all the code to be inserted into <script>
648 */
649 public function getCode($cdata, $minifier)
650 {
651 $variables = $this->getVariables();
652 $functions = $this->getFunctions();
653 $onload = $this->getOnload();
654
655 if($variables == '' && $functions == '')
656 return '';
657
658 if($onload != '')
659 $functions .= "\n" . 'setTimeout(function(){' . $onload . '},10);';
660
661 $script = $variables . "\n" . $functions . "\n";
662 if(is_callable($minifier))
663 $script = call_user_func($minifier, $script);
664 elseif($minifier !== null)
665 $script = $this->minify($script);
666
667 // make closure
668 $script = '(function(){' . $script . "\n})();";
669
670 if($cdata)
671 $script = "// <![CDATA[\n" . $script . "\n// ]]>";
672
673 return $script;
674 }
675
676 /**
677 * Simple minifier
678 */
679 public static function minify($code)
680 {
681 $start = strlen($code);
682 $code = preg_replace(
683 ['/^\s+/m', '/\s*([{}=+<>,;?:)\/*-]|&&|\|\|)\s*/', ],
684 ['', '$1', ],
685 $code);
686
687 return $code;
688 }
689 }
690
691