PluginProbe
E2Pdf – Export Pdf Tool for WordPress / 1.32.49
E2Pdf – Export Pdf Tool for WordPress v1.32.49
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 1.08.00 1.08.06 All 72 releases
e2pdf / vendors / svggraph / Javascript.php

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

687 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 <graham@goat1000.com>
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 $anchors = ['left' => 'start', 'right' => 'end'];
186 $vars['anchor'] = isset($anchors[$vars['align']]) ?
187 $anchors[$vars['align']] : 'middle';
188 return $this->insertTemplate('texttt', $vars);
189
190 case 'ttEvent' :
191 $this->addFuncs('finditem');
192 $this->addInitFunction('ttEvent');
193 return $this->insertTemplate('ttEvent');
194
195 case 'popFront' :
196 $this->addFuncs('getE', 'finditem');
197 $this->addInitFunction('popFront');
198 return $this->insertTemplate('popFront');
199
200 case 'clickShowEvent' :
201 if($this->fader_enabled)
202 return $this->fadeAndClick();
203
204 $this->addFuncs('getE', 'finditem', 'setattr');
205 $this->addInitFunction('clickShowEvent');
206 return $this->insertTemplate('clickShowEvent');
207
208 case 'fade' :
209 if($this->clickshow_enabled)
210 return $this->fadeAndClick();
211
212 $this->addFuncs('getE', 'setattr', 'textAttr');
213 $this->addInitFunction('fade');
214 return $this->insertTemplate('fade');
215
216 case 'fadeEventIn' :
217 $this->addFuncs('finditem');
218 $this->addInitFunction('fadeEventIn');
219 return $this->insertTemplate('fadeEventIn');
220
221 case 'fadeEventOut' :
222 $this->addFuncs('finditem');
223 $this->addInitFunction('fadeEventOut');
224 return $this->insertTemplate('fadeEventOut');
225
226 case 'duplicate' :
227 $this->addFuncs('getE', 'newel', 'setattr');
228 $this->addInitFunction('initDups');
229 return $this->insertTemplate('duplicate', ['namespace' => $this->namespace]);
230
231 case 'svgNode' :
232 return $this->insertTemplate('svgNode', ['namespace' => $this->namespace]);
233
234 case 'autoHide' :
235 $this->addFuncs('getE', 'setattr', 'finditem');
236 $this->addInitFunction('autoHide');
237 return $this->insertTemplate('autoHide');
238
239 case 'chEvt' :
240 $this->addInitFunction('chEvt');
241 return $this->insertTemplate('chEvt');
242
243 case 'showCoords' :
244 $this->addFuncs('getE', 'newel', 'newtext', 'getData', 'showhide',
245 'fitRect', 'textAttr', 'strValueX', 'strValueY');
246
247 // format text for assoc X, assoc Y or x,y
248 $text_format_x = 'fnx(de,x,bb.width,gx,' .
249 'textAttr(ti,"unitsbx"),textAttr(ti,"unitsx"))';
250 $text_format_y = 'fny(de,bb.height-y,bb.height,gy,' .
251 'textAttr(ti,"unitsby"),textAttr(ti,"unitsy"))';
252
253 if(!$this->graph->getOption('crosshairs_show_h'))
254 $text_format = $text_format_x;
255 elseif(!$this->graph->getOption('crosshairs_show_v'))
256 $text_format = $text_format_y;
257 else
258 $text_format = $text_format_x . ' + ", " + ' . $text_format_y;
259
260 $pad = max(0, (int)$this->graph->getOption('crosshairs_text_padding'));
261 $space = max(0, (int)$this->graph->getOption('crosshairs_text_space'));
262 $vars = [
263 'text_format' => $text_format,
264 'pad' => $pad,
265 // calculate these here to save doing it in JS
266 'pad_space' => $pad + $space,
267 'space2' => $space * 2,
268 ];
269 return $this->insertTemplate('showCoords', $vars);
270
271 case 'crosshairs' :
272 $this->addFuncs('chEvt', 'setattr', 'svgNode', 'svgCursorCoords',
273 'showhide');
274
275 $vars = [ 'show_x' => '', 'show_y' => '', 'show_text' => ''];
276 if($this->graph->getOption('crosshairs_show_text')) {
277 $this->addFunction('showCoords');
278 $vars['show_text'] = 'showCoords(de, x - bb.x, y - bb.y, bb, on);';
279 }
280 if($this->graph->getOption('crosshairs_show_h'))
281 $vars['show_x'] = 'showhide(xc,on);';
282 if($this->graph->getOption('crosshairs_show_v'))
283 $vars['show_y'] = 'showhide(yc,on);';
284 return $this->insertTemplate('crosshairs', $vars);
285
286 case 'dragEvent' :
287 $this->addFuncs('newel', 'getE', 'setattr', 'finditem', 'svgCursorCoords');
288 $this->addInitFunction('dragEvent');
289 return $this->insertTemplate('dragEvent');
290
291 case 'magEvt' :
292 $this->addInitFunction('magEvt');
293 $vars = ['namespace' => $this->namespace];
294 return $this->insertTemplate('magEvt', $vars);
295
296 default :
297 // Trying to add a function that doesn't exist?
298 throw new \Exception('Unknown function "' . $name . '"');
299 }
300
301 $this->insertFunction($realname, $fn);
302 }
303
304 /**
305 * Inserts a Javascript function into the list
306 */
307 public function insertFunction($name, $fn)
308 {
309 $this->functions[$name] = $fn;
310 }
311
312 /**
313 * Inserts a function from a template
314 */
315 public function insertTemplate($name, $vars = null, $realname = null)
316 {
317 if($realname === null)
318 $realname = $name;
319 $file_path = __DIR__ . '/templates/' . $name . '.txt';
320 if(!file_exists($file_path))
321 throw new \Exception('Template [' . $name . '.txt] not found.');
322 $content = file_get_contents($file_path);
323
324 if($vars !== null) {
325 // insert variables into template
326 $content = preg_replace_callback('/{\$([a-z]+):([a-z0-9_]+)}/',
327 function($m) use($vars) {
328 list(, $type, $var) = $m;
329 if(!isset($vars[$var]))
330 throw new \Exception('Variable [' . $var . '] not defined.');
331
332 $value = $vars[$var];
333 if('number' === $type) {
334 if(is_numeric($value))
335 return new Number($value);
336
337 if(is_object($value) && get_class($value) === 'Goat1000\\SVGGraph\\Number')
338 return $value;
339
340 throw new \Exception('Variable [' . $var . '] not numeric, value "' .
341 $value . '".');
342 }
343 return $value; }, $content);
344 }
345
346 $this->insertFunction($realname, $content);
347 }
348
349 /**
350 * Convert hex from regex matched entity to javascript escape sequence
351 */
352 public static function hex2js($m)
353 {
354 return sprintf('\u%04x', base_convert($m[1], 16, 10));
355 }
356
357 /**
358 * Convert decimal from regex matched entity to javascript escape sequence
359 */
360 public static function dec2js($m)
361 {
362 return sprintf('\u%04x', $m[1]);
363 }
364
365 public static function reEscape($string)
366 {
367 // convert XML char entities to JS unicode
368 $string = preg_replace_callback('/&#x([a-f0-9]+);/',
369 'Goat1000\\SVGGraph\\Javascript::hex2js', $string);
370 $string = preg_replace_callback('/&#([0-9]+);/',
371 'Goat1000\\SVGGraph\\Javascript::dec2js', $string);
372 return $string;
373 }
374
375 /**
376 * Adds a Javascript variable
377 * - use $value:$more for assoc
378 * - use NULL:$more for array
379 */
380 public function insertVariable($var, $value, $more = null, $quote = true)
381 {
382 $q = $quote ? '"' : '';
383 if($more === null)
384 $this->variables[$var] = $q . $this->reEscape($value) . $q;
385 elseif($value === null)
386 $this->variables[$var][] = $q . $this->reEscape($more) . $q;
387 else
388 $this->variables[$var][$value] = $q . $this->reEscape($more) . $q;
389 }
390
391 /**
392 * Insert a numeric variable
393 */
394 public function insertNumberVar($var, $value)
395 {
396 $this->variables[$var] = new Number($value);
397 }
398
399 /**
400 * Adds an init function to the list
401 */
402 public function addInitFunction($name)
403 {
404 $this->init_functions[$name] = 1;
405 }
406
407 /**
408 * Insert a comment into the Javascript section - handy for debugging!
409 */
410 public function insertComment($details)
411 {
412 $this->comments[] = $details;
413 }
414
415 /**
416 * Fade and click at the same time requires different functions
417 */
418 private function fadeAndClick()
419 {
420 $this->addFuncs('getE', 'finditem', 'fading', 'textAttr', 'setattr');
421 $this->addInitFunction('clickShowEvent');
422 $this->addInitFunction('fade');
423 $this->insertTemplate('clickShowEvent_fade', null, 'clickShowEvent');
424 $this->insertTemplate('fade_clickShow', null, 'fade');
425 }
426
427 /**
428 * Sets the tooltip for an element
429 */
430 public function setTooltip(&$element, $text, $duplicate = false)
431 {
432 $this->addFuncs('tooltip', 'texttt', 'ttEvent');
433 if(!isset($element['id']))
434 $element['id'] = $this->graph->newID();
435 $this->insertVariable('tips', $element['id'], $text);
436
437 if($duplicate) {
438 $this->addOverlay($element['id'], $this->graph->newID());
439 }
440 }
441
442 /**
443 * Sets click show/hide for an element
444 * If using with fading, this must be used first
445 */
446 public function setClickShow(&$element, $target, $hidden, $duplicate = false)
447 {
448 if(!isset($element['id']))
449 $element['id'] = $this->graph->newID();
450 $id = $duplicate ? $this->graph->newID() : $element['id'];
451 if($duplicate)
452 $this->addOverlay($element['id'], $id);
453
454 $this->addFunction('clickShowEvent');
455 $show = $hidden ? 0 : 1;
456 $this->insertVariable('clickElements', $element['id'], $target);
457 $this->insertVariable('clickMap', $target, $show, false);
458 $this->clickshow_enabled = true;
459 }
460
461 /**
462 * Sets pop to front for $target when mouse over $element
463 */
464 public function setPopFront(&$element, $target, $duplicate = false)
465 {
466 if(!isset($element['id']))
467 $element['id'] = $this->graph->newID();
468 $id = $duplicate ? $this->graph->newID() : $element['id'];
469 if($duplicate)
470 $this->addOverlay($element['id'], $id);
471
472 $this->addFunction('popFront');
473 $this->insertVariable('popfronts', $element['id'], $target);
474 }
475
476 /**
477 * Sets the fader for an element
478 * If using with clickShow, that must be used first
479 */
480 public function setFader(&$element, $in, $out, $target = null,
481 $duplicate = false)
482 {
483 if(!isset($element['id']))
484 $element['id'] = $this->graph->newID();
485 if($target === null)
486 $target = $element['id'];
487 $id = $duplicate ? $this->graph->newID() : $element['id'];
488
489 $this->addFunction('fade');
490 if($in) {
491 $this->addFunction('fadeEventIn');
492 $this->insertNumberVar('fistep', $in);
493 }
494 if($out) {
495 $this->addFunction('fadeEventOut');
496 $this->insertNumberVar('fostep', -$out);
497 }
498 $this->insertVariable('fades', $element['id'],
499 '{id:"' . $target . '",dir:0}', false);
500 $this->insertNumberVar('fstart', $in ? 0 : 1);
501
502 if($duplicate)
503 $this->addOverlay($element['id'], $id);
504 $this->fader_enabled = true;
505 }
506
507 /**
508 * Makes an item draggable
509 */
510 public function setDraggable(&$element)
511 {
512 if(!isset($element['id']))
513 $element['id'] = $this->graph->newID();
514 $this->addFunction('dragEvent');
515 $this->insertVariable('draggable', $element['id'], 0);
516 }
517
518 /**
519 * Makes something auto-hide
520 */
521 public function autoHide(&$element, $hidden_opacity = 0, $full_opacity = 1)
522 {
523 if(!isset($element['id']))
524 $element['id'] = $this->graph->newID();
525 $this->addFunction('autoHide');
526 $this->insertVariable('autohide', $element['id'], 0);
527 $this->insertVariable('ah_opacity', $element['id'], '[' .
528 new Number($hidden_opacity) . ',' .
529 new Number($full_opacity) . ']', false);
530 }
531
532 /**
533 * Adds magnifier
534 */
535 public function magnifier()
536 {
537 $max_mag = 10.0;
538 $min_mag = 1.1;
539 $max_pan = 10.0;
540 $min_pan = 1.1;
541 $mag = (float)$this->graph->getOption('magnify');
542 $pan = (float)$this->graph->getOption('magnify_pan');
543 if($mag <= $min_mag)
544 $mag = $min_mag;
545 elseif($mag > $max_mag)
546 $mag = $max_mag;
547 if($pan <= $min_pan)
548 $pan = $min_pan;
549 elseif($pan > $max_pan)
550 $pan = $max_pan;
551
552 $vars = [
553 'magnification' => $mag,
554 'sensitivity' => $pan,
555 'namespace' => $this->namespace,
556 ];
557 $this->addFuncs('magEvt','svgNode','svgCursorCoords','setattr');
558 $this->insertTemplate('magnifier', $vars);
559 }
560
561 /**
562 * Add an overlaid copy of an element, with opacity of 0
563 */
564 public function addOverlay($from, $to)
565 {
566 $this->addFunction('duplicate');
567
568 // order matters, so clear previous value
569 if(isset($this->variables['dups'][$from]))
570 unset($this->variables['dups'][$from]);
571 $this->insertVariable('dups', $from, $to);
572 }
573
574 /**
575 * Returns the variables (and comments) as Javascript code
576 */
577 public function getVariables()
578 {
579 $variables = '';
580 if(count($this->variables)) {
581 $vlist = [];
582 foreach($this->variables as $name => $value) {
583 $var = $name;
584 if(is_array($value)) {
585 if(isset($value[0]) && isset($value[count($value)-1])) {
586 $var .= '=[' . implode(',', $value) . ']';
587 } else {
588 $vs = [];
589 foreach($value as $k => $v)
590 if($k)
591 $vs[] = $k . ':' . $v;
592
593 $var .= '={' . implode(',', $vs) . '}';
594 }
595 } elseif($value !== null) {
596 $var .= '=' . $value;
597 }
598 $vlist[] = $var;
599 }
600 $variables = 'var ' . implode(', ', $vlist) . ';';
601 }
602 // comments can be stuck with the variables
603 if(count($this->comments)) {
604 foreach($this->comments as $c) {
605 if(!is_string($c))
606 $c = print_r($c, true);
607 $variables .= "\n// " . str_replace("\n", "\n// ", $c);
608 }
609 }
610 return $variables;
611 }
612
613 /**
614 * Returns the functions as Javascript code
615 */
616 public function getFunctions()
617 {
618 $functions = '';
619 if(count($this->functions)) {
620 if(count($this->init_functions)) {
621 $vars = [
622 'init_funcs' => implode('();', array_keys($this->init_functions)) . '();'
623 ];
624 $this->insertTemplate('init', $vars);
625 }
626 $functions = implode('', $this->functions);
627 }
628
629 return $functions;
630 }
631
632 /**
633 * Returns the onload code to use for the SVG
634 */
635 public function getOnload()
636 {
637 if(count($this->init_functions))
638 return 'init()';
639 return '';
640 }
641
642 /**
643 * Returns all the code to be inserted into <script>
644 */
645 public function getCode($cdata, $minifier)
646 {
647 $variables = $this->getVariables();
648 $functions = $this->getFunctions();
649 $onload = $this->getOnload();
650
651 if($variables == '' && $functions == '')
652 return '';
653
654 if($onload != '')
655 $functions .= "\n" . 'setTimeout(function(){' . $onload . '},10);';
656
657 $script = $variables . "\n" . $functions . "\n";
658 if(is_callable($minifier))
659 $script = call_user_func($minifier, $script);
660 elseif($minifier !== null)
661 $script = $this->minify($script);
662
663 // make closure
664 $script = '(function(){' . $script . "\n})();";
665
666 if($cdata)
667 $script = "// <![CDATA[\n" . $script . "\n// ]]>";
668
669 return $script;
670 }
671
672 /**
673 * Simple minifier
674 */
675 public static function minify($code)
676 {
677 $start = strlen($code);
678 $code = preg_replace(
679 ['/^\s+/m', '/\s*([{}=+<>,;?:)\/*-]|&&|\|\|)\s*/', ],
680 ['', '$1', ],
681 $code);
682
683 return $code;
684 }
685 }
686
687