├── License
├── README.md
├── builder
├── builder.php
└── builder.py.php
├── composer.json
├── lib
├── EMT.Test.php
├── external
│ └── finediff.php
├── fs.php
└── lib.php
├── misc
├── no.png
└── yes.png
├── src-php
├── EMT.Lib.php
├── EMT.Tret.Abbr.php
├── EMT.Tret.Dash.php
├── EMT.Tret.Date.php
├── EMT.Tret.Etc.php
├── EMT.Tret.Nobr.php
├── EMT.Tret.Number.php
├── EMT.Tret.OptAlign.php
├── EMT.Tret.Punctmark.php
├── EMT.Tret.Quote.php
├── EMT.Tret.Space.php
├── EMT.Tret.Symbol.php
├── EMT.Tret.Text.php
├── EMT.Tret.php
└── EMT.php
├── src-py
└── EMT.py
├── tests
├── test.bugs.php
├── test.em.php
├── test.nsys.php
├── test.reqbug.php
└── tests.json
├── tools-php
├── clean.php
├── debug.php
├── example.php
├── online-fast.php
├── online.php
└── test.php
└── tools-py
├── run.py
└── test.py
/License:
--------------------------------------------------------------------------------
1 | Public Domain
2 |
3 | Типограф является общественным достоянием.
4 |
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | mdash
2 | =====
3 |
4 | Evgeny Muravjev Typograph, http://mdash.ru
5 | Authors: Evgeny Muravjev & Alexander Drutsa
6 |
7 | EMT - Evgeny Muravjev Typograph
8 |
9 | BUILDING
10 | ========
11 | To build EMT you need php version 5.2 or newer.
12 | Suggested way to build typograph is to run builder.php through web-server.
13 |
14 | E.g.:
15 | http://localhost/mdash/builder/builder.php
16 |
17 | Then you will have 3 options: build for php, build for python, build tests for python.
18 |
19 | Resulting files are located on root of the project:
20 | EMT.php
21 | EMT.py
22 |
23 |
24 | RUNNING
25 | =======
26 | Use tools-php/online.php to test php version of the Typograph.
27 |
28 | tools-php/example.php contains few examples, how to use typograph inside php.
29 | tools-py/run.py contains examples, how to use typograph inside python.
30 |
31 | TESTING
32 | =======
33 | To run tests for php version you should go to the page tools-php/test.php through web-server.
34 | On this page you hit start to run all the tests.
35 |
36 | E.g.:
37 | http://localhost/mdash/tools-php/test.php
38 |
39 | To python tests you need first build test set from php version through builder/builder.php, and
40 | then run the tests:
41 |
42 | cd tools-py
43 |
44 | python test.py
45 |
46 | DEBUGGING
47 | =========
48 | Debug EMT, use debug.php
49 | This script will show all the rules that were used, for selected text and show step by step log.
50 |
--------------------------------------------------------------------------------
/builder/builder.php:
--------------------------------------------------------------------------------
1 | Сгенерировать файл типографа для PHP
14 | Сгенерировать файл типографа для Python
15 | Сгенерировать тесты для Python
16 | HTML;
17 | exit;
18 | }
19 | if($action == "installer" || $action == "installerpy") {
20 |
21 | function removebetween($str, $start, $end) {
22 | $z = explode($start, $str);
23 | $b = $z[0];
24 | if(count($z)==1) return $str;
25 | unset($z[0]);
26 | $d = explode($end, implode($start, $z));
27 | unset($d[0]);
28 | $str = $b.implode($end, $d);
29 | return removebetween($str, $start, $end);
30 | }
31 |
32 | function removelinewith($str, $inc) {
33 | $z = explode("\n", $str);
34 | $a = array();
35 | foreach($z as $d) {
36 | if(strpos($d,$inc)!==false) continue;
37 | $a[] = $d;
38 | }
39 | return implode("\n", $a);
40 | }
41 |
42 | function phpfile_read($file, $all = false)
43 | {
44 | $size = filesize($file);
45 | $fp2 = fopen($file, "r");
46 | fseek($fp2, 5);
47 | $s = fread($fp2, $size - 5 - 2);
48 | fclose($fp2);
49 |
50 | $lines = explode("\n", $s);
51 | $lines2 = array();
52 | foreach($lines as $line)
53 | {
54 | if(preg_match("/^\s*require(_once)?\s*\([^\)]*\)\s*;\s*$/i", $line))
55 | continue;
56 | $lines2[] = $line;
57 | }
58 | $s = implode("\n", $lines2);
59 |
60 | if(!$all) {
61 | $s = removebetween($s, "/**PYTHON", "PYTHON**/");
62 | $s = removelinewith($s, "replacement_py");
63 | }
64 |
65 | return $s;
66 | }
67 |
68 |
69 | $listx = FS::list_only_files("../src-php/","/^EMT.Tret\..*\.php$/");
70 |
71 | if($action == "installerpy") {
72 | $resname = "../EMT.forpy.php";
73 | }
74 |
75 | $fp = fopen($resname,"w");
76 | $dfile = "";
77 |
78 | $list = array();
79 |
80 | $list[] = "EMT.Lib.php";
81 | $list[] = "EMT.Tret.php";
82 | foreach($listx as $e) $list[] = $e;
83 | $list[] = "EMT.php";
84 |
85 | fprintf($fp, "";
108 | }
109 | if($action == "installerpy") {
110 | require_once("../EMT.forpy.php");
111 | $z = file_get_contents("../src-py/EMT.py");
112 |
113 | require_once("builder.py.php");
114 |
115 | $tretsx = array();
116 | $typograf = new EMTypograph();
117 | foreach($typograf->trets as $tret)
118 | {
119 | $tretx = $typograf->get_tret($tret);
120 | $tretsx[] = work_for_py($tretx);
121 | }
122 |
123 | $zz = str_replace("#####EMT_TRETS#####", implode("", $tretsx), $z);
124 | file_put_contents("../EMT.py", $zz);
125 | @unlink("../EMT.forpy.php");
126 | echo "Сгенерирован скрипт типографа для Python
";
127 | }
128 |
129 | if($action == "testpy") {
130 | $noecho = 1;
131 | require_once("../tools-php/test.php");
132 | $list = FS::list_only_files("../tests/", '/^test\.[0-9a-z\.\-_]+\.php$/i');
133 |
134 | if(count($list)>0)
135 | {
136 | $tester = new EMTTester();
137 | $tester->double_test = isset($_GET['double_test']);
138 | $tester->set_typoclass("EMTypograph");
139 | foreach($list as $file) include("../tests/$file");
140 | //$ok = $tester->testit();
141 | //$result = $tester->results;
142 | } else {
143 | echo "В каталоге tests тесты не обнаружены";
144 | exit;
145 | }
146 |
147 | file_put_contents("../tests/tests.json", json_encode($tester->list));
148 | echo "Сгенерированы тесты типографа для Python
";
149 | }
150 |
151 | ?>
--------------------------------------------------------------------------------
/builder/builder.py.php:
--------------------------------------------------------------------------------
1 | rules;
7 | foreach($rules as $k => $b) {
8 | //echo $k."
";
9 | if(isset($rules[$k]['function'])) {
10 | echo "Функция в правиле: ".$rules[$k]['function']."
\n";
11 | continue;
12 | }
13 | if($rules[$k]['replacement_python']) {
14 | $rules[$k]['replacement'] = $rules[$k]['replacement_python'];
15 | continue;
16 | }
17 | if(is_string($rules[$k]['pattern'])) {
18 | if(!ispregeval($rules[$k]['pattern'])) continue;
19 | $rules[$k]['replacement'] = processpyrepl($rules[$k]['replacement']);
20 | continue;
21 | }
22 | $eval = false;
23 | foreach($rules[$k]['pattern'] as $v) {
24 | if(ispregeval($v)) {
25 | $eval = true;
26 | break;
27 | }
28 | }
29 | if(!$eval) continue;
30 | if(is_string($rules[$k]['replacement'])) {
31 | $rules[$k]['replacement'] = processpyrepl($rules[$k]['replacement']);
32 | continue;
33 | }
34 | foreach($rules[$k]['pattern'] as $i => $v) {
35 | if(ispregeval($v)) $rules[$k]['replacement'][$i] = processpyrepl($rules[$k]['replacement'][$i]);
36 | }
37 | }
38 | $rule_order = prepare_var(array_keys($rules));
39 | $rules = prepare_var($rules);
40 |
41 |
42 | $zf = file_get_contents("../src-php/".str_replace("_",".",$cls).".php");
43 | echo "Обработка класса $cls
\n";
44 | $b = array();
45 | if(preg_match_all('/(public|protected|private) +\$([a-z0-9_]+)/i', $zf, $m,PREG_SET_ORDER)) {
46 | foreach($m as $mx) {
47 | if(!in_array($mx[2], array('title', 'rules'))) {
48 | $b[] = $mx[2];
49 | }
50 | }
51 | }
52 | $extvars = "";
53 | foreach ($b as $var)
54 | {
55 | echo " - Доп переменная $var
\n";
56 | $vc = prepare_var($tret->$var);
57 | $extvars .=<<";
160 | $repl = opreplace($repl, '.', '+');
161 | $repl = arrayreplace($repl);
162 | $repl = condopreplace($repl);
163 | $repl = str_replace('self::', '', $repl);
164 | $repl = str_replace('$this->', 'self.', $repl);
165 | $repl = str_replace('EMT_Lib::', 'EMT_Lib.', $repl);
166 | $repl = str_replace('substr(', 'EMT_Lib.substr(', $repl);
167 | $repl = str_replace('str_replace(', 'EMT_Lib.str_replace(', $repl);
168 | $repl = str_replace('intval(', 'int(', $repl);
169 | $repl = str_replace_safequote('||', ' or ', $repl);
170 | $repl = str_replace_safequote('&&', ' and ', $repl);
171 | $repl = str_replace_safequote('===', '==', $repl);
172 | $repl = preg_replace('/trim\(([^\)]+)\)/', '\1.strip()', $repl);
173 | $repl = preg_replace('/isset\(\$m\[([0-9])+\]/', '(m.group(\1)', $repl);
174 | $repl = preg_replace('/\$m\[([0-9]+)\]/', 'm.group(\1)', $repl);
175 | $repl = preg_replace('/substr_count\(([^,]+),/', '\1.count(', $repl);
176 | $repl = preg_replace('/str_repeat\(([^,]+),/', '\1 * (', $repl);
177 | $repl = preg_replace('/in_array\(([^,]+),/', '\1 in (', $repl);
178 | $repl = str_replace_safequote2('"', 'u"', $repl);
179 |
180 |
181 |
182 |
183 | echo "$repl
\n";
184 | return $repl;
185 | }
186 |
187 |
188 | function opreplace($str, $op, $new) {
189 | $off = 0;
190 | while(($r = mb_strpos($str, $op, $off)) !== false) {
191 | $z = mb_strlen($op);
192 | if(!inbrackets($str, $r)) {
193 | $str = mb_substr($str, 0, $r) . $new . mb_substr($str, $r+mb_strlen($op));
194 | $z = mb_strlen($new);
195 | }
196 | $off = $r+$z;
197 | }
198 | return $str;
199 | }
200 |
201 |
202 | function inbracketsx($str, $pos , $off = 0) {
203 | $p = mb_strpos($str, "\"", $off);
204 | $r = mb_strpos($str, "'", $off);
205 | if($p === false && $r === false) return false;
206 | if($p === false) $p = 10000000;
207 | if($r === false) $r = 10000000;
208 | $z = min($p,$r);
209 | if($z >= $pos) return false;
210 | $sym = mb_substr($str, $z, 1);
211 | $k = mb_strpos($str, $sym, $z+1);
212 | if($k === false) return true;
213 | if($k >= $pos) return true;
214 |
215 | return inbracketsx($str, $pos, $k+1);
216 | }
217 |
218 | function inbrackets($str, $pos ) {
219 | $str = str_replace('\\"', "||", $str);
220 | $str = str_replace("\\'", "||", $str);
221 |
222 | return inbracketsx($str, $pos);
223 | }
224 |
225 | function arrayreplace($str) {
226 | $off = 0;
227 | while(($r = mb_strpos($str, "array(", $off)) !== false) {
228 | if(mb_substr($str, $r-1,1) == "_") {
229 | $off = $r + 1;
230 | continue;
231 | }
232 | $l = getbetweensymb($str, $r+ 5, "(" , ")");
233 | if($l === false) {
234 | echo "ERROR REPLACING ARRAY()
";
235 | exit(1);
236 | return false;
237 | }
238 | //eval('$xx = '.mb_substr($str, $r, $l['end'] - $r).' ;');
239 | $new = "{".str_replace("=>",":",mb_substr($str, $r+mb_strlen("array("), $l['end'] - $r-1-mb_strlen("array(")))."}";
240 |
241 | //$new = json_encode($xx);
242 | $str = mb_substr($str, 0, $r) . $new . mb_substr($str, $l['end']);
243 | //echo "A";
244 | }
245 | return $str;
246 | }
247 |
248 | function getbetweensymb($str, $start, $open, $close) {
249 | $off = $start+mb_strlen($open);
250 | $dif = 1;
251 | while($dif>0) {
252 | $poff = $off;
253 | $roff = $off;
254 | do {
255 | $p = mb_strpos($str, $open, $poff);
256 | if($p === false) break;
257 | $poff = $p + mb_strlen($open);
258 |
259 | } while(inbrackets($str,$p));
260 | do {
261 | $r = mb_strpos($str, $close, $roff);
262 | if($r !== false) break;
263 | $roff = $r + mb_strlen($close);
264 | } while(inbrackets($str,$r));
265 | if($r === false ) {
266 | return false;
267 | }
268 | if ($p === false) $p = 10000000;
269 | $z = min($p, $r);
270 | if (mb_substr($str, $z, mb_strlen($open) ) == $open) {
271 | $dif++;
272 | $off = $z + mb_strlen($open);
273 | }
274 | if (mb_substr($str, $z, mb_strlen($close) ) == $close) {
275 | $dif--;
276 | $off = $z + mb_strlen($close);
277 | }
278 | }
279 | return array('start' => $start, 'end' => $off);
280 | }
281 |
282 |
283 | function utf8_strrev($str){
284 | preg_match_all('/./us', $str, $ar);
285 | return implode(array_reverse($ar[0]));
286 | }
287 | function fundleftopen($str, $start) {
288 | $strx = utf8_strrev($str);
289 | $x = getbetweensymb($strx, mb_strlen($str) - $start-1, ")", "(");
290 | if($x === false) return false;
291 | //var_dump($strx);
292 | //var_dump(mb_strlen($strx));
293 | //var_dump($x);
294 | //exit;
295 | return mb_strlen($str) -1- ($x['end'] -1) ;
296 | }
297 |
298 |
299 |
300 |
301 | function condopreplace($str) {
302 | $off = 0;
303 | while(($r = mb_strpos($str, "?", $off)) !== false) {
304 | if(inbrackets($str, $r)) {
305 | $off = $r+1;
306 | continue;
307 | }
308 | $left = fundleftopen($str, $r);
309 | if($left === false) {
310 | echo "CANT'T FIND LEFT BRACKET FOR ?
";
311 | exit(1);
312 | return false;
313 | }
314 |
315 | //echo "FOR ? we have -> ". $left. "
";
316 | $z = getbetweensymb($str, $left, "(", ")");
317 | if($z === false) {
318 | echo "CANT'T FIND RIGHT BRACKET FOR ?
";
319 | exit(1);
320 | return false;
321 | }
322 | $right = $z['end'] ;
323 | $offx = $r;
324 | while(true) {
325 | $col = mb_strpos($str, ":", $offx);
326 | if($col === false) {
327 | echo "CANT'T FIND COLON FOR ?
";
328 | exit(1);
329 | return false;
330 | }
331 | if($colon >= $right) {
332 | echo "CANT'T FIND COLON FOR ?
";
333 | exit(1);
334 | return false;
335 | }
336 | if(inbrackets($str,$col)) {
337 | $offx = $col+1;
338 | continue;
339 | }
340 | break;
341 | }
342 |
343 |
344 | $condition = mb_substr($str, $left+1, $r - ($left + 1));
345 | $iftrue = mb_substr($str, $r+1, $col - ($r + 1));
346 | $iffalse = mb_substr($str, $col+1, ($right-1) - ($col + 1));
347 |
348 | $str = mb_substr($str, 0, $left+1) . "(".$iftrue ." if " . $condition . " else ". $iffalse .")" . mb_substr($str, $right-1);
349 | //$off = $right;
350 |
351 | echo " AFTER: $str
";
352 |
353 | }
354 | return $str;
355 | }
356 |
357 |
358 | function str_replace_safequote($what, $with, $text) {
359 | $off = 0;
360 | while(($r = mb_strpos($text, $what, $off)) !== false) {
361 | if(inbrackets($text, $r)) {
362 | $off = $r+mb_strlen($what);
363 | continue;
364 | }
365 | $text = mb_substr($text, 0, $r) . $with . mb_substr($text, $r + mb_strlen($what));
366 |
367 | }
368 |
369 | return $text;
370 | }
371 |
372 | function str_replace_safequote2($what, $with, $text) {
373 | $off = 0;
374 | while(($r = mb_strpos($text, $what, $off)) !== false) {
375 | if(inbrackets($text, $r)) {
376 | $off = $r+mb_strlen($what);
377 | continue;
378 | }
379 | $text = mb_substr($text, 0, $r) . $with . mb_substr($text, $r + mb_strlen($what));
380 | $off = $r + mb_strlen($with);
381 |
382 | }
383 |
384 | return $text;
385 | }
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 | function utf162utf8($utf16)
398 | {
399 | if(!function_exists('mb_convert_encoding')) {
400 | return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
401 | }
402 |
403 | $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
404 |
405 | switch(true) {
406 | case ((0x7F & $bytes) == $bytes):
407 | return chr(0x7F & $bytes);
408 |
409 | case (0x07FF & $bytes) == $bytes:
410 | return chr(0xC0 | (($bytes >> 6) & 0x1F))
411 | . chr(0x80 | ($bytes & 0x3F));
412 |
413 | case (0xFFFF & $bytes) == $bytes:
414 | return chr(0xE0 | (($bytes >> 12) & 0x0F))
415 | . chr(0x80 | (($bytes >> 6) & 0x3F))
416 | . chr(0x80 | ($bytes & 0x3F));
417 | }
418 | return '';
419 | }
420 |
421 | function utf82utf16($utf8)
422 | {
423 | if(!function_exists('mb_convert_encoding')) {
424 | return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
425 | }
426 |
427 | switch(strlen($utf8)) {
428 | case 1:
429 | return $utf8;
430 |
431 | case 2:
432 | return chr(0x07 & (ord($utf8{0}) >> 2))
433 | . chr((0xC0 & (ord($utf8{0}) << 6))
434 | | (0x3F & ord($utf8{1})));
435 |
436 | case 3:
437 | return chr((0xF0 & (ord($utf8{0}) << 4))
438 | | (0x0F & (ord($utf8{1}) >> 2)))
439 | . chr((0xC0 & (ord($utf8{1}) << 6))
440 | | (0x7F & ord($utf8{2})));
441 | }
442 |
443 | return '';
444 | }
445 |
446 |
447 |
448 | function python_pair($key, $val)
449 | {
450 | $jsonval = python_encode($val);
451 | return python_encode(strval($key)) . ':' . $jsonval;
452 | }
453 |
454 |
455 | function python_encode($var)
456 | {
457 | switch (gettype($var)) {
458 | case 'boolean':
459 | return $var ? 'True' : 'False';
460 |
461 | case 'NULL':
462 | return 'null';
463 |
464 | case 'integer':
465 | return (int) $var;
466 |
467 | case 'double':
468 | case 'float':
469 | return (float) $var;
470 |
471 | case 'string':
472 |
473 | $ascii = '';
474 | $strlen_var = strlen($var);
475 | return 'u"'.addslashes($var).'"';
476 |
477 | case 'array':
478 |
479 | if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
480 | $properties = array_map('python_pair', array_keys($var), array_values($var));
481 | return '{' . implode(',', $properties) . '}';
482 | }
483 | $elements = array_map('python_encode', $var);
484 | return '[' . implode(',', $elements) . ']';
485 |
486 | case 'object':
487 | $vars = get_object_vars($var);
488 | $properties = array_map('python_pair',array_keys($vars),array_values($vars));
489 | return '{' . implode(',', $properties) . '}';
490 |
491 | default:
492 | return 'null';
493 | }
494 | }
495 |
496 |
497 | ?>
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "emuravjev/mdash",
3 | "description": "EMT - Evgeny Muravjev Typograph",
4 | "license": "Public Domain",
5 | "authors": [
6 | {
7 | "name": "Evgeny Muravjev",
8 | "email": "emuravjev@gmail.com"
9 | }
10 | ],
11 | "autoload": {
12 | "classmap": ["src-php/"]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/lib/EMT.Test.php:
--------------------------------------------------------------------------------
1 | _class;
19 | $typograph->set_tag_layout($layout);
20 | $ok = true;
21 | if(isset($test['safetags'])) {
22 | $safetags = array();
23 | if(is_string($test['safetags'])) $safetags = array($test['safetags']);
24 | else $safetags = $test['safetags'];
25 | foreach($safetags as $st) {
26 | $typograph->add_safe_tag($st);
27 | }
28 | }
29 | if(isset($test['params']) && is_array($test['params'])) $typograph->setup($test['params']);
30 |
31 | /*
32 | if(isset($test['params']['map']) || isset($test['params']['maps']))
33 | {
34 | if(isset($test['params']['map']))
35 | {
36 | $ret['map'] = $test['params']['map'];
37 | $ret['disable'] = $test['params']['map_disable'];
38 | $ret['strict'] = $test['params']['map_strict'];
39 | $test['params']['maps'] = array($ret);
40 | }
41 | if(is_array($test['params']['maps']))
42 | {
43 | foreach($test['params']['maps'] as $map)
44 | {
45 | $typograph->set_enable_map
46 | ($map['map'],
47 | isset($map['disable']) ? $map['disable'] : false,
48 | isset($map['strict']) ? $map['strict'] : false
49 | );
50 | }
51 | }
52 | } else {
53 | //if(isset($test['params']['ctl']))
54 | if(isset($test['params']['no_paragraph']) && $test['params']['no_paragraph'])
55 | {
56 | $typograph->get_tret('Etc')->disable_rule('paragraphs');
57 | }
58 | }*/
59 | $typograph->set_text($test['text']);
60 | return $typograph->apply();
61 | }
62 |
63 |
64 | protected function dotest($test)
65 | {
66 | $result = $this->_dotest($test, 1); // STYLE
67 | $ret['result'] = $result;
68 |
69 | if($test['result_classes'])
70 | {
71 | $result = $this->_dotest($test, 2); // CLASSES
72 | $ret['result_classes'] = $result;
73 | }
74 |
75 | $ret['error'] = false;
76 | if($ret['result'] !== $test['result']) $ret['error'] = true;
77 | if($test['result_classes']) if($ret['result_classes'] !== $test['result_classes']) $ret['error'] = true;
78 |
79 | if(!$ret['error'])
80 | {
81 | // повторное типографирование оттипографированного текста
82 | if($this->double_test)
83 | {
84 | $test['text'] = $ret['result'];
85 | $ret['result_second'] = $this->_dotest($test, 1); // STYLE
86 | if($test['result_classes'])
87 | {
88 | $test['text'] = $ret['result_classes'];
89 | $ret['result_classes_second'] = $this->_dotest($test, 2); // CLASSES
90 | }
91 |
92 | // повторное тестирование
93 | if($ret['result_second'] !== $test['result']) $ret['error'] = true;
94 | if($test['result_classes']) if($ret['result_classes_second'] !== $test['result_classes']) $ret['error'] = true;
95 | }
96 |
97 |
98 | }
99 |
100 |
101 | return $ret;
102 | }
103 |
104 | /**
105 | * Установить имя типографа класс
106 | *
107 | * @param string $class
108 | */
109 | public function set_typoclass($class)
110 | {
111 | $this->_class = $class;
112 | }
113 |
114 | /**
115 | * Установить группу тестов
116 | *
117 | * @param string $id
118 | * @param string $title
119 | */
120 | public function set_group($id, $title)
121 | {
122 | $this->grid = $id;
123 | $this->grtitle = $title;
124 | }
125 |
126 |
127 | /**
128 | * Добавить тест
129 | *
130 | * @param string $text - тестируемый тест
131 | * @param string $result - результат, который должен получится
132 | * @param string $id - Идентификатор теста или название теста, если массив, то оба
133 | * @param array/null $params - параметра (то что надо отключить/включить) и прочее, информация о тесте
134 | */
135 | public function add_test($text, $result, $result_classes = null, $id = null, $params = null, $safetags = null)
136 | {
137 | $arr = array(
138 | 'text' => $text,
139 | 'result' => $result,
140 | 'result_classes' => $result_classes,
141 | );
142 | if($params) $arr['params'] = $params;
143 | if($safetags) $arr['safetags'] = $safetags;
144 | if($id)
145 | {
146 | if(is_array($id))
147 | {
148 | if(isset($id['title'])) $arr['title'] = $id['title'];
149 | if(isset($id['id'])) $arr['id'] = $id['id'];
150 | }
151 | if(is_string($id)) $arr['title'] = $id;
152 | }
153 | $arr['grid'] = $this->grid;
154 | $arr['grtitle'] = $this->grtitle;
155 | $this->list[$this->count] = $arr;
156 | $this->count++;
157 | }
158 |
159 |
160 |
161 | /**
162 | * Получить информацию об тесте
163 | *
164 | *
165 | * @param int $num
166 | * @return array
167 | * 'text' - входной текст
168 | * 'result' - то, что должно получится
169 | * 'params' - переданые параметры
170 | */
171 | public function get_test_info($num)
172 | {
173 | if($num >= $this->count) return false;
174 | return $this->list[$num];
175 | }
176 |
177 | /**
178 | * Получить количество тестов
179 | *
180 | * @return int
181 | */
182 | public function get_test_count()
183 | {
184 | return $this->count;
185 | }
186 |
187 |
188 | /**
189 | * Протестировать типограф
190 | *
191 | * @return unknown
192 | */
193 | public function testit()
194 | {
195 | if(count($this->list)==0) return true;
196 | $this->info = array();
197 | $this->results = array();
198 | if(!$this->_class) {
199 | $this->results['error'] = "Не задан класс типографа";
200 | return false;
201 | }
202 | $this->results['raw'] = array();
203 | $this->results['errors'] = array();
204 | $num = 0;
205 | $num2 = 0;
206 | $prevgr = "A";
207 | foreach($this->list as $test)
208 | {
209 | if($test['grid']!=$prevgr) $num2=0;
210 | $prevgr = $test['grid'];
211 | $num++;
212 | $num2++;
213 | $ret = $this->dotest($test);
214 | $test['grnum'] = $num2;
215 | $this->results['raw'][$num] = $ret;
216 | if($ret['error'])
217 | {
218 | $this->results['errors'][] = array(
219 | 'num' => $num,
220 | );
221 | }
222 | $this->on_tested($num, $test, $ret);
223 | }
224 | if(count($this->results['errors'])>0) return false;
225 | return true;
226 | }
227 |
228 | /**
229 | * То случается при обработке
230 | *
231 | * @param ште $num
232 | * @param array $test
233 | * @param array $ret
234 | */
235 | public function on_tested($num, $test, $ret)
236 | {
237 |
238 | }
239 |
240 | }
241 |
242 |
243 | ?>
--------------------------------------------------------------------------------
/lib/external/finediff.php:
--------------------------------------------------------------------------------
1 | copy->insert
78 | * command (swap) for when the inserted segment is exactly the same
79 | * as the deleted one, and with only a copy operation in between.
80 | * TODO: How often this case occurs? Is it worth it? Can only
81 | * be done as a postprocessing method (->optimize()?)
82 | */
83 | abstract class FineDiffOp {
84 | abstract public function getFromLen();
85 | abstract public function getToLen();
86 | abstract public function getOpcode();
87 | }
88 |
89 | class FineDiffDeleteOp extends FineDiffOp {
90 | public function __construct($len) {
91 | $this->fromLen = $len;
92 | }
93 |
94 | public function getFromLen() {
95 | return $this->fromLen;
96 | }
97 |
98 | public function getToLen() {
99 | return 0;
100 | }
101 |
102 | public function getOpcode() {
103 | if ( $this->fromLen === 1 ) {
104 | return 'd';
105 | }
106 | return "d{$this->fromLen}";
107 | }
108 | }
109 |
110 | class FineDiffInsertOp extends FineDiffOp {
111 | public function __construct($text) {
112 | $this->text = $text;
113 | }
114 |
115 | public function getFromLen() {
116 | return 0;
117 | }
118 |
119 | public function getToLen() {
120 | return mb_strlen($this->text);
121 | }
122 |
123 | public function getText() {
124 | return $this->text;
125 | }
126 |
127 | public function getOpcode() {
128 | $to_len = mb_strlen($this->text);
129 | if ( $to_len === 1 ) {
130 | return "i:{$this->text}";
131 | }
132 | return "i{$to_len}:{$this->text}";
133 | }
134 | }
135 |
136 | class FineDiffReplaceOp extends FineDiffOp {
137 | public function __construct($fromLen, $text) {
138 | $this->fromLen = $fromLen;
139 | $this->text = $text;
140 | }
141 |
142 | public function getFromLen() {
143 | return $this->fromLen;
144 | }
145 |
146 | public function getToLen() {
147 | return mb_strlen($this->text);
148 | }
149 |
150 | public function getText() {
151 | return $this->text;
152 | }
153 |
154 | public function getOpcode() {
155 | if ( $this->fromLen === 1 ) {
156 | $del_opcode = 'd';
157 | }
158 | else {
159 | $del_opcode = "d{$this->fromLen}";
160 | }
161 | $to_len = mb_strlen($this->text);
162 | if ( $to_len === 1 ) {
163 | return "{$del_opcode}i:{$this->text}";
164 | }
165 | return "{$del_opcode}i{$to_len}:{$this->text}";
166 | }
167 | }
168 |
169 | class FineDiffCopyOp extends FineDiffOp {
170 | public function __construct($len) {
171 | $this->len = $len;
172 | }
173 |
174 | public function getFromLen() {
175 | return $this->len;
176 | }
177 |
178 | public function getToLen() {
179 | return $this->len;
180 | }
181 |
182 | public function getOpcode() {
183 | if ( $this->len === 1 ) {
184 | return 'c';
185 | }
186 | return "c{$this->len}";
187 | }
188 |
189 | public function increase($size) {
190 | return $this->len += $size;
191 | }
192 | }
193 |
194 | /**
195 | * FineDiff ops
196 | *
197 | * Collection of ops
198 | */
199 | class FineDiffOps {
200 | public $edits = array();
201 |
202 | public function appendOpcode($opcode, $from, $from_offset, $from_len) {
203 | if ( $opcode === 'c' ) {
204 | $edits[] = new FineDiffCopyOp($from_len);
205 | }
206 | else if ( $opcode === 'd' ) {
207 | $edits[] = new FineDiffDeleteOp($from_len);
208 | }
209 | else /* if ( $opcode === 'i' ) */ {
210 | $edits[] = new FineDiffInsertOp(mb_substr($from, $from_offset, $from_len));
211 | }
212 | }
213 | }
214 |
215 | /**
216 | * FineDiff class
217 | *
218 | * TODO: Document
219 | *
220 | */
221 | class FineDiff {
222 | /**
223 | * Constructor
224 | * ...
225 | * The $granularityStack allows FineDiff to be configurable so that
226 | * a particular stack tailored to the specific content of a document can
227 | * be passed.
228 | */
229 | public function __construct($from_text = '', $to_text = '', $granularityStack = null) {
230 | // setup stack for generic text documents by default
231 | $this->granularityStack = $granularityStack ? $granularityStack : self::$characterGranularity;
232 | $this->edits = array();
233 | $this->from_text = $from_text;
234 | $this->doDiff($from_text, $to_text);
235 | }
236 |
237 | public function getOps() {
238 | return $this->edits;
239 | }
240 |
241 | public function getOpcodes() {
242 | $opcodes = array();
243 | foreach ( $this->edits as $edit ) {
244 | $opcodes[] = $edit->getOpcode();
245 | }
246 | return implode('', $opcodes);
247 | }
248 |
249 | public function renderDiffToHTML() {
250 | $in_offset = 0;
251 | ob_start();
252 | foreach ( $this->edits as $edit ) {
253 | $n = $edit->getFromLen();
254 | if ( $edit instanceof FineDiffCopyOp ) {
255 | self::renderDiffToHTMLFromOpcode('c', $this->from_text, $in_offset, $n);
256 | }
257 | else if ( $edit instanceof FineDiffDeleteOp ) {
258 | self::renderDiffToHTMLFromOpcode('d', $this->from_text, $in_offset, $n);
259 | }
260 | else if ( $edit instanceof FineDiffInsertOp ) {
261 | self::renderDiffToHTMLFromOpcode('i', $edit->getText(), 0, $edit->getToLen());
262 | }
263 | else /* if ( $edit instanceof FineDiffReplaceOp ) */ {
264 | self::renderDiffToHTMLFromOpcode('d', $this->from_text, $in_offset, $n);
265 | self::renderDiffToHTMLFromOpcode('i', $edit->getText(), 0, $edit->getToLen());
266 | }
267 | $in_offset += $n;
268 | }
269 | return ob_get_clean();
270 | }
271 |
272 | public function renderDiffToHTML2() {
273 | $in_offset = 0;
274 | ob_start();
275 | foreach ( $this->edits as $edit ) {
276 | $n = $edit->getFromLen();
277 | if ( $edit instanceof FineDiffCopyOp ) {
278 | self::renderDiffToHTMLFromOpcode2('c', $this->from_text, $in_offset, $n);
279 | }
280 | else if ( $edit instanceof FineDiffDeleteOp ) {
281 | self::renderDiffToHTMLFromOpcode2('d', $this->from_text, $in_offset, $n);
282 | }
283 | else if ( $edit instanceof FineDiffInsertOp ) {
284 | self::renderDiffToHTMLFromOpcode2('i', $edit->getText(), 0, $edit->getToLen());
285 | }
286 | else /* if ( $edit instanceof FineDiffReplaceOp ) */ {
287 | self::renderDiffToHTMLFromOpcode2('d', $this->from_text, $in_offset, $n);
288 | self::renderDiffToHTMLFromOpcode2('i', $edit->getText(), 0, $edit->getToLen());
289 | }
290 | $in_offset += $n;
291 | }
292 | return ob_get_clean();
293 | }
294 |
295 | /**------------------------------------------------------------------------
296 | * Return an opcodes string describing the diff between a "From" and a
297 | * "To" string
298 | */
299 | public static function getDiffOpcodes($from, $to, $granularities = null) {
300 | $diff = new self($from, $to, $granularities);
301 | return $diff->getOpcodes();
302 | }
303 |
304 | /**------------------------------------------------------------------------
305 | * Return an iterable collection of diff ops from an opcodes string
306 | */
307 | public static function getDiffOpsFromOpcodes($opcodes) {
308 | $diffops = new FineDiffOps();
309 | self::renderFromOpcodes(null, $opcodes, array($diffops,'appendOpcode'));
310 | return $diffops->edits;
311 | }
312 |
313 | /**------------------------------------------------------------------------
314 | * Re-create the "To" string from the "From" string and an "Opcodes" string
315 | */
316 | public static function renderToTextFromOpcodes($from, $opcodes) {
317 | ob_start();
318 | self::renderFromOpcodes($from, $opcodes, array('FineDiff','renderToTextFromOpcode'));
319 | return ob_get_clean();
320 | }
321 |
322 | /**
323 | * Render the diff to an HTML string
324 | */
325 | public static function renderDiffToHTMLFromOpcodes($from, $opcodes) {
326 | ob_start();
327 | self::renderFromOpcodes($from, $opcodes, array('FineDiff', 'renderDiffToHTMLFromOpcode'));
328 | return ob_get_clean();
329 | }
330 |
331 | /**
332 | * Generic opcodes parser, user must supply callback for handling
333 | * single opcode
334 | */
335 | public static function renderFromOpcodes($from, $opcodes, $callback) {
336 | if ( !is_callable($callback) ) {
337 | return;
338 | }
339 | $opcodes_len = mb_strlen($opcodes);
340 | $from_offset = $opcodes_offset = 0;
341 | while ( $opcodes_offset < $opcodes_len ) {
342 | $opcode = mb_substr($opcodes, $opcodes_offset, 1);
343 | $opcodes_offset++;
344 | $n = intval(mb_substr($opcodes, $opcodes_offset));
345 | if ( $n ) {
346 | $opcodes_offset += mb_strlen(strval($n));
347 | }
348 | else {
349 | $n = 1;
350 | }
351 | if ( $opcode === 'c' ) { // copy n characters from source
352 | call_user_func($callback, 'c', $from, $from_offset, $n, '');
353 | $from_offset += $n;
354 | }
355 | else if ( $opcode === 'd' ) { // delete n characters from source
356 | call_user_func($callback, 'd', $from, $from_offset, $n, '');
357 | $from_offset += $n;
358 | }
359 | else /* if ( $opcode === 'i' ) */ { // insert n characters from opcodes
360 | call_user_func($callback, 'i', $opcodes, $opcodes_offset + 1, $n);
361 | $opcodes_offset += 1 + $n;
362 | }
363 | }
364 | }
365 |
366 | /**
367 | * Stock granularity stacks and delimiters
368 | */
369 |
370 | const paragraphDelimiters = "\n\r";
371 | public static $paragraphGranularity = array(
372 | FineDiff::paragraphDelimiters
373 | );
374 | const sentenceDelimiters = ".\n\r";
375 | public static $sentenceGranularity = array(
376 | FineDiff::paragraphDelimiters,
377 | FineDiff::sentenceDelimiters
378 | );
379 | const wordDelimiters = " \t.\n\r";
380 | public static $wordGranularity = array(
381 | FineDiff::paragraphDelimiters,
382 | FineDiff::sentenceDelimiters,
383 | FineDiff::wordDelimiters
384 | );
385 | const characterDelimiters = "";
386 | public static $characterGranularity = array(
387 | FineDiff::paragraphDelimiters,
388 | FineDiff::sentenceDelimiters,
389 | FineDiff::wordDelimiters,
390 | FineDiff::characterDelimiters
391 | );
392 |
393 | public static $textStack = array(
394 | ".",
395 | " \t.\n\r",
396 | ""
397 | );
398 |
399 | /**
400 | * Entry point to compute the diff.
401 | */
402 | private function doDiff($from_text, $to_text) {
403 | $this->last_edit = false;
404 | $this->stackpointer = 0;
405 | $this->from_text = $from_text;
406 | $this->from_offset = 0;
407 | // can't diff without at least one granularity specifier
408 | if ( empty($this->granularityStack) ) {
409 | return;
410 | }
411 | $this->_processGranularity($from_text, $to_text);
412 | }
413 |
414 | /**
415 | * This is the recursive function which is responsible for
416 | * handling/increasing granularity.
417 | *
418 | * Incrementally increasing the granularity is key to compute the
419 | * overall diff in a very efficient way.
420 | */
421 | private function _processGranularity($from_segment, $to_segment) {
422 | $delimiters = $this->granularityStack[$this->stackpointer++];
423 | $has_next_stage = $this->stackpointer < count($this->granularityStack);
424 | foreach ( FineDiff::doFragmentDiff($from_segment, $to_segment, $delimiters) as $fragment_edit ) {
425 | // increase granularity
426 | if ( $fragment_edit instanceof FineDiffReplaceOp && $has_next_stage ) {
427 | $this->_processGranularity(
428 | mb_substr($this->from_text, $this->from_offset, $fragment_edit->getFromLen()),
429 | $fragment_edit->getText()
430 | );
431 | }
432 | // fuse copy ops whenever possible
433 | else if ( $fragment_edit instanceof FineDiffCopyOp && $this->last_edit instanceof FineDiffCopyOp ) {
434 | $this->edits[count($this->edits)-1]->increase($fragment_edit->getFromLen());
435 | $this->from_offset += $fragment_edit->getFromLen();
436 | }
437 | else {
438 | /* $fragment_edit instanceof FineDiffCopyOp */
439 | /* $fragment_edit instanceof FineDiffDeleteOp */
440 | /* $fragment_edit instanceof FineDiffInsertOp */
441 | $this->edits[] = $this->last_edit = $fragment_edit;
442 | $this->from_offset += $fragment_edit->getFromLen();
443 | }
444 | }
445 | $this->stackpointer--;
446 | }
447 |
448 | /**
449 | * This is the core algorithm which actually perform the diff itself,
450 | * fragmenting the strings as per specified delimiters.
451 | *
452 | * This function is naturally recursive, however for performance purpose
453 | * a local job queue is used instead of outright recursivity.
454 | */
455 | private static function doFragmentDiff($from_text, $to_text, $delimiters) {
456 | // Empty delimiter means character-level diffing.
457 | // In such case, use code path optimized for character-level
458 | // diffing.
459 | if ( empty($delimiters) ) {
460 | return FineDiff::doCharDiff($from_text, $to_text);
461 | }
462 |
463 | $result = array();
464 |
465 | // fragment-level diffing
466 | $from_text_len = mb_strlen($from_text);
467 | $to_text_len = mb_strlen($to_text);
468 | $from_fragments = FineDiff::extractFragments($from_text, $delimiters);
469 | $to_fragments = FineDiff::extractFragments($to_text, $delimiters);
470 |
471 | $jobs = array(array(0, $from_text_len, 0, $to_text_len));
472 |
473 | $cached_array_keys = array();
474 |
475 | while ( $job = array_pop($jobs) ) {
476 |
477 | // get the segments which must be diff'ed
478 | list($from_segment_start, $from_segment_end, $to_segment_start, $to_segment_end) = $job;
479 |
480 | // catch easy cases first
481 | $from_segment_length = $from_segment_end - $from_segment_start;
482 | $to_segment_length = $to_segment_end - $to_segment_start;
483 | if ( !$from_segment_length || !$to_segment_length ) {
484 | if ( $from_segment_length ) {
485 | $result[$from_segment_start * 4] = new FineDiffDeleteOp($from_segment_length);
486 | }
487 | else if ( $to_segment_length ) {
488 | $result[$from_segment_start * 4 + 1] = new FineDiffInsertOp(mb_substr($to_text, $to_segment_start, $to_segment_length));
489 | }
490 | continue;
491 | }
492 |
493 | // find longest copy operation for the current segments
494 | $best_copy_length = 0;
495 |
496 | $from_base_fragment_index = $from_segment_start;
497 |
498 | $cached_array_keys_for_current_segment = array();
499 |
500 | while ( $from_base_fragment_index < $from_segment_end ) {
501 | $from_base_fragment = $from_fragments[$from_base_fragment_index];
502 | $from_base_fragment_length = mb_strlen($from_base_fragment);
503 | // performance boost: cache array keys
504 | if ( !isset($cached_array_keys_for_current_segment[$from_base_fragment]) ) {
505 | if ( !isset($cached_array_keys[$from_base_fragment]) ) {
506 | $to_all_fragment_indices = $cached_array_keys[$from_base_fragment] = array_keys($to_fragments, $from_base_fragment, true);
507 | }
508 | else {
509 | $to_all_fragment_indices = $cached_array_keys[$from_base_fragment];
510 | }
511 | // get only indices which falls within current segment
512 | if ( $to_segment_start > 0 || $to_segment_end < $to_text_len ) {
513 | $to_fragment_indices = array();
514 | foreach ( $to_all_fragment_indices as $to_fragment_index ) {
515 | if ( $to_fragment_index < $to_segment_start ) { continue; }
516 | if ( $to_fragment_index >= $to_segment_end ) { break; }
517 | $to_fragment_indices[] = $to_fragment_index;
518 | }
519 | $cached_array_keys_for_current_segment[$from_base_fragment] = $to_fragment_indices;
520 | }
521 | else {
522 | $to_fragment_indices = $to_all_fragment_indices;
523 | }
524 | }
525 | else {
526 | $to_fragment_indices = $cached_array_keys_for_current_segment[$from_base_fragment];
527 | }
528 | // iterate through collected indices
529 | foreach ( $to_fragment_indices as $to_base_fragment_index ) {
530 | $fragment_index_offset = $from_base_fragment_length;
531 | // iterate until no more match
532 | for (;;) {
533 | $fragment_from_index = $from_base_fragment_index + $fragment_index_offset;
534 | if ( $fragment_from_index >= $from_segment_end ) {
535 | break;
536 | }
537 | $fragment_to_index = $to_base_fragment_index + $fragment_index_offset;
538 | if ( $fragment_to_index >= $to_segment_end ) {
539 | break;
540 | }
541 | if ( $from_fragments[$fragment_from_index] !== $to_fragments[$fragment_to_index] ) {
542 | break;
543 | }
544 | $fragment_length = mb_strlen($from_fragments[$fragment_from_index]);
545 | $fragment_index_offset += $fragment_length;
546 | }
547 | if ( $fragment_index_offset > $best_copy_length ) {
548 | $best_copy_length = $fragment_index_offset;
549 | $best_from_start = $from_base_fragment_index;
550 | $best_to_start = $to_base_fragment_index;
551 | }
552 | }
553 | $from_base_fragment_index += mb_strlen($from_base_fragment);
554 | // If match is larger than half segment size, no point trying to find better
555 | // TODO: Really?
556 | if ( $best_copy_length >= $from_segment_length / 2) {
557 | break;
558 | }
559 | // no point to keep looking if what is left is less than
560 | // current best match
561 | if ( $from_base_fragment_index + $best_copy_length >= $from_segment_end ) {
562 | break;
563 | }
564 | }
565 |
566 | if ( $best_copy_length ) {
567 | $jobs[] = array($from_segment_start, $best_from_start, $to_segment_start, $best_to_start);
568 | $result[$best_from_start * 4 + 2] = new FineDiffCopyOp($best_copy_length);
569 | $jobs[] = array($best_from_start + $best_copy_length, $from_segment_end, $best_to_start + $best_copy_length, $to_segment_end);
570 | }
571 | else {
572 | $result[$from_segment_start * 4 ] = new FineDiffReplaceOp($from_segment_length, mb_substr($to_text, $to_segment_start, $to_segment_length));
573 | }
574 | }
575 |
576 | ksort($result, SORT_NUMERIC);
577 | return array_values($result);
578 | }
579 |
580 | /**
581 | * Perform a character-level diff.
582 | *
583 | * The algorithm is quite similar to doFragmentDiff(), except that
584 | * the code path is optimized for character-level diff -- strpos() is
585 | * used to find out the longest common subequence of characters.
586 | *
587 | * We try to find a match using the longest possible subsequence, which
588 | * is at most the length of the shortest of the two strings, then incrementally
589 | * reduce the size until a match is found.
590 | *
591 | * I still need to study more the performance of this function. It
592 | * appears that for long strings, the generic doFragmentDiff() is more
593 | * performant. For word-sized strings, doCharDiff() is somewhat more
594 | * performant.
595 | */
596 | private static function doCharDiff($from_text, $to_text) {
597 | $result = array();
598 | $jobs = array(array(0, mb_strlen($from_text), 0, mb_strlen($to_text)));
599 | while ( $job = array_pop($jobs) ) {
600 | // get the segments which must be diff'ed
601 | list($from_segment_start, $from_segment_end, $to_segment_start, $to_segment_end) = $job;
602 | $from_segment_len = $from_segment_end - $from_segment_start;
603 | $to_segment_len = $to_segment_end - $to_segment_start;
604 |
605 | // catch easy cases first
606 | if ( !$from_segment_len || !$to_segment_len ) {
607 | if ( $from_segment_len ) {
608 | $result[$from_segment_start * 4 + 0] = new fineDiffDeleteOp($from_segment_len);
609 | }
610 | else if ( $to_segment_len ) {
611 | $result[$from_segment_start * 4 + 1] = new fineDiffInsertOp(mb_substr($to_text, $to_segment_start, $to_segment_len));
612 | }
613 | continue;
614 | }
615 | if ( $from_segment_len >= $to_segment_len ) {
616 | $copy_len = $to_segment_len;
617 | while ( $copy_len ) {
618 | $to_copy_start = $to_segment_start;
619 | $to_copy_start_max = $to_segment_end - $copy_len;
620 | while ( $to_copy_start <= $to_copy_start_max ) {
621 | $from_copy_start = mb_strpos(mb_substr($from_text, $from_segment_start, $from_segment_len), mb_substr($to_text, $to_copy_start, $copy_len));
622 | if ( $from_copy_start !== false ) {
623 | $from_copy_start += $from_segment_start;
624 | break 2;
625 | }
626 | $to_copy_start++;
627 | }
628 | $copy_len--;
629 | }
630 | }
631 | else {
632 | $copy_len = $from_segment_len;
633 | while ( $copy_len ) {
634 | $from_copy_start = $from_segment_start;
635 | $from_copy_start_max = $from_segment_end - $copy_len;
636 | while ( $from_copy_start <= $from_copy_start_max ) {
637 | $to_copy_start = mb_strpos(mb_substr($to_text, $to_segment_start, $to_segment_len), mb_substr($from_text, $from_copy_start, $copy_len));
638 | if ( $to_copy_start !== false ) {
639 | $to_copy_start += $to_segment_start;
640 | break 2;
641 | }
642 | $from_copy_start++;
643 | }
644 | $copy_len--;
645 | }
646 | }
647 | // match found
648 | if ( $copy_len ) {
649 | $jobs[] = array($from_segment_start, $from_copy_start, $to_segment_start, $to_copy_start);
650 | $result[$from_copy_start * 4 + 2] = new FineDiffCopyOp($copy_len);
651 | $jobs[] = array($from_copy_start + $copy_len, $from_segment_end, $to_copy_start + $copy_len, $to_segment_end);
652 | }
653 | // no match, so delete all, insert all
654 | else {
655 | $result[$from_segment_start * 4] = new FineDiffReplaceOp($from_segment_len, mb_substr($to_text, $to_segment_start, $to_segment_len));
656 | }
657 | }
658 | ksort($result, SORT_NUMERIC);
659 | return array_values($result);
660 | }
661 |
662 | /**
663 | * Efficiently fragment the text into an array according to
664 | * specified delimiters.
665 | * No delimiters means fragment into single character.
666 | * The array indices are the offset of the fragments into
667 | * the input string.
668 | * A sentinel empty fragment is always added at the end.
669 | * Careful: No check is performed as to the validity of the
670 | * delimiters.
671 | */
672 | private static function extractFragments($text, $delimiters) {
673 | // special case: split into characters
674 | if ( empty($delimiters) ) {
675 | $chars = self::splitToChars($text);
676 | $chars[] = '';
677 | return $chars;
678 | }
679 | $fragments = array();
680 | $start = $end = 0;
681 | for (;;) {
682 | $end += self::mb_strcspn($text, $delimiters, $end);
683 | $end += self::mb_strspn($text, $delimiters, $end);
684 | if ( $end === $start ) {
685 | break;
686 | }
687 | $fragments[$start] = mb_substr($text, $start, $end - $start);
688 | $start = $end;
689 | }
690 | $fragments[$start] = '';
691 | return $fragments;
692 | }
693 |
694 | /**
695 | * Stock opcode renderers
696 | */
697 | private static function renderToTextFromOpcode($opcode, $from, $from_offset, $from_len) {
698 | if ( $opcode === 'c' || $opcode === 'i' ) {
699 | echo mb_substr($from, $from_offset, $from_len);
700 | }
701 | }
702 |
703 | private static function renderDiffToHTMLFromOpcode($opcode, $from, $from_offset, $from_len) {
704 | if ( $opcode === 'c' ) {
705 | echo htmlspecialchars(mb_substr($from, $from_offset, $from_len));
706 | }
707 | else if ( $opcode === 'd' ) {
708 | $deletion = mb_substr($from, $from_offset, $from_len);
709 | if ( strcspn($deletion, " \n\r") === 0 ) { // no mb_ here is okay
710 | $deletion = str_replace(array("\n","\r"), array('\n','\r'), $deletion);
711 | }
712 | echo '', htmlspecialchars($deletion), '';
713 | }
714 | else /* if ( $opcode === 'i' ) */ {
715 | echo '', htmlspecialchars(mb_substr($from, $from_offset, $from_len), ENT_QUOTES), '';
716 | }
717 | }
718 |
719 | private static function renderDiffToHTMLFromOpcode2($opcode, $from, $from_offset, $from_len) {
720 | if ( $opcode === 'c' ) {
721 | echo mb_substr($from, $from_offset, $from_len);
722 | }
723 | else if ( $opcode === 'd' ) {
724 | $deletion = mb_substr($from, $from_offset, $from_len);
725 | if ( strcspn($deletion, " \n\r") === 0 ) { // no mb_ here is okay
726 | $deletion = str_replace(array("\n","\r"), array('\n','\r'), $deletion);
727 | }
728 | echo '', $deletion, '';
729 | }
730 | else /* if ( $opcode === 'i' ) */ {
731 | echo '', mb_substr($from, $from_offset, $from_len), '';
732 | }
733 | }
734 |
735 | private static function splitToChars($str) {
736 | preg_match_all('/./us', $str, $matches);
737 | $matches = $matches[0];
738 |
739 | if (count($matches) === 0) return array('');
740 | return $matches;
741 | }
742 |
743 | private static function mb_strcspn($str, $delimiters, $start) {
744 | $dels = self::splitToChars($delimiters);
745 | $min = mb_strlen($str);
746 |
747 | foreach ($dels as $del) {
748 | $pos = mb_strpos($str, $del, $start);
749 | if ($pos !== false && $pos < $min) $min = $pos;
750 | }
751 |
752 | return $min - $start;
753 | }
754 |
755 | private static function mb_strspn($str, $delimiters, $start) {
756 | $str = mb_substr($str, $start);
757 | $dels = self::splitToChars($delimiters);
758 |
759 | foreach ($dels as $idx => $del) {
760 | $dels[$idx] = preg_quote($del, '/');
761 | }
762 |
763 | $dels = implode('|', $dels);
764 |
765 | preg_match("/^($dels)+/us", $str, $match);
766 | return $match ? mb_strlen($match[0]) : 0;
767 | }
768 | }
769 |
--------------------------------------------------------------------------------
/lib/fs.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/lib.php:
--------------------------------------------------------------------------------
1 | $v) {
9 | unset($process[$key][$k]);
10 | if (is_array($v)) {
11 | $process[$key][stripslashes($k)] = $v;
12 | $process[] = &$process[$key][stripslashes($k)];
13 | } else {
14 | $process[$key][stripslashes($k)] = stripslashes($v);
15 | }
16 | }
17 | }
18 | unset($process);
19 | }
20 | ?>
--------------------------------------------------------------------------------
/misc/no.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/emuravjev/mdash/cbc6a811b50957272a0025786dc3ad504c9fb222/misc/no.png
--------------------------------------------------------------------------------
/misc/yes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/emuravjev/mdash/cbc6a811b50957272a0025786dc3ad504c9fb222/misc/yes.png
--------------------------------------------------------------------------------
/src-php/EMT.Lib.php:
--------------------------------------------------------------------------------
1 | array('html' => array('«', '»', '”', '‘', '„', '“', '"', '«', '»'),
17 | 'utf8' => array(0x201E, 0x201C, 0x201F, 0x201D, 0x00AB, 0x00BB)),
18 | ' ' => array('html' => array(' ', ' ', ' '),
19 | 'utf8' => array(0x00A0, 0x2002, 0x2003, 0x2008, 0x2009)),
20 | '-' => array('html' => array(/*'—',*/ '–', '−', '', '—', '–'),
21 | 'utf8' => array(0x002D, /*0x2014,*/ 0x2010, 0x2012, 0x2013)),
22 | '—' => array('html' => array('—'),
23 | 'utf8' => array(0x2014)),
24 | '==' => array('html' => array('≡'),
25 | 'utf8' => array(0x2261)),
26 | '...' => array('html' => array('…', '
'),
27 | 'utf8' => array(0x2026)),
28 | '!=' => array('html' => array('≠', '≠'),
29 | 'utf8' => array(0x2260)),
30 | '<=' => array('html' => array('≤', '≤'),
31 | 'utf8' => array(0x2264)),
32 | '>=' => array('html' => array('≥', '≥'),
33 | 'utf8' => array(0x2265)),
34 | '1/2' => array('html' => array('½', '½'),
35 | 'utf8' => array(0x00BD)),
36 | '1/4' => array('html' => array('¼', '¼'),
37 | 'utf8' => array(0x00BC)),
38 | '3/4' => array('html' => array('¾', '¾'),
39 | 'utf8' => array(0x00BE)),
40 | '+-' => array('html' => array('±', '±'),
41 | 'utf8' => array(0x00B1)),
42 | '&' => array('html' => array('&', '&')),
43 | '(tm)' => array('html' => array('™', ''),
44 | 'utf8' => array(0x2122)),
45 | //'(r)' => array('html' => array('®', '®', '®'),
46 | '(r)' => array('html' => array('®', '®'),
47 | 'utf8' => array(0x00AE)),
48 | '(c)' => array('html' => array('©', '©'),
49 | 'utf8' => array(0x00A9)),
50 | '§' => array('html' => array('§', '§'),
51 | 'utf8' => array(0x00A7)),
52 | '`' => array('html' => array('́')),
53 | '\'' => array('html' => array('’', '’')),
54 | 'x' => array('html' => array('×', '×'),
55 | 'utf8' => array('×') /* какой же у него может быть код? */),
56 |
57 | );
58 |
59 | /**
60 | * Добавление к тегам атрибута 'id', благодаря которому
61 | * при повторном типографирование текста будут удалены теги,
62 | * расставленные данным типографом
63 | *
64 | * @var array
65 | */
66 | protected static $_typographSpecificTagId = false;
67 |
68 |
69 | /**
70 | * Костыли для работы с символами UTF-8
71 | *
72 | * @author somebody?
73 | * @param int $c код символа в кодировке UTF-8 (например, 0x00AB)
74 | * @return bool|string
75 | */
76 | public static function _getUnicodeChar($c)
77 | {
78 | if ($c <= 0x7F) {
79 | return chr($c);
80 | } else if ($c <= 0x7FF) {
81 | return chr(0xC0 | $c >> 6)
82 | . chr(0x80 | $c & 0x3F);
83 | } else if ($c <= 0xFFFF) {
84 | return chr(0xE0 | $c >> 12)
85 | . chr(0x80 | $c >> 6 & 0x3F)
86 | . chr(0x80 | $c & 0x3F);
87 | } else if ($c <= 0x10FFFF) {
88 | return chr(0xF0 | $c >> 18)
89 | . chr(0x80 | $c >> 12 & 0x3F)
90 | . chr(0x80 | $c >> 6 & 0x3F)
91 | . chr(0x80 | $c & 0x3F);
92 | } else {
93 | return false;
94 | }
95 | }
96 |
97 |
98 | /**
99 | * Удаление кодов HTML из текста
100 | *
101 | *
102 | * // Remove UTF-8 chars:
103 | * $str = EMT_Lib::clear_special_chars('your text', 'utf8');
104 | * // ... or HTML codes only:
105 | * $str = EMT_Lib::clear_special_chars('your text', 'html');
106 | * // ... or combo:
107 | * $str = EMT_Lib::clear_special_chars('your text');
108 | *
109 | *
110 | * @param string $text
111 | * @param mixed $mode
112 | * @return string|bool
113 | */
114 | public static function clear_special_chars($text, $mode = null)
115 | {
116 | if(is_string($mode)) $mode = array($mode);
117 | if(is_null($mode)) $mode = array('utf8', 'html');
118 | if(!is_array($mode)) return false;
119 | $moder = array();
120 | foreach($mode as $mod) if(in_array($mod, array('utf8','html'))) $moder[] = $mod;
121 | if(count($moder)==0) return false;
122 |
123 | foreach (self::$_charsTable as $char => $vals)
124 | {
125 | foreach ($mode as $type)
126 | {
127 | if (isset($vals[$type]))
128 | {
129 | foreach ($vals[$type] as $v)
130 | {
131 | if ('utf8' === $type && is_int($v))
132 | {
133 | $v = self::_getUnicodeChar($v);
134 | }
135 | if ('html' === $type)
136 | {
137 | if(preg_match("/<[a-z]+>/i",$v))
138 | {
139 | $v = self::safe_tag_chars($v, true);
140 | }
141 | }
142 | $text = str_replace($v, $char, $text);
143 | }
144 | }
145 | }
146 | }
147 |
148 | return $text;
149 | }
150 |
151 | /**
152 | * Удаление тегов HTML из текста
153 | * Тег
будет преобразов в перенос строки \n, сочетание тегов
-
154 | * в двойной перенос
155 | *
156 | * @param string $text
157 | * @param array $allowableTag массив из тегов, которые будут проигнорированы
158 | * @return string
159 | */
160 | public static function remove_html_tags($text, $allowableTag = null)
161 | {
162 | $ignore = null;
163 |
164 | if (null !== $allowableTag)
165 | {
166 | if (is_string($allowableTag))
167 | {
168 | $allowableTag = array($allowableTag);
169 | }
170 | if (is_array($allowableTag))
171 | {
172 | $tags = array();
173 | foreach ($allowableTag as $tag)
174 | {
175 | if ('<' !== substr($tag, 0, 1) || '>' !== substr($tag, -1, 1)) continue;
176 | if ('/' === substr($tag, 1, 1)) continue;
177 | $tags [] = $tag;
178 | }
179 | $ignore = implode('', $tags);
180 | }
181 | }
182 | $text = preg_replace(array('/\
/i', '/\<\/p\>\s*\
/'), array("\n","\n\n"), $text);
183 | $text = strip_tags($text, $ignore);
184 | return $text;
185 | }
186 |
187 | /**
188 | * Сохраняем содержимое тегов HTML
189 | *
190 | * Тег 'a' кодируется со специальным префиксом для дальнейшей
191 | * возможности выносить за него кавычки.
192 | *
193 | * @param string $text
194 | * @param bool $safe
195 | * @return string
196 | */
197 | public static function safe_tag_chars($text, $way)
198 | {
199 | if ($way)
200 | $text = preg_replace_callback('/(\<\/?)([^<>]+?)(\>)/s', EMT_Lib::create_function('$m','return (strlen($m[1])==1 && substr(trim($m[2]), 0, 1) == \'-\' && substr(trim($m[2]), 1, 1) != \'-\')? $m[0] : $m[1].( substr(trim($m[2]), 0, 1) === "a" ? "%%___" : "" ) . EMT_Lib::encrypt_tag(trim($m[2])) . $m[3];'), $text);
201 | else
202 | $text = preg_replace_callback('/(\<\/?)([^<>]+?)(\>)/s', EMT_Lib::create_function('$m','return (strlen($m[1])==1 && substr(trim($m[2]), 0, 1) == \'-\' && substr(trim($m[2]), 1, 1) != \'-\')? $m[0] : $m[1].( substr(trim($m[2]), 0, 3) === "%%___" ? EMT_Lib::decrypt_tag(substr(trim($m[2]), 4)) : EMT_Lib::decrypt_tag(trim($m[2])) ) . $m[3];'), $text);
203 | return $text;
204 | }
205 |
206 |
207 | /**
208 | * Декодриует спец блоки
209 | *
210 | * @param string $text
211 | * @return string
212 | */
213 | public static function decode_internal_blocks($text)
214 | {
215 | $text = preg_replace_callback('/'.EMT_Lib::INTERNAL_BLOCK_OPEN.'([a-zA-Z0-9\/=]+?)'.EMT_Lib::INTERNAL_BLOCK_CLOSE.'/s', EMT_Lib::create_function('$m','return EMT_Lib::decrypt_tag($m[1]);'), $text);
216 | return $text;
217 | }
218 |
219 | /**
220 | * Кодирует спец блок
221 | *
222 | * @param string $text
223 | * @return string
224 | */
225 | public static function iblock($text)
226 | {
227 | return EMT_Lib::INTERNAL_BLOCK_OPEN. EMT_Lib::encrypt_tag($text).EMT_Lib::INTERNAL_BLOCK_CLOSE;
228 | }
229 |
230 |
231 | /**
232 | * Создание тега с защищенным содержимым
233 | *
234 | * @param string $content текст, который будет обрамлен тегом
235 | * @param string $tag тэг
236 | * @param array $attribute список атрибутов, где ключ - имя атрибута, а значение - само значение данного атрибута
237 | * @return string
238 | */
239 | public static function build_safe_tag($content, $tag = 'span', $attribute = array(), $layout = EMT_Lib::LAYOUT_STYLE )
240 | {
241 | $htmlTag = $tag;
242 |
243 | if (self::$_typographSpecificTagId)
244 | {
245 | if(!isset($attribute['id']))
246 | {
247 | $attribute['id'] = 'emt-2' . mt_rand(1000,9999);
248 | }
249 | }
250 |
251 | $classname = "";
252 | if (count($attribute))
253 | {
254 |
255 | if($layout & EMT_lib::LAYOUT_STYLE)
256 | {
257 | if(isset($attribute['__style']) && $attribute['__style'])
258 | {
259 | if(isset($attribute['style']) && $attribute['style'])
260 | {
261 | $st = trim($attribute['style']);
262 | if(mb_substr($st, -1) != ";") $st .= ";";
263 | $st .= $attribute['__style'];
264 | $attribute['style'] = $st;
265 | } else {
266 | $attribute['style'] = $attribute['__style'];
267 | }
268 | unset($attribute['__style']);
269 | }
270 |
271 | }
272 | foreach ($attribute as $attr => $value)
273 | {
274 | if($attr == "__style") continue;
275 | if($attr == "class") {
276 | $classname = "$value";
277 | continue;
278 | }
279 | $htmlTag .= " $attr=\"$value\"";
280 | }
281 |
282 | }
283 |
284 | if( ($layout & EMT_lib::LAYOUT_CLASS ) && $classname) {
285 | $htmlTag .= " class=\"$classname\"";
286 | }
287 |
288 | return "<" . self::encrypt_tag($htmlTag) . ">$content" . self::encrypt_tag($tag) . ">";
289 | }
290 |
291 | /**
292 | * Метод, осуществляющий кодирование (сохранение) информации
293 | * с целью невозможности типографировать ее
294 | *
295 | * @param string $text
296 | * @return string
297 | */
298 | public static function encrypt_tag($text)
299 | {
300 | return base64_encode($text)."=";
301 | }
302 |
303 | /**
304 | * Метод, осуществляющий декодирование информации
305 | *
306 | * @param string $text
307 | * @return string
308 | */
309 | public static function decrypt_tag($text)
310 | {
311 | return base64_decode(substr($text,0,-1));
312 | }
313 |
314 |
315 |
316 | public static function strpos_ex(&$haystack, $needle, $offset = null)
317 | {
318 | if(is_array($needle))
319 | {
320 | $m = false;
321 | $w = false;
322 | foreach($needle as $n)
323 | {
324 | $p = strpos($haystack, $n , $offset);
325 | if($p===false) continue;
326 | if($m === false)
327 | {
328 | $m = $p;
329 | $w = $n;
330 | continue;
331 | }
332 | if($p < $m)
333 | {
334 | $m = $p;
335 | $w = $n;
336 | }
337 | }
338 | if($m === false) return false;
339 | return array('pos' => $m, 'str' => $w);
340 | }
341 | return strpos($haystack, $needle, $offset);
342 | }
343 |
344 | public static function create_function($args, $code) {
345 | if (version_compare(PHP_VERSION, '7.0.0') < 0) {
346 | return create_function($args, $code);
347 | }
348 | $closure = false;
349 | eval('$closure = function('.$args.') { '.$code.' }; ');
350 | return $closure;
351 | }
352 |
353 | public static function _process_selector_pattern(&$pattern)
354 | {
355 | if($pattern===false) return;
356 | $pattern = preg_quote($pattern , '/');
357 | $pattern = str_replace("\\*", "[a-z0-9_\-]*", $pattern);
358 | $pattern = "/".$pattern."/i";
359 | }
360 | public static function _test_pattern($pattern, $text)
361 | {
362 | if($pattern === false) return true;
363 | return preg_match($pattern, $text);
364 | }
365 |
366 | public static function strtolower($string)
367 | {
368 | $convert_to = array(
369 | "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u",
370 | "v", "w", "x", "y", "z", "à", "á", "â", "ã", "ä", "å", "æ", "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï",
371 | "ð", "ñ", "ò", "ó", "ô", "õ", "ö", "ø", "ù", "ú", "û", "ü", "ý", "а", "б", "в", "г", "д", "е", "ё", "ж",
372 | "з", "и", "й", "к", "л", "м", "н", "о", "п", "р", "с", "т", "у", "ф", "х", "ц", "ч", "ш", "щ", "ъ", "ы",
373 | "ь", "э", "ю", "я"
374 | );
375 | $convert_from = array(
376 | "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U",
377 | "V", "W", "X", "Y", "Z", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï",
378 | "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "Ø", "Ù", "Ú", "Û", "Ü", "Ý", "А", "Б", "В", "Г", "Д", "Е", "Ё", "Ж",
379 | "З", "И", "Й", "К", "Л", "М", "Н", "О", "П", "Р", "С", "Т", "У", "Ф", "Х", "Ц", "Ч", "Ш", "Щ", "Ъ", "Ъ",
380 | "Ь", "Э", "Ю", "Я"
381 | );
382 |
383 | return str_replace($convert_from, $convert_to, $string);
384 | }
385 |
386 | // взято с http://www.w3.org/TR/html4/sgml/entities.html
387 | protected static $html4_char_ents = array(
388 | 'nbsp' => 160,
389 | 'iexcl' => 161,
390 | 'cent' => 162,
391 | 'pound' => 163,
392 | 'curren' => 164,
393 | 'yen' => 165,
394 | 'brvbar' => 166,
395 | 'sect' => 167,
396 | 'uml' => 168,
397 | 'copy' => 169,
398 | 'ordf' => 170,
399 | 'laquo' => 171,
400 | 'not' => 172,
401 | 'shy' => 173,
402 | 'reg' => 174,
403 | 'macr' => 175,
404 | 'deg' => 176,
405 | 'plusmn' => 177,
406 | 'sup2' => 178,
407 | 'sup3' => 179,
408 | 'acute' => 180,
409 | 'micro' => 181,
410 | 'para' => 182,
411 | 'middot' => 183,
412 | 'cedil' => 184,
413 | 'sup1' => 185,
414 | 'ordm' => 186,
415 | 'raquo' => 187,
416 | 'frac14' => 188,
417 | 'frac12' => 189,
418 | 'frac34' => 190,
419 | 'iquest' => 191,
420 | 'Agrave' => 192,
421 | 'Aacute' => 193,
422 | 'Acirc' => 194,
423 | 'Atilde' => 195,
424 | 'Auml' => 196,
425 | 'Aring' => 197,
426 | 'AElig' => 198,
427 | 'Ccedil' => 199,
428 | 'Egrave' => 200,
429 | 'Eacute' => 201,
430 | 'Ecirc' => 202,
431 | 'Euml' => 203,
432 | 'Igrave' => 204,
433 | 'Iacute' => 205,
434 | 'Icirc' => 206,
435 | 'Iuml' => 207,
436 | 'ETH' => 208,
437 | 'Ntilde' => 209,
438 | 'Ograve' => 210,
439 | 'Oacute' => 211,
440 | 'Ocirc' => 212,
441 | 'Otilde' => 213,
442 | 'Ouml' => 214,
443 | 'times' => 215,
444 | 'Oslash' => 216,
445 | 'Ugrave' => 217,
446 | 'Uacute' => 218,
447 | 'Ucirc' => 219,
448 | 'Uuml' => 220,
449 | 'Yacute' => 221,
450 | 'THORN' => 222,
451 | 'szlig' => 223,
452 | 'agrave' => 224,
453 | 'aacute' => 225,
454 | 'acirc' => 226,
455 | 'atilde' => 227,
456 | 'auml' => 228,
457 | 'aring' => 229,
458 | 'aelig' => 230,
459 | 'ccedil' => 231,
460 | 'egrave' => 232,
461 | 'eacute' => 233,
462 | 'ecirc' => 234,
463 | 'euml' => 235,
464 | 'igrave' => 236,
465 | 'iacute' => 237,
466 | 'icirc' => 238,
467 | 'iuml' => 239,
468 | 'eth' => 240,
469 | 'ntilde' => 241,
470 | 'ograve' => 242,
471 | 'oacute' => 243,
472 | 'ocirc' => 244,
473 | 'otilde' => 245,
474 | 'ouml' => 246,
475 | 'divide' => 247,
476 | 'oslash' => 248,
477 | 'ugrave' => 249,
478 | 'uacute' => 250,
479 | 'ucirc' => 251,
480 | 'uuml' => 252,
481 | 'yacute' => 253,
482 | 'thorn' => 254,
483 | 'yuml' => 255,
484 | 'fnof' => 402,
485 | 'Alpha' => 913,
486 | 'Beta' => 914,
487 | 'Gamma' => 915,
488 | 'Delta' => 916,
489 | 'Epsilon' => 917,
490 | 'Zeta' => 918,
491 | 'Eta' => 919,
492 | 'Theta' => 920,
493 | 'Iota' => 921,
494 | 'Kappa' => 922,
495 | 'Lambda' => 923,
496 | 'Mu' => 924,
497 | 'Nu' => 925,
498 | 'Xi' => 926,
499 | 'Omicron' => 927,
500 | 'Pi' => 928,
501 | 'Rho' => 929,
502 | 'Sigma' => 931,
503 | 'Tau' => 932,
504 | 'Upsilon' => 933,
505 | 'Phi' => 934,
506 | 'Chi' => 935,
507 | 'Psi' => 936,
508 | 'Omega' => 937,
509 | 'alpha' => 945,
510 | 'beta' => 946,
511 | 'gamma' => 947,
512 | 'delta' => 948,
513 | 'epsilon' => 949,
514 | 'zeta' => 950,
515 | 'eta' => 951,
516 | 'theta' => 952,
517 | 'iota' => 953,
518 | 'kappa' => 954,
519 | 'lambda' => 955,
520 | 'mu' => 956,
521 | 'nu' => 957,
522 | 'xi' => 958,
523 | 'omicron' => 959,
524 | 'pi' => 960,
525 | 'rho' => 961,
526 | 'sigmaf' => 962,
527 | 'sigma' => 963,
528 | 'tau' => 964,
529 | 'upsilon' => 965,
530 | 'phi' => 966,
531 | 'chi' => 967,
532 | 'psi' => 968,
533 | 'omega' => 969,
534 | 'thetasym' => 977,
535 | 'upsih' => 978,
536 | 'piv' => 982,
537 | 'bull' => 8226,
538 | 'hellip' => 8230,
539 | 'prime' => 8242,
540 | 'Prime' => 8243,
541 | 'oline' => 8254,
542 | 'frasl' => 8260,
543 | 'weierp' => 8472,
544 | 'image' => 8465,
545 | 'real' => 8476,
546 | 'trade' => 8482,
547 | 'alefsym' => 8501,
548 | 'larr' => 8592,
549 | 'uarr' => 8593,
550 | 'rarr' => 8594,
551 | 'darr' => 8595,
552 | 'harr' => 8596,
553 | 'crarr' => 8629,
554 | 'lArr' => 8656,
555 | 'uArr' => 8657,
556 | 'rArr' => 8658,
557 | 'dArr' => 8659,
558 | 'hArr' => 8660,
559 | 'forall' => 8704,
560 | 'part' => 8706,
561 | 'exist' => 8707,
562 | 'empty' => 8709,
563 | 'nabla' => 8711,
564 | 'isin' => 8712,
565 | 'notin' => 8713,
566 | 'ni' => 8715,
567 | 'prod' => 8719,
568 | 'sum' => 8721,
569 | 'minus' => 8722,
570 | 'lowast' => 8727,
571 | 'radic' => 8730,
572 | 'prop' => 8733,
573 | 'infin' => 8734,
574 | 'ang' => 8736,
575 | 'and' => 8743,
576 | 'or' => 8744,
577 | 'cap' => 8745,
578 | 'cup' => 8746,
579 | 'int' => 8747,
580 | 'there4' => 8756,
581 | 'sim' => 8764,
582 | 'cong' => 8773,
583 | 'asymp' => 8776,
584 | 'ne' => 8800,
585 | 'equiv' => 8801,
586 | 'le' => 8804,
587 | 'ge' => 8805,
588 | 'sub' => 8834,
589 | 'sup' => 8835,
590 | 'nsub' => 8836,
591 | 'sube' => 8838,
592 | 'supe' => 8839,
593 | 'oplus' => 8853,
594 | 'otimes' => 8855,
595 | 'perp' => 8869,
596 | 'sdot' => 8901,
597 | 'lceil' => 8968,
598 | 'rceil' => 8969,
599 | 'lfloor' => 8970,
600 | 'rfloor' => 8971,
601 | 'lang' => 9001,
602 | 'rang' => 9002,
603 | 'loz' => 9674,
604 | 'spades' => 9824,
605 | 'clubs' => 9827,
606 | 'hearts' => 9829,
607 | 'diams' => 9830,
608 | 'quot' => 34,
609 | 'amp' => 38,
610 | 'lt' => 60,
611 | 'gt' => 62,
612 | 'OElig' => 338,
613 | 'oelig' => 339,
614 | 'Scaron' => 352,
615 | 'scaron' => 353,
616 | 'Yuml' => 376,
617 | 'circ' => 710,
618 | 'tilde' => 732,
619 | 'ensp' => 8194,
620 | 'emsp' => 8195,
621 | 'thinsp' => 8201,
622 | 'zwnj' => 8204,
623 | 'zwj' => 8205,
624 | 'lrm' => 8206,
625 | 'rlm' => 8207,
626 | 'ndash' => 8211,
627 | 'mdash' => 8212,
628 | 'lsquo' => 8216,
629 | 'rsquo' => 8217,
630 | 'sbquo' => 8218,
631 | 'ldquo' => 8220,
632 | 'rdquo' => 8221,
633 | 'bdquo' => 8222,
634 | 'dagger' => 8224,
635 | 'Dagger' => 8225,
636 | 'permil' => 8240,
637 | 'lsaquo' => 8249,
638 | 'rsaquo' => 8250,
639 | 'euro' => 8364,
640 | );
641 | /**
642 | * Вернуть уникод символ по html entinty
643 | *
644 | * @param string $entity
645 | * @return string
646 | */
647 | public static function html_char_entity_to_unicode($entity)
648 | {
649 | if(isset(self::$html4_char_ents[$entity])) return self::_getUnicodeChar(self::$html4_char_ents[$entity]);
650 | return false;
651 | }
652 |
653 | /**
654 | * Сконвериторвать все html entity в соответсвующие юникод символы
655 | *
656 | * @param string $text
657 | */
658 | public static function convert_html_entities_to_unicode(&$text)
659 | {
660 | $text = preg_replace_callback("/\([0-9]+)\;/",
661 | EMT_Lib::create_function('$m', 'return EMT_Lib::_getUnicodeChar(intval($m[1]));')
662 | , $text);
663 | $text = preg_replace_callback("/\([0-9A-F]+)\;/",
664 | EMT_Lib::create_function('$m', 'return EMT_Lib::_getUnicodeChar(hexdec($m[1]));')
665 | , $text);
666 | $text = preg_replace_callback("/\&([a-zA-Z0-9]+)\;/",
667 | EMT_Lib::create_function('$m', '$r = EMT_Lib::html_char_entity_to_unicode($m[1]); return $r ? $r : $m[0];')
668 | , $text);
669 | }
670 |
671 | public static function rstrpos ($haystack, $needle, $offset = 0){
672 |
673 | if(trim($haystack) != "" && trim($needle) != "" && $offset <= mb_strlen($haystack))
674 | {
675 | $last_pos = $offset;
676 | $found = false;
677 | while(($curr_pos = mb_strpos($haystack, $needle, $last_pos)) !== false)
678 | {
679 | $found = true;
680 | $last_pos = $curr_pos + 1;
681 | }
682 | if($found)
683 | {
684 | return $last_pos - 1;
685 | }
686 | else
687 | {
688 | return false;
689 | }
690 | }
691 | else
692 | {
693 | return false;
694 | }
695 | }
696 |
697 | public static function ifop($cond, $true, $false) {
698 | return $cond ? $true : $false;
699 | }
700 |
701 | public static function split_number($num) {
702 | return number_format($num, 0, '', ' ');
703 | }
704 |
705 | // https://mathiasbynens.be/demo/url-regex
706 | // @gruber v2 (218 chars)
707 | public static function url_regex() {
708 | /*return << надо делать её висячей
64 | *
65 | * @return void
66 | */
67 | protected function oaquote_extra()
68 | {
69 | $this->_text = $this->preg_replace_e(
70 | '/(<' .self::BASE64_PARAGRAPH_TAG . '>)([\040\t]+)?(\«\;)/e',
71 | '$m[1] . $this->tag($m[3], "span", array("class"=>"oa_oquote_nl"))',
72 | $this->_text);
73 | }
74 |
75 |
76 | }
77 |
78 | ?>
--------------------------------------------------------------------------------
/src-php/EMT.Tret.Punctmark.php:
--------------------------------------------------------------------------------
1 | array(
13 | 'description' => 'Расстановка запятых перед а, но',
14 | 'pattern' => '/([a-zа-яё])(\s| )(но|а)(\s| )/u',
15 | 'replacement' => '\1,\2\3\4'
16 | ),
17 | 'punctuation_marks_limit' => array(
18 | 'description' => 'Лишние восклицательные, вопросительные знаки и точки',
19 | 'pattern' => '/([\!\.\?]){4,}/',
20 | 'replacement' => '\1\1\1'
21 | ),
22 | 'punctuation_marks_base_limit' => array(
23 | 'description' => 'Лишние запятые, двоеточия, точки с запятой',
24 | 'pattern' => array(
25 | '/([\,]|[\:]){2,}/',
26 | '/(;){2,}/',
27 | '/((,|:);){2,}/',
28 | ),
29 | 'replacement' => array(
30 | '\1',
31 | '\1',
32 | '\1',
33 | ),
34 | ),
35 | 'hellip' => array(
36 | 'description' => 'Замена трех точек на знак многоточия',
37 | 'simple_replace'=> true,
38 | 'pattern' => '...',
39 | 'replacement' => '…'
40 | ),
41 | 'fix_excl_quest_marks' => array(
42 | 'description' => 'Замена восклицательного и вопросительного знаков местами',
43 | 'pattern' => '/([a-zа-яё0-9])\!\?(\s|$|\<)/ui',
44 | 'replacement' => '\1?!\2'
45 | ),
46 | 'fix_pmarks' => array(
47 | 'description' => 'Замена сдвоенных знаков препинания на одинарные',
48 | 'pattern' => array(
49 | '/([^\!\?])\.\./',
50 | '/([a-zа-яё0-9])(\!|\.)(\!|\.|\?)(\s|$|\<)/ui',
51 | '/([a-zа-яё0-9])(\?)(\?)(\s|$|\<)/ui',
52 | ),
53 | 'replacement' => array(
54 | '\1.',
55 | '\1\2\4',
56 | '\1\2\4'
57 | ),
58 | ),
59 | 'fix_brackets' => array(
60 | 'description' => 'Лишние пробелы после открывающей скобочки и перед закрывающей',
61 | 'pattern' => array('/(\()(\040|\t)+/', '/(\040|\t)+(\))/'),
62 | 'replacement' => array('\1', '\2')
63 | ),
64 | 'fix_brackets_space' => array(
65 | 'description' => 'Пробел перед открывающей скобочкой',
66 | 'pattern' => '/([a-zа-яё])(\()/iu',
67 | 'replacement' => '\1 \2'
68 | ),
69 | 'dot_on_end' => array(
70 | 'description' => 'Точка в конце текста, если её там нет',
71 | 'disabled' => true,
72 | 'pattern' => '/([a-zа-яё0-9])(\040|\t|\ \;)*$/ui',
73 | //'pattern' => '/(([^\.\!\?])|(&(ra|ld)quo;))$/',
74 | 'replacement' => '\1.'
75 | ),
76 |
77 | );
78 | }
79 |
80 | ?>
--------------------------------------------------------------------------------
/src-php/EMT.Tret.Quote.php:
--------------------------------------------------------------------------------
1 | array(
19 | 'description' => 'Кавычки вне тэга ',
20 | //'pattern' => '/(\<%%\_\_.+?\>)\"(.+?)\"(\<\/%%\_\_.+?\>)/s',
21 | 'pattern' => '/(\<%%\_\_[^\>]+\>)\"(.+?)\"(\<\/%%\_\_[^\>]+\>)/s',
22 | 'replacement' => '"\1\2\3"'
23 | ),
24 |
25 | 'open_quote' => array(
26 | 'description' => 'Открывающая кавычка',
27 | 'pattern' => '/(^|\(|\s|\>|-)((\"|\\\")+)(\S+?)/iue',
28 | 'replacement' => '$m[1] . str_repeat(self::QUOTE_FIRS_OPEN, substr_count($m[2],"\"") ) . $m[4]'
29 | ),
30 | 'close_quote' => array(
31 | 'description' => 'Закрывающая кавычка',
32 | 'pattern' => '/([a-zа-яё0-9]|\.|\&hellip\;|\!|\?|\>|\)|\:|\+|\%|\@|\#|\$|\*)((\"|\\\")+)(\.|\&hellip\;|\;|\:|\?|\!|\,|\s|\)|\<\/|\<|$)/uie',
33 | 'replacement' => '$m[1] . str_repeat(self::QUOTE_FIRS_CLOSE, substr_count($m[2],"\"") ) . $m[4]'
34 | ),
35 | 'close_quote_adv' => array(
36 | 'description' => 'Закрывающая кавычка особые случаи',
37 | //'pattern' => '/([a-zа-яё0-9]|\.|\&hellip\;|\!|\?|\>|\)|\:)((\"|\\\"|\«\;)+)(\<.+?\>)(\.|\&hellip\;|\;|\:|\?|\!|\,|\s|\)|\<\/|$)/uie',
38 | 'pattern' =>
39 | array(
40 | '/([a-zа-яё0-9]|\.|\&hellip\;|\!|\?|\>|\)|\:|\+|\%|\@|\#|\$|\*)((\"|\\\"|\«\;)+)(\<[^\>]+\>)(\.|\&hellip\;|\;|\:|\?|\!|\,|\)|\<\/|$| )/uie',
41 | '/([a-zа-яё0-9]|\.|\&hellip\;|\!|\?|\>|\)|\:|\+|\%|\@|\#|\$|\*)(\s+)((\"|\\\")+)(\s+)(\.|\&hellip\;|\;|\:|\?|\!|\,|\)|\<\/|$| )/uie',
42 | '/\>(\«\;)\.($|\s|\<)/ui',
43 | '/\>(\«\;),($|\s|\<|\S)/ui',
44 | '/\>(\«\;):($|\s|\<|\S)/ui',
45 | '/\>(\«\;);($|\s|\<|\S)/ui',
46 | '/\>(\«\;)\)($|\s|\<|\S)/ui',
47 | '/((\"|\\\")+)$/uie',
48 | ),
49 | 'replacement' =>
50 | array(
51 | '$m[1] . str_repeat(self::QUOTE_FIRS_CLOSE, substr_count($m[2],"\"")+substr_count($m[2],"«") ) . $m[4]. $m[5]',
52 | '$m[1] .$m[2]. str_repeat(self::QUOTE_FIRS_CLOSE, substr_count($m[3],"\"")+substr_count($m[3],"«") ) . $m[5]. $m[6]',
53 | '>».\2',
54 | '>»,\2',
55 | '>»:\2',
56 | '>»;\2',
57 | '>»)\2',
58 | 'str_repeat(self::QUOTE_FIRS_CLOSE, substr_count($m[1],"\"") )',
59 | ),
60 | ),
61 | 'open_quote_adv' => array(
62 | 'description' => 'Открывающая кавычка особые случаи',
63 | 'pattern' => '/(^|\(|\s|\>)(\"|\\\")(\s)(\S+)/iue',
64 | 'replacement' => '$m[1] . self::QUOTE_FIRS_OPEN .$m[4]'
65 | ),
66 | 'close_quote_adv_2' => array(
67 | 'description' => 'Закрывающая кавычка последний шанс',
68 | 'pattern' => '/(\S)((\"|\\\")+)(\.|\&hellip\;|\;|\:|\?|\!|\,|\s|\)|\<\/|\<|$)/uie',
69 | 'replacement' => '$m[1] . str_repeat(self::QUOTE_FIRS_CLOSE, substr_count($m[2],"\"") ) . $m[4]'
70 | ),
71 | 'quotation' => array(
72 | 'description' => 'Внутренние кавычки-лапки и дюймы',
73 | 'function' => 'build_sub_quotations'
74 | ),
75 | );
76 |
77 | protected function inject_in($pos, $text, &$thetext)
78 | {
79 | for($i=0;$i ... ... ... dsdsd sdsdadadad dsdsd sdsdadadad
\nШитый золотом узор!..
\nГосударственное дело, —
\nРасшибись, а будь добёр!", null, "Тире в конце строки (стихотворная форма)", $no_p);
59 |
60 | $tester->add_test("123-32", "123−32", null, "Расстановка знака минус между числами", $no_p);
61 |
62 | $tester->add_test("don't", "don’t", null, "Расстановка правильного апострофа в английских текстах", $no_p);
63 |
64 | $tester->add_test("§32, §IV", "§ 32, § IV", null, "Замена символа параграф с привязкой к числу", $no_p);
65 |
66 | $tester->add_test("№15Ф, №34/25, № 807", "№ 15Ф, № 34/25, № 807", null, "Замена символа номер с привязкой к числу", $no_p);
67 |
68 | $tester->add_test(<<add_test(<<add_test("10,34руб., 23тыс.долл., 64 млн.евро, 34.3€, 56$, 3,65уе", "10,34 руб., 23 тыс. долл., 64 млн евро, 34.3 €, 56 $, 3,65 у.е.", null, "Расстановка пробелов и привязка в денежных сокращениях", $no_p);
79 |
80 | $tester->add_test("Лес, газ, нефть и тд., и т.п.. Перины, подушки в тч. подушки-думки.", "Лес, газ, нефть Заяц сидел на опушке.
20 | HTML
21 | , <<Заяц сидел на опушке.
23 | HTML
24 | , null, "Отмена типографирования тэга pre с вложенным тэгом", array('Text.paragraphs'=>'off', 'OptAlign.all'=>'off'), "code");
25 |
26 |
27 | $tester->add_test(<<Заяц сидел на опушке.
29 | HTML
30 | , <<Заяц сидел на опушке.
32 | HTML
33 | , null, "Отмена типографирования тэга pre в внешним тэгом", array('Text.paragraphs'=>'off', 'OptAlign.all'=>'off'), "code");
34 |
35 | $tester->add_test(<<mdash.ru/the-best-typograph.html
41 | Что-то: mdash.ru/A0SAFESEQUENCENUM1ID
42 | HTML
43 | , null, "Обработка ссылок с имеющейся маркой", array('Text.paragraphs'=>'off', 'OptAlign.all'=>'off'), "code");
44 |
45 | ?>
--------------------------------------------------------------------------------
/tools-php/clean.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tools-php/debug.php:
--------------------------------------------------------------------------------
1 | debug_on();
33 | $typograph->log_on();
34 | if($_REQUEST['useoptions'])
35 | {
36 | $use_class_checked = $_REQUEST['use_method']==2 ? "checked" : "";
37 | $use_style_checked = $_REQUEST['use_method']==1 ? "checked" : "";
38 | $layout = 0;
39 | if($_REQUEST['use_method']==2) $layout = EMT_Lib::LAYOUT_CLASS ;
40 | if($_REQUEST['use_method']==1) $layout = EMT_Lib::LAYOUT_STYLE ;
41 | //if($_REQUEST['use_style']) $layout |= EMT_Lib::LAYOUT_STYLE ;
42 | $typograph->set_tag_layout($layout);
43 | $opt_r = array();
44 | foreach($options as $tretname => $tlist) $opt_r[$tretname] = array_keys($tlist);
45 | $typograph->set_enable_map($opt_r);
46 | }
47 | $typograph->set_text($_REQUEST['inputdata']);
48 | $result = $typograph->apply();
49 |
50 |
51 | function draw_debug_item($tret_title, $tret_name, $rule_title, $rule_name, $result, $something_changed, $result_raw )
52 | {
53 | $h = "
\n";
141 | }
142 | $typooptions .= "".
143 | "{$tret_obj->title}".$tret."
\nТипограф Евгения Муравьёва. Отладчик.
192 |
214 |
215 |
216 | HTML;
217 |
218 | if($ok)
219 | echo <<
223 |
224 | HTML code:
225 | $code
226 |
227 |
228 |
229 |
230 | Отладка
показать всё показать необработанные рездуьтаты
231 |
235 |
236 |
237 |
238 | Логи
Посмотреть
239 |
240 |
241 | HTML;
242 |
243 | echo <<
245 |
246 | HTML;
247 |
248 | ?>
--------------------------------------------------------------------------------
/tools-php/example.php:
--------------------------------------------------------------------------------
1 | add_safe_tag('code');
11 | $t->set_text("
");
12 | echo $t->apply();
13 |
14 | exit;
15 |
16 | // 1. Запуск типографа с настройками по умолчанию
17 | $typograf = new EMTypograph();
18 | $typograf->set_text("...Когда В. И. Пупкин увидел в газете ( это была "Сермяжная правда" № 45) рубрику Weather Forecast(r), он не поверил своим глазам - температуру обещали +-451F.");
19 | $result = $typograf->apply();
20 | echo "Настройки по умолчанию: " . $result . "\n";
21 |
22 |
23 |
24 | // 2. Ручная настройка правил
25 | $typograf = new EMTypograph();
26 | $typograf->set_text("...Когда В. И. Пупкин увидел в газете ( это была "Сермяжная правда" № 45) рубрику Weather Forecast(r), он не поверил своим глазам - температуру обещали +-451F.");
27 | $typograf->setup(array(
28 | 'Text.paragraphs' => 'off',
29 | 'OptAlign.oa_oquote' => 'off',
30 | 'OptAlign.oa_obracket_coma' => 'off',
31 | ));
32 | $result = $typograf->apply();
33 | echo "Без параграфов, висячей пунктуации: " . $result . "\"text\"
\n";
34 |
35 |
36 |
37 |
38 | // 3. Быстрый запуск типографа с настройками по умолчанию
39 | $result = EMTypograph::fast_apply("...Когда В. И. Пупкин увидел в газете ( это была "Сермяжная правда" № 45) рубрику Weather Forecast(r), он не поверил своим глазам - температуру обещали +-451F.");
40 | echo "Быстрый запуск: " . $result . "
\n";
41 |
42 |
43 |
44 |
45 | // 4. Быстрый запуск типографа с ручными настройками
46 | $result = EMTypograph::fast_apply("...Когда В. И. Пупкин увидел в газете ( это была "Сермяжная правда" № 45) рубрику Weather Forecast(r), он не поверил своим глазам - температуру обещали +-451F.",array(
47 | 'Text.paragraphs' => 'off',
48 | 'OptAlign.oa_oquote' => 'off',
49 | 'OptAlign.oa_obracket_coma' => 'off',
50 | ));
51 | echo "Быстрый запуск настройками: " . $result . "
\n";
52 |
53 |
54 | // 5. Ручная настройка правила - использования css классов вместо inline стилей
55 | $typograf = new EMTypograph();
56 | $typograf->set_text("...Когда В. И. Пупкин увидел в газете ( это была "Сермяжная правда" № 45) рубрику Weather Forecast(r), он не поверил своим глазам - температуру обещали +-451F.");
57 | $typograf->setup(array(
58 | 'OptAlign.layout' => 'class',
59 | ));
60 | $result = $typograf->apply();
61 | echo "Классы вместо инлайн стилей: " . $result . "
\n";
62 |
63 |
64 | ?>
--------------------------------------------------------------------------------
/tools-php/online-fast.php:
--------------------------------------------------------------------------------
1 | set_text($_REQUEST['inputdata']);
13 | $result = $typograph->apply();
14 |
15 | $html = $result;
16 | $code = htmlspecialchars($html);
17 | }
18 |
19 |
20 | $phpself = $_SERVER['PHP_SELF'];
21 | echo <<
23 |
24 |
43 |
45 |
46 | HTML code:
47 | $code
48 |
49 |
50 |
51 |
52 | HTML;
53 |
54 |
55 | ?>
--------------------------------------------------------------------------------
/tools-php/online.php:
--------------------------------------------------------------------------------
1 | get_options_list();
8 | $options = array();
9 | foreach($option_list['all'] as $opt => $info)
10 | {
11 | // опции были переданы
12 | if(isset($_REQUEST['text']))
13 | {
14 | if(!isset($_REQUEST['options'][$opt]))
15 | {
16 | if(isset($info['way']) && ($info['way'] == "reverse")) $options[$opt] = "on"; else $options[$opt] = "off";
17 | if(isset($info['selector'])) $options[$opt] = isset($info['reversed']) ? "on" : "off";
18 | continue;
19 | }
20 |
21 | $options[$opt] = $_REQUEST['options'][$opt];
22 | } else { // опции не были переданы, тогда всё по умолчанию
23 |
24 | // одно из правил ядра
25 | if(isset($info['way']))
26 | {
27 | if($info['way']!="reverse") $options[$opt] = ( isset($info['disabled']) ? "off" : "on" ) ; else $options[$opt] = ( isset($info['disabled']) ? "on" : "off" ) ;
28 | }
29 | if(isset($info['selector']))
30 | {
31 | if(isset($info['reversed'])) $options[$opt] = ( isset($info['disabled']) ? "on" : "off" ); else $options[$opt] = ( isset($info['disabled']) ? "off" : "on" );
32 | }
33 | }
34 | }
35 |
36 | $text = false;
37 | $result = false;
38 | $code = false;
39 | $error = false;
40 | if(isset($_REQUEST['text']))
41 | {
42 | $text = $_REQUEST['text'];
43 | $inputdata = htmlspecialchars($_REQUEST['text']);
44 |
45 | $typograph->setup($options);
46 | $typograph->set_text($text);
47 | $result = $typograph->apply();
48 | }
49 |
50 | if(isset($_REQUEST['format']))
51 | {
52 | json_encode($result);
53 | } else {
54 |
55 | $options_html = "";
56 | foreach($option_list['group'] as $key => $ginfo)
57 | {
58 | $group = $ginfo['name'];
59 | $option_html = "";
60 | if(is_array($ginfo['options']))
61 | {
62 | foreach($ginfo['options'] as $optname)
63 | {
64 | $option = $option_list['all'][$optname];
65 |
66 | if($optname == "Nobr.nowrap")
67 | {
68 | $option_html .= " Использовать nobr$optname=off ".
69 | " Использовать nowrap$optname=on
\n";
70 | continue;
71 | }
72 | if($optname == "OptAlign.layout")
73 | {
74 | $option_html .= " Использовать стили$optname=style ".
75 | " Использовать классы$optname=class
\n";
76 | continue;
77 | }
78 | if(isset($option['way']))
79 | {
80 | $value = ($option['way']=="direct"? "on" : "off");
81 | $option_html .= " $option[description]$optname=on|off
\n";
82 | continue;
83 | }
84 | if(isset($option['selector']) && !(isset($option['hide']) && $option['hide']))
85 | {
86 | $value = isset($option['reversed']) ? "off" : "on" ;
87 | $option_html .= " $option[description]$optname=on|off
\n";
88 | continue;
89 | }
90 |
91 | }
92 | }
93 | $options_html .= "$ginfo[title]
$option_html";
94 | }
95 |
96 | if($text !== false )
97 | {
98 | if(!$typograph->ok)
99 | {
100 | $error = "";
101 | foreach($typograph->errors as $err) $error .= "