├── 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"; 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("/\&#x([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 <<]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))#iSu 714 | URLREGEX; 715 | 716 | /* 717 | return << -------------------------------------------------------------------------------- /src-php/EMT.Tret.Abbr.php: -------------------------------------------------------------------------------- 1 | 'word-spacing:nowrap;', 15 | ); 16 | 17 | public $rules = array( 18 | 'nobr_abbreviation' => array( 19 | 'description' => 'Расстановка пробелов перед сокращениями dpi, lpi', 20 | 'pattern' => '/(\s+|^|\>)(\d+)(\040|\t)*(dpi|lpi)([\s\;\.\?\!\:\(]|$)/i', 21 | 'replacement' => '\1\2 \4\5' 22 | ), 23 | 'nobr_acronym' => array( 24 | 'description' => 'Расстановка пробелов перед сокращениями гл., стр., рис., илл., ст., п.', 25 | 'pattern' => '/(\s|^|\>|\()(гл|стр|рис|илл?|ст|п|с)\.(\040|\t)*(\d+)(\ \;|\s|\.|\,|\?|\!|$)/iu', 26 | 'replacement' => '\1\2. \4\5' 27 | ), 28 | 'nobr_sm_im' => array( 29 | 'description' => 'Расстановка пробелов перед сокращениями см., им.', 30 | 'pattern' => '/(\s|^|\>|\()(см|им)\.(\040|\t)*([а-яё0-9a-z]+)(\s|\.|\,|\?|\!|$)/iu', 31 | 'replacement' => '\1\2. \4\5' 32 | ), 33 | 'nobr_locations' => array( 34 | 'description' => 'Расстановка пробелов в сокращениях г., ул., пер., д.', 35 | 'pattern' => array( 36 | '/(\s|^|\>)(г|ул|пер|просп|пл|бул|наб|пр|ш|туп)\.(\040|\t)*([а-яё0-9a-z]+)(\s|\.|\,|\?|\!|$)/iu', 37 | '/(\s|^|\>)(б\-р|пр\-кт)(\040|\t)*([а-яё0-9a-z]+)(\s|\.|\,|\?|\!|$)/iu', 38 | '/(\s|^|\>)(д|кв|эт)\.(\040|\t)*(\d+)(\s|\.|\,|\?|\!|$)/iu', 39 | ), 40 | 'replacement' => array( 41 | '\1\2. \4\5', 42 | '\1\2 \4\5', 43 | '\1\2. \4\5', 44 | ) 45 | ), 46 | 'nbsp_before_unit' => array( 47 | 'description' => 'Замена символов и привязка сокращений в размерных величинах: м, см, м2…', 48 | 'pattern' => array( 49 | '/(\s|^|\>|\ \;|\,)(\d+)( |\ \;)?(м|мм|см|дм|км|гм|km|dm|cm|mm)(\s|\.|\!|\?|\,|$|\±\;|\;|\<)/iu', 50 | '/(\s|^|\>|\ \;|\,)(\d+)( |\ \;)?(м|мм|см|дм|км|гм|km|dm|cm|mm)([32]|³|²)(\s|\.|\!|\?|\,|$|\±\;|\;|\<)/iue' 51 | ), 52 | 'replacement' => array( 53 | '\1\2 \4\5', 54 | '$m[1].$m[2]." ".$m[4].($m[5]=="3"||$m[5]=="2"? "&sup".$m[5].";" : $m[5] ).$m[6]' 55 | ), 56 | ), 57 | 'nbsp_before_weight_unit' => array( 58 | 'description' => 'Замена символов и привязка сокращений в весовых величинах: г, кг, мг…', 59 | 'pattern' => '/(\s|^|\>|\ \;|\,)(\d+)( |\ \;)?(г|кг|мг|т)(\s|\.|\!|\?|\,|$|\ \;|\;)/iu', 60 | 'replacement' => '\1\2 \4\5', 61 | ), 62 | 'nobr_before_unit_volt' => array( 63 | 'description' => 'Установка пробельных символов в сокращении вольт', 64 | 'pattern' => '/(\d+)([вВ]| В)(\s|\.|\!|\?|\,|$)/u', 65 | 'replacement' => '\1 В\3' 66 | ), 67 | 'ps_pps' => array( 68 | 'description' => 'Объединение сокращений P.S., P.P.S.', 69 | 'pattern' => '/(^|\040|\t|\>|\r|\n)(p\.\040?)(p\.\040?)?(s\.)([^\<])/ie', 70 | 'replacement' => '$m[1] . $this->tag(trim($m[2]) . " " . ($m[3] ? trim($m[3]) . " " : ""). $m[4], "span", array("class" => "nowrap") ).$m[5] ' 71 | ), 72 | 'nobr_vtch_itd_itp' => array( 73 | 'description' => 'Объединение сокращений и т.д., и т.п., в т.ч.', 74 | 'cycled' => true, 75 | 'pattern' => array( 76 | '/(^|\s|\ \;)и( |\ \;)т\.?[ ]?д(\.|$|\s|\ \;)/ue', 77 | '/(^|\s|\ \;)и( |\ \;)т\.?[ ]?п(\.|$|\s|\ \;)/ue', 78 | '/(^|\s|\ \;)в( |\ \;)т\.?[ ]?ч(\.|$|\s|\ \;)/ue', 79 | ), 80 | 'replacement' => array( 81 | '$m[1].$this->tag("и т. д.", "span", array("class" => "nowrap")).($m[3]!="."? $m[3] : "" )', 82 | '$m[1].$this->tag("и т. п.", "span", array("class" => "nowrap")).($m[3]!="."? $m[3] : "" )', 83 | '$m[1].$this->tag("в т. ч.", "span", array("class" => "nowrap")).($m[3]!="."? $m[3] : "" )', 84 | ) 85 | ), 86 | 'nbsp_te' => array( 87 | 'description' => 'Обработка т.е.', 88 | 'pattern' => '/(^|\s|\ \;)([тТ])\.?[ ]?е\./ue', 89 | 'replacement' => '$m[1].$this->tag($m[2].". е.", "span", array("class" => "nowrap"))', 90 | ), 91 | 'nbsp_money_abbr' => array( 92 | 'description' => 'Форматирование денежных сокращений (расстановка пробелов и привязка названия валюты к числу)', 93 | 'pattern' => '/(\d)((\040|\ \;)?(тыс|млн|млрд)\.?(\040|\ \;)?)?(\040|\ \;)?(руб\.|долл\.|евро|€|€|\$|у[\.]? ?е[\.]?(\s|$))/ieu', 94 | 'replacement' => '$m[1].($m[4]?" ".$m[4].($m[4]=="тыс"?".":""):"")." ".(!preg_match("#у[\\\\.]? ?е[\\\\.]?#iu",$m[7])?$m[7]:"у.е.")', 95 | 'replacement_python' => 'm.group(1)+(u" "+m.group(4)+(u"." if m.group(4)==u"тыс" else u"") if m.group(4) else u"")+u" "+(m.group(7) if not re.match(u"у[\\\\.]? ?е[\\\\.]?",m.group(7),re.I | re.U) else u"у.е.")' 96 | //'replacement_py' => 'm.group(1)+(\" \"+m.group(4)+(m.group(4)==\"\u0442\u044b\u0441\"?\".\" if m.group(4) else \"\"):\"\")+\" \"+(m.group(7) if !preg_match(\"#\u0443[\\\\.]? ?\u0435[\\\\.]?#iu\",m.group(7)) else \"\u0443.\u0435.\")' 97 | ), 98 | 'nbsp_money_abbr_rev' => array( 99 | 'description' => 'Привязка валюты к числу спереди', 100 | 'pattern' => '/(€|€|\$)\s?(\d)/iu', 101 | 'replacement' => '\1 \2' 102 | ), 103 | 'nbsp_org_abbr' => array( 104 | 'description' => 'Привязка сокращений форм собственности к названиям организаций', 105 | 'pattern' => '/([^a-zA-Zа-яёА-ЯЁ]|^)(ООО|ЗАО|ОАО|НИИ|ПБОЮЛ) ([a-zA-Zа-яёА-ЯЁ]|\"|\«\;|\&bdquo\;|<)/u', 106 | 'replacement' => '\1\2 \3' 107 | ), 108 | 'nobr_gost' => array( 109 | 'description' => 'Привязка сокращения ГОСТ к номеру', 110 | 'pattern' => array( 111 | '/(\040|\t|\ \;|^)ГОСТ( |\ \;)?(\d+)((\-|\&minus\;|\&mdash\;)(\d+))?(( |\ \;)(\-|\&mdash\;))?/ieu', 112 | '/(\040|\t|\ \;|^|\>)ГОСТ( |\ \;)?(\d+)(\-|\&minus\;|\&mdash\;)(\d+)/ieu', 113 | ), 114 | 'replacement' => array( 115 | '$m[1].$this->tag("ГОСТ ".$m[3].(isset($m[6])?"–".$m[6]:"").(isset($m[7])?" —":""),"span", array("class"=>"nowrap"))', 116 | '$m[1]."ГОСТ ".$m[3]."–".$m[5]', 117 | ), 118 | ), 119 | /* 120 | 'nobr_vtch_itd_itp' => array( 121 | 'description' => 'Привязка сокращений до н.э., н.э.', 122 | 'pattern' => array( 123 | 124 | //IV в до н.э, в V-VIвв до нэ., третий в. н.э. 125 | 126 | '/(\s|\ \;)и( |\ \;)т\.?[ ]?д\./ue', 127 | '/(\s|\ \;)и( |\ \;)т\.?[ ]?п\./ue', 128 | '/(\s|\ \;)в( |\ \;)т\.?[ ]?ч\./ue', 129 | ), 130 | 'replacement' => array( 131 | '$m[1].$this->tag("и т. д.", "span", array("class" => "nowrap"))', 132 | '$m[1].$this->tag("и т. п.", "span", array("class" => "nowrap"))', 133 | '$m[1].$this->tag("в т. ч.", "span", array("class" => "nowrap"))', 134 | ) 135 | ), 136 | */ 137 | 138 | 139 | ); 140 | } 141 | 142 | ?> -------------------------------------------------------------------------------- /src-php/EMT.Tret.Dash.php: -------------------------------------------------------------------------------- 1 | array( 12 | 'description' => 'Замена символа тире на html конструкцию', 13 | 'pattern' => '/—/iu', 14 | 'replacement' => '—' 15 | ), 16 | 'mdash' => array( 17 | 'description' => 'Тире после кавычек, скобочек, пунктуации', 18 | 'pattern' => array( 19 | '/([a-zа-яё0-9]+|\,|\:|\)|\&(ra|ld)quo\;|\|\"|\>)(\040|\t)(—|\-|\&mdash\;)(\s|$|\<)/ui', 20 | '/(\,|\:|\)|\")(—|\-|\&mdash\;)(\s|$|\<)/ui', 21 | ), 22 | 'replacement' => array( 23 | '\1 —\5', 24 | '\1 —\3', 25 | ), 26 | ), 27 | 'mdash_2' => array( 28 | 'description' => 'Тире после переноса строки', 29 | 'pattern' => '/(\n|\r|^|\>)(\-|\&mdash\;)(\t|\040)/', 30 | 'replacement' => '\1— ' 31 | ), 32 | 'mdash_3' => array( 33 | 'description' => 'Тире после знаков восклицания, троеточия и прочее', 34 | 'pattern' => '/(\.|\!|\?|\&hellip\;)(\040|\t|\ \;)(\-|\&mdash\;)(\040|\t|\ \;)/', 35 | 'replacement' => '\1 — ' 36 | ), 37 | 'iz_za_pod' => array( 38 | 'description' => 'Расстановка дефисов между из-за, из-под', 39 | 'pattern' => '/(\s|\ \;|\>|^)(из)(\040|\t|\ \;)\-?(за|под)([\.\,\!\?\:\;]|\040|\ \;)/uie', 40 | 'replacement' => '($m[1] == " " ? " " : $m[1]) . $m[2]."-".$m[4] . ($m[5] == " "? " " : $m[5])' 41 | ), 42 | 'to_libo_nibud' => array( 43 | 'description' => 'Автоматическая простановка дефисов в обезличенных местоимениях и междометиях', 44 | 'cycled' => true, 45 | 'pattern' => '/(\s|^|\ \;|\>)(кто|кем|когда|зачем|почему|как|что|чем|где|чего|кого)\-?(\040|\t|\ \;)\-?(то|либо|нибудь)([\.\,\!\?\;]|\040|\ \;|$)/uie', 46 | 'replacement' => '($m[1] == " " ? " " : $m[1]) . $m[2]."-".$m[4] . ($m[5] == " "? " " : $m[5])' 47 | ), 48 | 'koe_kak' => array( 49 | 'description' => 'Кое-как, кой-кого, все-таки', 50 | 'cycled' => true, 51 | 'pattern' => array( 52 | '/(\s|^|\ \;|\>)(кое)\-?(\040|\t|\ \;)\-?(как)([\.\,\!\?\;]|\040|\ \;|$)/uie', 53 | '/(\s|^|\ \;|\>)(кой)\-?(\040|\t|\ \;)\-?(кого)([\.\,\!\?\;]|\040|\ \;|$)/uie', 54 | '/(\s|^|\ \;|\>)(вс[её])\-?(\040|\t|\ \;)\-?(таки)([\.\,\!\?\;]|\040|\ \;|$)/uie', 55 | ), 56 | 'replacement' => '($m[1] == " " ? " " : $m[1]) . $m[2]."-".$m[4] . ($m[5] == " "? " " : $m[5])' 57 | ), 58 | 'ka_de_kas' => array( 59 | 'description' => 'Расстановка дефисов с частицами ка, де, кась', 60 | 'disabled' => true, 61 | 'pattern' => array( 62 | '/(\s|^|\ \;|\>)([а-яё]+)(\040|\t|\ \;)(ка)([\.\,\!\?\;]|\040|\ \;|$)/uie', 63 | '/(\s|^|\ \;|\>)([а-яё]+)(\040|\t|\ \;)(де)([\.\,\!\?\;]|\040|\ \;|$)/uie', 64 | '/(\s|^|\ \;|\>)([а-яё]+)(\040|\t|\ \;)(кась)([\.\,\!\?\;]|\040|\ \;|$)/uie', 65 | ), 66 | 'replacement' => '($m[1] == " " ? " " : $m[1]) . $m[2]."-".$m[4] . ($m[5] == " "? " " : $m[5])' 67 | ), 68 | 69 | 70 | 71 | ); 72 | 73 | } 74 | 75 | ?> -------------------------------------------------------------------------------- /src-php/EMT.Tret.Date.php: -------------------------------------------------------------------------------- 1 | 'word-spacing:nowrap;', 13 | ); 14 | 15 | public $rules = array( 16 | 'years' => array( 17 | 'description' => 'Установка тире и пробельных символов в периодах дат', 18 | 'pattern' => '/(с|по|период|середины|начала|начало|конца|конец|половины|в|между|\([cс]\)|\©\;)(\s+|\ \;)([\d]{4})(-|\&mdash\;|\&minus\;)([\d]{4})(( |\ \;)?(г\.г\.|гг\.|гг|г\.|г)([^а-яёa-z]))?/eui', 19 | 'replacement' => '$m[1].$m[2]. (intval($m[3])>=intval($m[5])? $m[3].$m[4].$m[5] : $m[3]."—".$m[5]) . (isset($m[6])? " гг.":"").(isset($m[9])?$m[9]:"")' 20 | ), 21 | 'mdash_month_interval' => array( 22 | 'description' => 'Расстановка тире и объединение в неразрывные периоды месяцев', 23 | 'disabled' => true, 24 | 'pattern' => '/((январ|феврал|сентябр|октябр|ноябр|декабр)([ьяюе]|[её]м)|(апрел|июн|июл)([ьяюе]|ем)|(март|август)([ауе]|ом)?|ма[йяюе]|маем)\-((январ|феврал|сентябр|октябр|ноябр|декабр)([ьяюе]|[её]м)|(апрел|июн|июл)([ьяюе]|ем)|(март|август)([ауе]|ом)?|ма[йяюе]|маем)/iu', 25 | 'replacement' => '\1—\8' 26 | ), 27 | 'nbsp_and_dash_month_interval' => array( 28 | 'description' => 'Расстановка тире и объединение в неразрывные периоды дней', 29 | 'disabled' => true, 30 | 'pattern' => '/([^\>]|^)(\d+)(\-|\&minus\;|\&mdash\;)(\d+)( |\ \;)(января|февраля|марта|апреля|мая|июня|июля|августа|сентября|октября|ноября|декабря)([^\<]|$)/ieu', 31 | 'replacement' => '$m[1].$this->tag($m[2]."—".$m[4]." ".$m[6],"span", array("class"=>"nowrap")).$m[7]' 32 | ), 33 | 'nobr_year_in_date' => array( 34 | 'description' => 'Привязка года к дате', 35 | 'pattern' => array( 36 | '/(\s|\ \;)([0-9]{2}\.[0-9]{2}\.([0-9]{2})?[0-9]{2})(\s|\ \;)?г(\.|\s|\ \;)/eiu', 37 | '/(\s|\ \;)([0-9]{2}\.[0-9]{2}\.([0-9]{2})?[0-9]{2})(\s|\ \;|\.(\s|\ \;|$)|$)/eiu', 38 | ), 39 | 'replacement' => array( 40 | '$m[1].$this->tag($m[2]." г.","span", array("class"=>"nowrap")).($m[5]==="."?"":" ")', 41 | '$m[1].$this->tag($m[2],"span", array("class"=>"nowrap")).$m[4]', 42 | ), 43 | ), 44 | 'space_posle_goda' => array( 45 | 'description' => 'Пробел после года', 46 | 'pattern' => '/(^|\040|\ \;)([0-9]{3,4})(год([ауе]|ом)?)([^a-zа-яё]|$)/ui', 47 | 'replacement' => '\1\2 \3\5' 48 | ), 49 | 'nbsp_posle_goda_abbr' => array( 50 | 'description' => 'Пробел после года', 51 | 'pattern' => '/(^|\040|\ \;|\"|\«\;)([0-9]{3,4})[ ]?(г\.)([^a-zа-яё]|$)/ui', 52 | 'replacement' => '\1\2 \3\4' 53 | ), 54 | 55 | ); 56 | } 57 | 58 | ?> -------------------------------------------------------------------------------- /src-php/EMT.Tret.Etc.php: -------------------------------------------------------------------------------- 1 | 'word-spacing:nowrap;', 13 | ); 14 | 15 | 16 | /** 17 | * Базовые параметры тофа 18 | * 19 | * @var array 20 | */ 21 | public $title = "Прочее"; 22 | public $rules = array( 23 | 'acute_accent' => array( 24 | 'description' => 'Акцент', 25 | 'pattern' => '/(у|е|ы|а|о|э|я|и|ю|ё)\`(\w)/i', 26 | 'replacement' => '\1́\2' 27 | ), 28 | 29 | 30 | 31 | 'word_sup' => array( 32 | 'description' => 'Надстрочный текст после символа ^', 33 | 'pattern' => '/((\s|\ \;|^)+)\^([a-zа-яё0-9\.\:\,\-]+)(\s|\ \;|$|\.$)/ieu', 34 | 'replacement' => '"" . $this->tag($this->tag($m[3],"small"),"sup") . $m[4]' 35 | ), 36 | 'century_period' => array( 37 | 'description' => 'Тире между диапозоном веков', 38 | 'pattern' => '/(\040|\t|\ \;|^)([XIV]{1,5})(-|\&mdash\;)([XIV]{1,5})(( |\ \;)?(в\.в\.|вв\.|вв|в\.|в))/eu', 39 | 'replacement' => '$m[1] .$this->tag($m[2]."—".$m[4]." вв.","span", array("class"=>"nowrap"))' 40 | ), 41 | 'time_interval' => array( 42 | 'description' => 'Тире и отмена переноса между диапозоном времени', 43 | 'pattern' => '/([^\d\>]|^)([\d]{1,2}\:[\d]{2})(-|\&mdash\;|\&minus\;)([\d]{1,2}\:[\d]{2})([^\d\<]|$)/eui', 44 | 'replacement' => '$m[1] . $this->tag($m[2]."—".$m[4],"span", array("class"=>"nowrap")).$m[5]' 45 | ), 46 | 'split_number_to_triads' => array( 47 | 'description' => 'Разбиение числа на триады', 48 | 'pattern' => '/([^a-zA-Z0-9<\)]|^)([0-9]{5,})([^a-zA-Z>\(]|$)/eu', 49 | 'replacement' => '$m[1].str_replace(" "," ",EMT_Lib::split_number($m[2])).$m[3] ' 50 | //'function' => 'split_number' 51 | ), 52 | 'expand_no_nbsp_in_nobr' => array( 53 | 'description' => 'Удаление nbsp в nobr/nowrap тэгах', 54 | 'function' => 'remove_nbsp' 55 | ), 56 | 'nobr_to_nbsp' => array( 57 | 'description' => 'Преобразование nobr в nbsp', 58 | 'disabled' => true, 59 | 'function' => 'nobr_to_nbsp' 60 | ), 61 | ); 62 | 63 | protected function remove_nbsp() 64 | { 65 | $thetag = $this->tag("###", 'span', array('class' => "nowrap")); 66 | $arr = explode("###", $thetag); 67 | $b = preg_quote($arr[0], '/'); 68 | $e = preg_quote($arr[1], '/'); 69 | 70 | $match = '/(^|[^a-zа-яё])([a-zа-яё]+)\ \;('.$b.')/iu'; 71 | do { 72 | $this->_text = preg_replace($match, '\1\3\2 ', $this->_text); 73 | } while(preg_match($match, $this->_text)); 74 | 75 | $match = '/('.$e.')\ \;([a-zа-яё]+)($|[^a-zа-яё])/iu'; 76 | do { 77 | $this->_text = preg_replace($match, ' \2\1\3', $this->_text); 78 | } while(preg_match($match, $this->_text)); 79 | 80 | $this->_text = $this->preg_replace_e('/'.$b.'.*?'.$e.'/iue', 'str_replace(" "," ",$m[0]);' , $this->_text ); 81 | } 82 | 83 | protected function nobr_to_nbsp() 84 | { 85 | $thetag = $this->tag("###", 'span', array('class' => "nowrap")); 86 | $arr = explode("###", $thetag); 87 | $b = preg_quote($arr[0], '/'); 88 | $e = preg_quote($arr[1], '/'); 89 | $this->_text = $this->preg_replace_e('/'.$b.'(.*?)'.$e.'/iue', 'str_replace(" "," ",$m[1]);' , $this->_text ); 90 | } 91 | /* 92 | protected function split_number () { 93 | 94 | $this->preg_replace_e("/([^a-zA-Z<]|^)([0-9]{5,})([^a-zA-Z>]|$)/u", ) 95 | 96 | $match = ; 97 | while(preg_match($match, $this->_text, $m)) { 98 | $repl = ""; 99 | for($i = strlen($m[2]); $i >=0 ; $i-=3) 100 | if($i-3>=0) $repl = ($i>3?" ":"").substr($m[2], $i-3, 3) . $repl; else $repl = substr($m[2], 0, $i) . $repl; 101 | $this->_text = str_replace($m[1], $repl, $this->_text); 102 | } 103 | }*/ 104 | 105 | } 106 | 107 | /**PYTHON 108 | def remove_nbsp(self): 109 | thetag = self.tag(u"###", u'span', {u'class': u"nowrap"}) 110 | arr = thetag.split(u"###") 111 | b = re.escape(arr[0]) 112 | e = re.escape(arr[1]) 113 | 114 | match = u'/(^|[^a-zа-яё])([a-zа-яё]+)\ \;(' + b + u')/iu' 115 | p = EMT_Lib.parse_preg_pattern(match) 116 | while (True): 117 | self._text = EMT_Lib.preg_replace(match, u"\\1\\3\\2 ", self._text) 118 | if not (re.match(p['pattern'], self._text, p['flags'])): 119 | break 120 | 121 | match = u'/(' + e + u')\ \;([a-zа-яё]+)($|[^a-zа-яё])/iu' 122 | p = EMT_Lib.parse_preg_pattern(match) 123 | while (True): 124 | self._text = EMT_Lib.preg_replace(match, u" \\2\\1\\3", self._text) 125 | if not (re.match(p['pattern'], self._text, p['flags'])): 126 | break 127 | 128 | self._text = EMT_Lib.preg_replace(u'/' + b + u'.*?' + e + u'/iue', u'EMT_Lib.str_replace(" "," ",m.group(0))' , self._text ) 129 | 130 | def nobr_to_nbsp(self): 131 | thetag = self.tag(u"###", u'span', {u'class': u"nowrap"}) 132 | arr = thetag.split(u"###") 133 | b = re.escape(arr[0]) 134 | e = re.escape(arr[1]) 135 | 136 | self._text = EMT_Lib.preg_replace(u'/' + b + u'(.*?)' + e + u'/iue', u'EMT_Lib.str_replace(" "," ",m.group(1))' , self._text ) 137 | 138 | PYTHON**/ 139 | 140 | ?> -------------------------------------------------------------------------------- /src-php/EMT.Tret.Nobr.php: -------------------------------------------------------------------------------- 1 | 'word-spacing:nowrap;', 13 | ); 14 | 15 | public $rules = array( 16 | 17 | 'super_nbsp' => array( 18 | 'description' => 'Привязка союзов и предлогов к написанным после словам', 19 | 'pattern' => '/(\s|^|\&(la|bd)quo\;|\>|\(|\&mdash\;\ \;)([a-zа-яё]{1,2}\s+)([a-zа-яё]{1,2}\s+)?([a-zа-яё0-9\-]{2,}|[0-9])/ieu', 20 | 'replacement' => '$m[1] . trim($m[3]) . " " . ($m[4] ? trim($m[4]) . " " : "") . $m[5]' 21 | ), 22 | 'nbsp_in_the_end' => array( 23 | 'description' => 'Привязка союзов и предлогов к предыдущим словам в случае конца предложения', 24 | 'pattern' => '/([a-zа-яё0-9\-]{3,}) ([a-zа-яё]{1,2})\.( [A-ZА-ЯЁ]|$)/u', 25 | 'replacement' => '\1 \2.\3' 26 | ), 27 | 'phone_builder' => array( 28 | 'description' => 'Объединение в неразрывные конструкции номеров телефонов', 29 | 'pattern' => 30 | array( 31 | '/([^\d\+]|^)([\+]?[0-9]{1,3})( |\ \;|\&thinsp\;)([0-9]{3,4}|\([0-9]{3,4}\))( |\ \;|\&thinsp\;)([0-9]{2,3})(-|\&minus\;)([0-9]{2})(-|\&minus\;)([0-9]{2})([^\d]|$)/e', 32 | '/([^\d\+]|^)([\+]?[0-9]{1,3})( |\ \;|\&thinsp\;)([0-9]{3,4}|[0-9]{3,4})( |\ \;|\&thinsp\;)([0-9]{2,3})(-|\&minus\;)([0-9]{2})(-|\&minus\;)([0-9]{2})([^\d]|$)/e', 33 | ), 34 | 'replacement' => 35 | array( 36 | '$m[1] .(($m[1] == ">" || $m[11] == "<") ? $m[2]." ".$m[4]." ".$m[6]."-".$m[8]."-".$m[10] :$this->tag($m[2]." ".$m[4]." ".$m[6]."-".$m[8]."-".$m[10], "span", array("class"=>"nowrap")) ).$m[11]', 37 | '$m[1] .(($m[1] == ">" || $m[11] == "<") ? $m[2]." ".$m[4]." ".$m[6]."-".$m[8]."-".$m[10] :$this->tag($m[2]." ".$m[4]." ".$m[6]."-".$m[8]."-".$m[10], "span", array("class"=>"nowrap")) ).$m[11]', 38 | ), 39 | ), 40 | 'phone_builder_v2' => array( 41 | 'description' => 'Дополнительный формат номеров телефонов', 42 | 'pattern' => '/([^\d]|^)\+\s?([0-9]{1})\s?\(([0-9]{3,4})\)\s?(\d{3})(\d{2})(\d{2})([^\d]|$)/ie', 43 | 'replacement' => '$m[1].$this->tag("+".$m[2]." ".$m[3]." ".$m[4]."-".$m[5]."-".$m[6], "span", array("class" => "nowrap")).$m[7]', 44 | ), 45 | 'ip_address' => array( 46 | 'description' => 'Объединение IP-адресов', 47 | 'pattern' => '/(\s|\ \;|^)(\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3})/ie', 48 | 'replacement' => '$m[1] . $this->nowrap_ip_address($m[2])' 49 | ), 50 | 'dots_for_surname_abbr' => array( 51 | 'disabled' => true, 52 | 'description' => 'Простановка точек к инициалам у фамилии', 53 | 'pattern' => 54 | array( 55 | '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ])\.?(\s|\ \;)?([А-ЯЁ])(\s|\ \;)([А-ЯЁ][а-яё]+)(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', 56 | '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ][а-яё]+)(\s|\ \;)([А-ЯЁ])\.?(\s|\ \;)?([А-ЯЁ])\.?(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', 57 | ), 58 | 'replacement' => 59 | array( 60 | '$m[1].$this->tag($m[2].". ".$m[4].". ".$m[6], "span", array("class" => "nowrap")).$m[7]', 61 | '$m[1].$this->tag($m[2]." ".$m[4].". ".$m[6].".", "span", array("class" => "nowrap")).$m[7]', 62 | ), 63 | ), 64 | 'spaces_nobr_in_surname_abbr' => array( 65 | 'description' => 'Привязка инициалов к фамилиям', 66 | 'pattern' => 67 | array( 68 | '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ])\.(\s|\ \;)?([А-ЯЁ])\.(\s|\ \;)?([А-ЯЁ][а-яё]+)(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', 69 | '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ][а-яё]+)(\s|\ \;)([А-ЯЁ])\.(\s|\ \;)?([А-ЯЁ])\.(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', 70 | '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ])(\s|\ \;)?([А-ЯЁ])(\s|\ \;)([А-ЯЁ][а-яё]+)(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', 71 | '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ][а-яё]+)(\s|\ \;)([А-ЯЁ])(\s|\ \;)?([А-ЯЁ])(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', 72 | //'/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([A-Z])\.?(\s|\ \;)?([A-Z])(\.(\s|\ \;)?|(\s|\ \;))([A-Z][a-z]+)(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', 73 | //'/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([A-Z][a-z]+)(\s|\ \;)([A-Z])\.?(\s|\ \;)?([A-Z])\.?(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', 74 | ), 75 | 'replacement' => 76 | array( 77 | '$m[1].$this->tag($m[2].". ".$m[4].". ".$m[6], "span", array("class" => "nowrap")).$m[7]', 78 | '$m[1].$this->tag($m[2]." ".$m[4].". ".$m[6].".", "span", array("class" => "nowrap")).$m[7]', 79 | '$m[1].$this->tag($m[2].(isset($m[3])? " " : "" ).$m[4].(isset($m[5])? " " : "" ).$m[6], "span", array("class" => "nowrap")).$m[7]', 80 | '$m[1].$this->tag($m[2]." ".$m[4].(isset($m[5])? " " : "" ).$m[6], "span", array("class" => "nowrap")).$m[7]', 81 | //'$m[1].$this->tag($m[2].". ".$m[4].". ".$m[8], "span", array("class" => "nowrap")).$m[9]', 82 | //'$m[1].$this->tag($m[2]." ".$m[4].". ".$m[6].".", "span", array("class" => "nowrap")).$m[7]', 83 | ), 84 | ), 85 | 'nbsp_before_particle' => array( 86 | 'description' => 'Неразрывный пробел перед частицей', 87 | 'pattern' => '/(\040|\t)+(ли|бы|б|же|ж)(\ \;|\.|\,|\:|\;|\&hellip\;|\?|\s)/iue', 88 | 'replacement' => '" ".$m[2] . ($m[3] == " " ? " " : $m[3])' 89 | ), 90 | 'nbsp_v_kak_to' => array( 91 | 'description' => 'Неразрывный пробел в как то', 92 | 'pattern' => '/как то\:/ui', 93 | 'replacement' => 'как то:' 94 | ), 95 | 'nbsp_celcius' => array( 96 | 'description' => 'Привязка градусов к числу', 97 | 'pattern' => '/(\s|^|\>|\ \;)(\d+)( |\ \;)?(°|\°\;)(C|С)(\s|\.|\!|\?|\,|$|\ \;|\;)/iu', 98 | 'replacement' => '\1\2 \4C\6' 99 | ), 100 | 'hyphen_nowrap_in_small_words' => array( 101 | 'description' => 'Обрамление пятисимвольных слов разделенных дефисом в неразрывные блоки', 102 | 'disabled' => true, 103 | 'cycled' => true, 104 | 'pattern' => '/(\ \;|\s|\>|^)([a-zа-яё]{1}\-[a-zа-яё]{4}|[a-zа-яё]{2}\-[a-zа-яё]{3}|[a-zа-яё]{3}\-[a-zа-яё]{2}|[a-zа-яё]{4}\-[a-zа-яё]{1}|когда\-то|кое\-как|кой\-кого|вс[её]\-таки|[а-яё]+\-(кась|ка|де))(\s|\.|\,|\!|\?|\ \;|\&hellip\;|$)/uie', 105 | 'replacement' => '$m[1] . $this->tag($m[2], "span", array("class"=>"nowrap")) . $m[4]', 106 | ), 107 | 'hyphen_nowrap' => array( 108 | 'description' => 'Отмена переноса слова с дефисом', 109 | 'disabled' => true, 110 | 'cycled' => true, 111 | 'pattern' => '/(\ \;|\s|\>|^)([a-zа-яё]+)((\-([a-zа-яё]+)){1,2})(\s|\.|\,|\!|\?|\ \;|\&hellip\;|$)/uie', 112 | 'replacement' => '$m[1] . $this->tag($m[2].$m[3], "span", array("class"=>"nowrap")) . $m[6]' 113 | ), 114 | ); 115 | 116 | /** 117 | * Объединение IP-адрессов в неразрывные конструкции (IPv4 only) 118 | * 119 | * @param unknown_type $triads 120 | * @return unknown 121 | */ 122 | protected function nowrap_ip_address($triads) 123 | { 124 | $triad = explode('.', $triads); 125 | $addTag = true; 126 | 127 | foreach ($triad as $value) { 128 | $value = (int) $value; 129 | if ($value > 255) { 130 | $addTag = false; 131 | break; 132 | } 133 | } 134 | 135 | if (true === $addTag) { 136 | $triads = $this->tag($triads, 'span', array('class' => "nowrap")); 137 | } 138 | 139 | return $triads; 140 | } 141 | } 142 | /**PYTHON 143 | # * Объединение IP-адрессов в неразрывные конструкции (IPv4 only) 144 | # * 145 | # * @param unknown_type $triads 146 | # * @return unknown 147 | def nowrap_ip_address(self, triads): 148 | triad = triads.split('.') 149 | addTag = True 150 | 151 | for value in triad: 152 | value = int(value) 153 | if (value > 255): 154 | addTag = false 155 | break 156 | 157 | if (addTag == True): 158 | triads = self.tag(triads, 'span', {'class': "nowrap"}) 159 | 160 | return triads 161 | 162 | PYTHON**/ 163 | ?> -------------------------------------------------------------------------------- /src-php/EMT.Tret.Number.php: -------------------------------------------------------------------------------- 1 | array( 14 | 'description' => 'Расстановка знака минус между числами', 15 | 'pattern' => '/(\d+)\-(\d)/i', 16 | 'replacement' => '\1−\2' 17 | ), 18 | 'minus_in_numbers_range' => array( 19 | 'description' => 'Расстановка знака минус между диапозоном чисел', 20 | 'pattern' => '/(^|\s|\ \;)(\&minus\;|\-)(\d+)(\.\.\.|\&hellip\;)(\s|\ \;)?(\+|\-|\&minus\;)?(\d+)/ie', 21 | 'replacement' => '$m[1] ."−".$m[3] . $m[4].$m[5].($m[6]=="+"?$m[6]:"−").$m[7]' 22 | ), 23 | 'auto_times_x' => array( 24 | 'description' => 'Замена x на символ × в размерных единицах', 25 | 'cycled' => true, 26 | 'pattern' => '/([^a-zA-Z><]|^)(\×\;)?(\d+)(\040*)(x|х)(\040*)(\d+)([^a-zA-Z><]|$)/u', 27 | 'replacement' => '\1\2\3×\7\8' 28 | ), 29 | 'numeric_sub' => array( 30 | 'description' => 'Нижний индекс', 31 | 'pattern' => '/([a-zа-яё0-9])\_([\d]{1,3})([^@а-яёa-z0-9]|$)/ieu', 32 | 'replacement' => '$m[1] . $this->tag($this->tag($m[2],"small"),"sub") . $m[3]' 33 | ), 34 | 'numeric_sup' => array( 35 | 'description' => 'Верхний индекс', 36 | 'pattern' => '/([a-zа-яё0-9])\^([\d]{1,3})([^а-яёa-z0-9]|$)/ieu', 37 | 'replacement' => '$m[1] . $this->tag($this->tag($m[2],"small"),"sup") . $m[3]' 38 | ), 39 | 'simple_fraction' => array( 40 | 'description' => 'Замена дробей 1/2, 1/4, 3/4 на соответствующие символы', 41 | 'pattern' => array('/(^|\D)1\/(2|4)(\D)/', '/(^|\D)3\/4(\D)/'), 42 | 'replacement' => array('\1&frac1\2;\3', '\1¾\2') 43 | ), 44 | 'math_chars' => array( 45 | 'description' => 'Математические знаки больше/меньше/плюс минус/неравно', 46 | 'pattern' => array('/!=/', '/\<=/', '/([^=]|^)\>=/', '/~=/', '/\+-/'), 47 | 'replacement' => array('≠', '≤', '\1≥', '≅', '±' ) 48 | ), 49 | 50 | 'thinsp_between_number_triads' => array( 51 | 'description' => 'Объединение триад чисел полупробелом', 52 | 'pattern' => '/([0-9]{1,3}( [0-9]{3}){1,})(.|$)/ue', 53 | 'replacement' => '($m[3]=="-"? $m[0]:str_replace(" "," ",$m[1]).$m[3])' 54 | ), 55 | 'thinsp_between_no_and_number' => array( 56 | 'description' => 'Пробел между симоволом номера и числом', 57 | 'pattern' => '/(№|\№\;)(\s| )*(\d)/iu', 58 | 'replacement' => '№ \3' 59 | ), 60 | 'thinsp_between_sect_and_number' => array( 61 | 'description' => 'Пробел между параграфом и числом', 62 | 'pattern' => '/(§|\§\;)(\s| )*(\d+|[IVX]+|[a-zа-яё]+)/ui', 63 | 'replacement' => '§ \3' 64 | ), 65 | ); 66 | } 67 | 68 | ?> -------------------------------------------------------------------------------- /src-php/EMT.Tret.OptAlign.php: -------------------------------------------------------------------------------- 1 | "margin-right:0.3em;", 12 | "oa_obracket_sp_b" => "margin-left:-0.3em;", 13 | "oa_obracket_nl_b" => "margin-left:-0.3em;", 14 | "oa_comma_b" => "margin-right:-0.2em;", 15 | "oa_comma_e" => "margin-left:0.2em;", 16 | 'oa_oquote_nl' => "margin-left:-0.44em;", 17 | 'oa_oqoute_sp_s' => "margin-right:0.44em;", 18 | 'oa_oqoute_sp_q' => "margin-left:-0.44em;", 19 | ); 20 | 21 | /** 22 | * Базовые параметры тофа 23 | * 24 | * @var array 25 | */ 26 | public $title = "Оптическое выравнивание"; 27 | public $rules = array( 28 | 'oa_oquote' => array( 29 | 'description' => 'Оптическое выравнивание открывающей кавычки', 30 | //'disabled' => true, 31 | 'pattern' => array( 32 | '/([a-zа-яё\-]{3,})(\040|\ \;|\t)(\«\;)/uie', 33 | '/(\n|\r|^)(\«\;)/ei' 34 | ), 35 | 'replacement' => array( 36 | '$m[1] . $this->tag($m[2], "span", array("class"=>"oa_oqoute_sp_s")) . $this->tag($m[3], "span", array("class"=>"oa_oqoute_sp_q"))', 37 | '$m[1] . $this->tag($m[2], "span", array("class"=>"oa_oquote_nl"))', 38 | ), 39 | ), 40 | 'oa_oquote_extra' => array( 41 | 'description' => 'Оптическое выравнивание кавычки', 42 | //'disabled' => true, 43 | 'function' => 'oaquote_extra' 44 | ), 45 | 'oa_obracket_coma' => array( 46 | 'description' => 'Оптическое выравнивание для пунктуации (скобка)', 47 | //'disabled' => true, 48 | 'pattern' => array( 49 | '/(\040|\ \;|\t)\(/ei', 50 | '/(\n|\r|^)\(/ei', 51 | //'/([а-яёa-z0-9]+)\,(\040+)/iue', 52 | ), 53 | 'replacement' => array( 54 | '$this->tag($m[1], "span", array("class"=>"oa_obracket_sp_s")) . $this->tag("(", "span", array("class"=>"oa_obracket_sp_b"))', 55 | '$m[1] . $this->tag("(", "span", array("class"=>"oa_obracket_nl_b"))', 56 | //'$m[1] . $this->tag(",", "span", array("class"=>"oa_comma_b")) . $this->tag(" ", "span", array("class"=>"oa_comma_e"))', 57 | ), 58 | ), 59 | 60 | ); 61 | 62 | /** 63 | * Если стоит открывающая кавычка после

надо делать её висячей 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_text, "")!==false ? "" : (strpos($this->_text,"\r\n")!==false ? "\r\n\r\n" :"\n\n"); 87 | 88 | $texts_in = explode($exp, $this->_text); 89 | $texts_out = array(); 90 | 91 | foreach($texts_in as $textx) { 92 | 93 | $okposstack = array('0'); 94 | $okpos = 0; 95 | $level = 0; 96 | $off = 0; 97 | while(true) 98 | { 99 | $p = EMT_Lib::strpos_ex($textx, array("«", "»"), $off); 100 | if($p===false) break; 101 | if($p['str'] == "«") 102 | { 103 | if($level>0) if(!$this->is_on('no_bdquotes')) $this->inject_in($p['pos'], self::QUOTE_CRAWSE_OPEN, $textx); 104 | $level++; 105 | } 106 | if($p['str'] == "»") 107 | { 108 | $level--; 109 | if($level>0) if(!$this->is_on('no_bdquotes')) $this->inject_in($p['pos'], self::QUOTE_CRAWSE_CLOSE, $textx); 110 | } 111 | $off = $p['pos']+strlen($p['str']); 112 | if($level == 0) 113 | { 114 | $okpos = $off; 115 | array_push($okposstack, $okpos); 116 | } elseif($level<0) // уровень стал меньше нуля 117 | { 118 | if(!$this->is_on('no_inches')) 119 | { 120 | do{ 121 | $lokpos = array_pop($okposstack); 122 | $k = substr($textx, $lokpos, $off-$lokpos); 123 | $k = str_replace(self::QUOTE_CRAWSE_OPEN, self::QUOTE_FIRS_OPEN, $k); 124 | $k = str_replace(self::QUOTE_CRAWSE_CLOSE, self::QUOTE_FIRS_CLOSE, $k); 125 | //$k = preg_replace("/(^|[^0-9])([0-9]+)\»\;/ui", '\1\2″', $k, 1, $amount); 126 | 127 | $amount = 0; 128 | $__ax = preg_match_all("/(^|[^0-9])([0-9]+)\»\;/ui", $k, $m); 129 | $__ay = 0; 130 | if($__ax) 131 | { 132 | $k = preg_replace_callback("/(^|[^0-9])([0-9]+)\»\;/ui", 133 | EMT_Lib::create_function('$m','global $__ax,$__ay; $__ay++; if($__ay==$__ax){ return $m[1].$m[2]."″";} return $m[0];'), 134 | $k); 135 | $amount = 1; 136 | } 137 | 138 | 139 | 140 | } while(($amount==0) && count($okposstack)); 141 | 142 | // успешно сделали замену 143 | if($amount == 1) 144 | { 145 | // заново просмотрим содержимое 146 | $textx = substr($textx, 0, $lokpos). $k . substr($textx, $off); 147 | $off = $lokpos; 148 | $level = 0; 149 | continue; 150 | } 151 | 152 | // иначе просто заменим последнюю явно на " от отчаяния 153 | if($amount == 0) 154 | { 155 | // говорим, что всё в порядке 156 | $level = 0; 157 | $textx = substr($textx, 0, $p['pos']). '"' . substr($textx, $off); 158 | $off = $p['pos'] + strlen('"'); 159 | $okposstack = array($off); 160 | continue; 161 | } 162 | } 163 | } 164 | 165 | 166 | } 167 | // не совпало количество, отменяем все подкавычки 168 | if($level != 0 ){ 169 | 170 | // закрывающих меньше, чем надо 171 | if($level>0) 172 | { 173 | $k = substr($textx, $okpos); 174 | $k = str_replace(self::QUOTE_CRAWSE_OPEN, self::QUOTE_FIRS_OPEN, $k); 175 | $k = str_replace(self::QUOTE_CRAWSE_CLOSE, self::QUOTE_FIRS_CLOSE, $k); 176 | $textx = substr($textx, 0, $okpos). $k; 177 | } 178 | } 179 | $texts_out[] = $textx; 180 | } 181 | $this->_text = implode($exp, $texts_out); 182 | } 183 | 184 | } 185 | 186 | 187 | 188 | /**PYTHON 189 | def inject_in(self, pos, text, chtext): 190 | chtext = (chtext[0:pos] if pos>0 else u'') + text + chtext[pos+len(text):] 191 | return chtext 192 | 193 | def build_sub_quotations(self): 194 | global __ax,__ay 195 | 196 | exp = "" if self._text.find( "")>=0 else ( "\r\n\r\n" if self._text.find( "\r\n")>=0 else "\n\n") 197 | 198 | texts_in = self._text.split(exp) 199 | texts_out = [] 200 | 201 | for textx in texts_in: 202 | okposstack = [0] 203 | okpos = 0 204 | level = 0 205 | off = 0 206 | while True: 207 | p = EMT_Lib.strpos_ex(textx, ["«", "»"], off) 208 | 209 | if isinstance(p, bool) and (p == False): 210 | break 211 | if (p['str'] == "«"): 212 | if (level>0) and (not self.is_on('no_bdquotes')): 213 | textx = self.inject_in(p['pos'], QUOTE_CRAWSE_OPEN, textx) #TODO::: WTF self::QUOTE_CRAWSE_OPEN ??? 214 | level += 1; 215 | 216 | if (p['str'] == "»"): 217 | level -= 1 218 | if (level>0) and (not self.is_on('no_bdquotes')): 219 | textx = self.inject_in(p['pos'], QUOTE_CRAWSE_CLOSE, textx) #TODO::: WTF self::QUOTE_CRAWSE_OPEN ??? 220 | 221 | off = p['pos'] + len(p['str']) 222 | 223 | if(level == 0): 224 | okpos = off 225 | okposstack.append(okpos) 226 | 227 | elif (level<0): # // уровень стал меньше нуля 228 | if(not self.is_on('no_inches')): 229 | 230 | while (True): 231 | lokpos = okposstack.pop(len(okposstack)-1) 232 | k = EMT_Lib.substr(textx, lokpos, off - lokpos) 233 | k = EMT_Lib.str_replace(QUOTE_CRAWSE_OPEN, QUOTE_FIRS_OPEN, k) 234 | k = EMT_Lib.str_replace(QUOTE_CRAWSE_CLOSE, QUOTE_FIRS_CLOSE, k) 235 | #//$k = preg_replace("/(^|[^0-9])([0-9]+)\»\;/ui", '\1\2″', $k, 1, $amount); 236 | 237 | amount = 0 238 | m = re.findall("(^|[^0-9])([0-9]+)\»\;", k, re.I | re.U) 239 | __ax = len(m) 240 | __ay = 0 241 | if(__ax): 242 | def quote_extra_replace_function(m): 243 | global __ax,__ay 244 | __ay+=1 245 | if __ay==__ax: 246 | return m.group(1)+m.group(2)+"″" 247 | return m.group(0) 248 | 249 | k = re.sub("(^|[^0-9])([0-9]+)\»\;", 250 | quote_extra_replace_function, 251 | k, 0, re.I | re.U); 252 | amount = 1 253 | 254 | if not ((amount==0) and len(okposstack)): 255 | break 256 | 257 | 258 | #// успешно сделали замену 259 | if (amount == 1): 260 | #// заново просмотрим содержимое 261 | textx = EMT_Lib.substr(textx, 0, lokpos) + k + EMT_Lib.substr(textx, off) 262 | off = lokpos 263 | level = 0 264 | continue 265 | 266 | #// иначе просто заменим последнюю явно на " от отчаяния 267 | if (amount == 0): 268 | #// говорим, что всё в порядке 269 | level = 0 270 | textx = EMT_Lib.substr(textx, 0, p['pos']) + '"' + EMT_Lib.substr(textx, off) 271 | off = p['pos'] + len('"') 272 | okposstack = [off] 273 | continue 274 | 275 | #// не совпало количество, отменяем все подкавычки 276 | if (level != 0 ): 277 | 278 | #// закрывающих меньше, чем надо 279 | if (level>0): 280 | k = EMT_Lib.substr(textx, okpos) 281 | k = EMT_Lib.str_replace(QUOTE_CRAWSE_OPEN, QUOTE_FIRS_OPEN, k) 282 | k = EMT_Lib.str_replace(QUOTE_CRAWSE_CLOSE, QUOTE_FIRS_CLOSE, k) 283 | textx = EMT_Lib.substr(textx, 0, okpos) + k 284 | texts_out.append(textx) 285 | 286 | self._text = exp.join(texts_out) 287 | 288 | 289 | 290 | PYTHON**/ 291 | 292 | 293 | ?> -------------------------------------------------------------------------------- /src-php/EMT.Tret.Space.php: -------------------------------------------------------------------------------- 1 | 'word-spacing:nowrap;', 16 | ); 17 | 18 | public $rules = array( 19 | 'nobr_twosym_abbr' => array( 20 | 'description' => 'Неразрывный перед 2х символьной аббревиатурой', 21 | 'pattern' => '/([a-zA-Zа-яёА-ЯЁ])(\040|\t)+([A-ZА-ЯЁ]{2})([\s\;\.\?\!\:\(\"]|\&(ra|ld)quo\;|$)/u', 22 | 'replacement' => '\1 \3\4' 23 | ), 24 | 'remove_space_before_punctuationmarks' => array( 25 | 'description' => 'Удаление пробела перед точкой, запятой, двоеточием, точкой с запятой', 26 | 'pattern' => '/((\040|\t|\ \;)+)([\,\:\.\;\?])(\s+|$)/', 27 | 'replacement' => '\3\4' 28 | ), 29 | 'autospace_after_comma' => array( 30 | 'description' => 'Пробел после запятой', 31 | 'pattern' => array( 32 | '/(\040|\t|\ \;)\,([а-яёa-z0-9])/iu', 33 | '/([^0-9])\,([а-яёa-z0-9])/iu', 34 | ), 35 | 'replacement' => array( 36 | ', \2', 37 | '\1, \2' 38 | ), 39 | ), 40 | 'autospace_after_pmarks' => array( 41 | 'description' => 'Пробел после знаков пунктуации, кроме точки', 42 | 'pattern' => '/(\040|\t|\ \;|^|\n)([a-zа-яё0-9]+)(\040|\t|\ \;)?(\:|\)|\,|\&hellip\;|(?:\!|\?)+)([а-яёa-z])/iu', 43 | 'replacement' => '\1\2\4 \5' 44 | ), 45 | 'autospace_after_dot' => array( 46 | 'description' => 'Пробел после точки', 47 | 'pattern' => array( 48 | '/(\040|\t|\ \;|^)([a-zа-яё0-9]+)(\040|\t|\ \;)?\.([а-яёa-z]{5,})($|[^a-zа-яё])/iue', 49 | '/(\040|\t|\ \;|^)([a-zа-яё0-9]+)\.([а-яёa-z]{1,4})($|[^a-zа-яё])/iue', 50 | ), 51 | 'replacement' => array( 52 | //'\1\2. \4', 53 | '$m[1].$m[2]."." .( $m[5] == "." ? "" : " ").$m[4].$m[5]', 54 | '$m[1].$m[2]."." .(in_array(EMT_Lib::strtolower($m[3]), $this->domain_zones)? "":( $m[4] == "." ? "" : " ")). $m[3].$m[4]' 55 | ), 56 | ), 57 | 'autospace_after_hellips' => array( 58 | 'description' => 'Пробел после знаков троеточий с вопросительным или восклицательными знаками', 59 | 'pattern' => '/([\?\!]\.\.)([а-яёa-z])/iu', 60 | 'replacement' => '\1 \2' 61 | ), 62 | 'many_spaces_to_one' => array( 63 | 'description' => 'Удаление лишних пробельных символов и табуляций', 64 | 'pattern' => '/(\040|\t)+/', 65 | 'replacement' => ' ' 66 | ), 67 | 'clear_percent' => array( 68 | 'description' => 'Удаление пробела перед символом процента', 69 | 'pattern' => '/(\d+)([\t\040]+)\%/', 70 | 'replacement' => '\1%' 71 | ), 72 | 'nbsp_before_open_quote' => array( 73 | 'description' => 'Неразрывный пробел перед открывающей скобкой', 74 | 'pattern' => '/(^|\040|\t|>)([a-zа-яё]{1,2})\040(\«\;|\&bdquo\;)/u', 75 | 'replacement' => '\1\2 \3' 76 | ), 77 | 78 | 'nbsp_before_month' => array( 79 | 'description' => 'Неразрывный пробел в датах перед числом и месяцем', 80 | 'pattern' => '/(\d)(\s)+(января|февраля|марта|апреля|мая|июня|июля|августа|сентября|октября|ноября|декабря)([^\<]|$)/iu', 81 | 'replacement' => '\1 \3\4' 82 | ), 83 | 'spaces_on_end' => array( 84 | 'description' => 'Удаление пробелов в конце текста', 85 | 'pattern' => '/ +$/', 86 | 'replacement' => '' 87 | ), 88 | 'no_space_posle_hellip' => array( 89 | 'description' => 'Отсутстввие пробела после троеточия после открывающей кавычки', 90 | 'pattern' => '/(\«\;|\&bdquo\;)( |\ \;)?\&hellip\;( |\ \;)?([a-zа-яё])/ui', 91 | 'replacement' => '\1…\4' 92 | ), 93 | 'space_posle_goda' => array( 94 | 'description' => 'Пробел после года', 95 | 'pattern' => '/(^|\040|\ \;)([0-9]{3,4})(год([ауе]|ом)?)([^a-zа-яё]|$)/ui', 96 | 'replacement' => '\1\2 \3\5' 97 | ), 98 | ); 99 | } 100 | 101 | ?> -------------------------------------------------------------------------------- /src-php/EMT.Tret.Symbol.php: -------------------------------------------------------------------------------- 1 | 'word-spacing:nowrap;', 16 | ); 17 | 18 | 19 | public $title = "Специальные символы"; 20 | public $rules = array( 21 | 'tm_replace' => array( 22 | 'description' => 'Замена (tm) на символ торговой марки', 23 | 'pattern' => '/([\040\t])?\(tm\)/i', 24 | 'replacement' => '™' 25 | ), 26 | 'r_sign_replace' => array( 27 | 'description' => 'Замена (R) на символ зарегистрированной торговой марки', 28 | 'pattern' => array( 29 | '/(.|^)\(r\)(.|$)/ie', 30 | //'/([^\>]|^)\(r\)([^\<]|$)/ie', 31 | //'/\>\(r\)\ array( 34 | //'$m[1].$this->tag("®", "sup").$m[2]', 35 | '$m[1]."®".$m[2]', 36 | //'>®<' 37 | ), 38 | ), 39 | 'copy_replace' => array( 40 | 'description' => 'Замена (c) на символ копирайт', 41 | 'pattern' => array( 42 | '/\((c|с)\)\s+/iu', 43 | '/\((c|с)\)($|\.|,|!|\?)/iu', 44 | ), 45 | 'replacement' => array( 46 | '© ', 47 | '©\2', 48 | ), 49 | ), 50 | 'apostrophe' => array( 51 | 'description' => 'Расстановка правильного апострофа в текстах', 52 | 'pattern' => '/(\s|^|\>|\&rsquo\;)([a-zа-яё]{1,})\'([a-zа-яё]+)/ui', 53 | 'replacement' => '\1\2’\3', 54 | 'cycled' => true 55 | ), 56 | /* 57 | 'ru_apostrophe' => array( 58 | 'description' => 'Расстановка правильного апострофа в русских текстах', 59 | 'pattern' => '/(\s|^|\>)([а-яё]+)\'([а-яё]+)/iu', 60 | 'replacement' => '\1\2’\3' 61 | ), 62 | */ 63 | 'degree_f' => array( 64 | 'description' => 'Градусы по Фаренгейту', 65 | 'pattern' => '/([0-9]+)F($|\s|\.|\,|\;|\:|\ \;|\?|\!)/eu', 66 | 'replacement' => '"".$this->tag($m[1]." °F","span", array("class"=>"nowrap")) .$m[2]' 67 | ), 68 | 'euro_symbol' => array( 69 | 'description' => 'Символ евро', 70 | 'simple_replace' => true, 71 | 'pattern' => '€', 72 | 'replacement' => '€' 73 | ), 74 | 'arrows_symbols' => array( 75 | 'description' => 'Замена стрелок вправо-влево на html коды', 76 | //'pattern' => array('/(\s|\>|\ \;|^)\-\>($|\s|\ \;|\<)/', '/(\s|\>|\ \;|^|;)\<\-(\s|\ \;|$|\<)/', '/→/u', '/←/u'), 77 | //'pattern' => array('/\-\>($|\s|\ \;|\<)/', '/(\s|\>|\ \;|^|;)\<\-(\s|\ \;|$|\<)/', '/→/u', '/←/u'), 78 | 'pattern' => array('/([^-]|^)\-\>/', '/\<\-/', '/→/u', '/←/u'), 79 | //'replacement' => array('\1→\2', '\1←\2', '→', '←' ), 80 | 'replacement' => array('\1→', '←', '→', '←' ), 81 | ), 82 | ); 83 | } 84 | 85 | ?> -------------------------------------------------------------------------------- /src-php/EMT.Tret.Text.php: -------------------------------------------------------------------------------- 1 | 'word-spacing:nowrap;', 11 | ); 12 | 13 | /** 14 | * Базовые параметры тофа 15 | * 16 | * @var array 17 | */ 18 | public $title = "Текст и абзацы"; 19 | public $rules = array( 20 | 'auto_links' => array( 21 | 'description' => 'Выделение ссылок из текста', 22 | 'pattern' => '/(\s|^)(http|ftp|mailto|https)(:\/\/)([^\s\,\!\<]{4,})(\s|\.|\,|\!|\?|\<|$)/ieu', 23 | 'replacement' => '$m[1] . $this->tag((substr($m[4],-1)=="."?substr($m[4],0,-1):$m[4]), "a", array("href" => $m[2].$m[3].(substr($m[4],-1)=="."?substr($m[4],0,-1):$m[4]))) . (substr($m[4],-1)=="."?".":"") .$m[5]' 24 | ), 25 | 'email' => array( 26 | 'description' => 'Выделение эл. почты из текста', 27 | 'pattern' => '/(\s|^|\ \;|\()([a-z0-9\-\_\.]{2,})\@([a-z0-9\-\.]{2,})\.([a-z]{2,6})(\)|\s|\.|\,|\!|\?|$|\<)/ie', 28 | 'replacement' => '$m[1] . $this->tag($m[2]."@".$m[3].".".$m[4], "a", array("href" => "mailto:".$m[2]."@".$m[3].".".$m[4])) . $m[5]' 29 | ), 30 | 'no_repeat_words' => array( 31 | 'description' => 'Удаление повторяющихся слов', 32 | 'disabled' => true, 33 | 'pattern' => array( 34 | '/([а-яё]{3,})( |\t|\ \;)\1/iu', 35 | '/(\s|\ \;|^|\.|\!|\?)(([А-ЯЁ])([а-яё]{2,}))( |\t|\ \;)(([а-яё])\4)/eu', 36 | ), 37 | 'replacement' => array( 38 | '\1', 39 | '$m[1].($m[7] === EMT_Lib::strtolower($m[3]) ? $m[2] : $m[2].$m[5].$m[6] )', 40 | ) 41 | ), 42 | 'paragraphs' => array( 43 | 'description' => 'Простановка параграфов', 44 | 'function' => 'build_paragraphs' 45 | ), 46 | 'breakline' => array( 47 | 'description' => 'Простановка переносов строк', 48 | 'function' => 'build_brs' 49 | ), 50 | 51 | ); 52 | 53 | /** 54 | * Расстановка защищенных тегов параграфа (

...

) и переноса строки 55 | * 56 | * @return void 57 | */ 58 | protected function do_paragraphs($text) { 59 | $text = str_replace("\r\n","\n",$text); 60 | $text = str_replace("\r","\n",$text); 61 | $text = '<' . self::BASE64_PARAGRAPH_TAG . '>' . trim($text) . ''; 62 | //$text = $this->preg_replace_e('/([\040\t]+)?(\n|\r){2,}/e', '"<" .self::BASE64_PARAGRAPH_TAG . ">"', $text); 63 | //$text = $this->preg_replace_e('/([\040\t]+)?(\n){2,}/e', '"<" .self::BASE64_PARAGRAPH_TAG . ">"', $text); 64 | $text = $this->preg_replace_e('/([\040\t]+)?(\n)+([\040\t]*)(\n)+/e', '$m[1]."".EMT_Lib::iblock($m[2].$m[3])."<" .self::BASE64_PARAGRAPH_TAG . ">"', $text); 65 | //$text = $this->preg_replace_e('/([\040\t]+)?(\n)+([\040\t]*)(\n)+/e', '""."<" .self::BASE64_PARAGRAPH_TAG . ">"', $text); 66 | //может от открвающего до закрывающего ?! 67 | $text = preg_replace('/\<' . self::BASE64_PARAGRAPH_TAG . '\>('.EMT_Lib::INTERNAL_BLOCK_OPEN.'[a-zA-Z0-9\/=]+?'.EMT_Lib::INTERNAL_BLOCK_CLOSE.')?\<\/' . self::BASE64_PARAGRAPH_TAG . '\>/s', "", $text); 68 | return $text; 69 | } 70 | 71 | /** 72 | * Расстановка защищенных тегов параграфа (

...

) и переноса строки 73 | * 74 | * @return void 75 | */ 76 | protected function build_paragraphs() 77 | { 78 | $r = mb_strpos($this->_text, '<' . self::BASE64_PARAGRAPH_TAG . '>' ); 79 | $p = EMT_Lib::rstrpos($this->_text, '' ) ; 80 | if(($r!== false) && ($p !== false)) { 81 | 82 | $beg = mb_substr($this->_text,0,$r); 83 | $end = mb_substr($this->_text,$p+mb_strlen('')); 84 | $this->_text = 85 | (trim($beg) ? $this->do_paragraphs($beg). "\n":"") .'<' . self::BASE64_PARAGRAPH_TAG . '>'. 86 | mb_substr($this->_text,$r + mb_strlen('<' . self::BASE64_PARAGRAPH_TAG . '>'),$p -($r + mb_strlen('<' . self::BASE64_PARAGRAPH_TAG . '>')) ).''. 87 | (trim($end) ? "\n".$this->do_paragraphs($end) :"") ; 88 | } else { 89 | $this->_text = $this->do_paragraphs($this->_text); 90 | } 91 | } 92 | 93 | /** 94 | * Расстановка защищенных тегов параграфа (

...

) и переноса строки 95 | * 96 | * @return void 97 | */ 98 | protected function build_brs() 99 | { 100 | $this->_text = $this->preg_replace_e('/(\<\/' . self::BASE64_PARAGRAPH_TAG . '\>)([\r\n \t]+)(\<' . self::BASE64_PARAGRAPH_TAG . '\>)/mse', '$m[1].EMT_Lib::iblock($m[2]).$m[3]', $this->_text); 101 | 102 | if (!preg_match('/\<' . self::BASE64_BREAKLINE_TAG . '\>/', $this->_text)) { 103 | $this->_text = str_replace("\r\n","\n",$this->_text); 104 | $this->_text = str_replace("\r","\n",$this->_text); 105 | //$this->_text = $this->preg_replace_e('/(\n|\r)/e', '"<" . self::BASE64_BREAKLINE_TAG . ">"', $this->_text); 106 | $this->_text = $this->preg_replace_e('/(\n)/e', '"<" . self::BASE64_BREAKLINE_TAG . ">\n"', $this->_text); 107 | } 108 | } 109 | } 110 | /**PYTHON 111 | 112 | def do_paragraphs(self, text): 113 | text = EMT_Lib.str_replace(u"\r\n",u"\n",text) 114 | text = EMT_Lib.str_replace(u"\r",u"\n",text) 115 | text = u'<' + BASE64_PARAGRAPH_TAG + u'>' + text.strip() + u'' 116 | text = self.preg_replace('/([\040\t]+)?(\n)+([\040\t]*)(\n)+/e', '(u"" if m.group(1) is None else m.group(1))+u""+EMT_Lib.iblock(m.group(2)+m.group(3))+u"<" +BASE64_PARAGRAPH_TAG + u">"', text) 117 | text = self.preg_replace('/\<' + BASE64_PARAGRAPH_TAG + '\>(' + INTERNAL_BLOCK_OPEN + '[a-zA-Z0-9\/=]+?' + INTERNAL_BLOCK_CLOSE + ')?\<\/' + BASE64_PARAGRAPH_TAG + '\>/s', "", text) 118 | return text 119 | 120 | 121 | def build_paragraphs(self): 122 | r = self._text.find( u'<' + BASE64_PARAGRAPH_TAG + u'>' ) 123 | p = self._text.rfind( u'' ) 124 | if(( r != -1) and (p != -1)): 125 | 126 | beg = EMT_Lib.substr(self._text,0,r); 127 | end = EMT_Lib.substr(self._text,p+len(u'')) 128 | self._text = (self.do_paragraphs(beg)+ u"\n" if beg.strip() else u"") +u'<' + BASE64_PARAGRAPH_TAG + u'>'+EMT_Lib.substr(self._text,r + len(u'<' + BASE64_PARAGRAPH_TAG + u'>'),p -(r + len(u'<' + BASE64_PARAGRAPH_TAG + u'>')) )+u''+( u"\n"+self.do_paragraphs(end) if end.strip() else u"") 129 | else: 130 | self._text = self.do_paragraphs(self._text) 131 | 132 | def build_brs(self): 133 | self._text = self.preg_replace('/(\<\/' + BASE64_PARAGRAPH_TAG + '\>)([\r\n \t]+)(\<' + BASE64_PARAGRAPH_TAG + '\>)/mse', 'm.group(1)+EMT_Lib.iblock(m.group(2))+m.group(3)', self._text); 134 | 135 | if (not re.match('\<' + BASE64_BREAKLINE_TAG + '\>', self._text)): 136 | self._text = EMT_Lib.str_replace("\r\n","\n",self._text) 137 | self._text = EMT_Lib.str_replace("\r","\n",self._text) 138 | self._text = self.preg_replace('/(\n)/e', '"<" + BASE64_BREAKLINE_TAG + ">\\n"', self._text) 139 | 140 | 141 | PYTHON**/ 142 | ?> -------------------------------------------------------------------------------- /src-php/EMT.Tret.php: -------------------------------------------------------------------------------- 1 | logging) return; 61 | $this->logs[] = array('info' => $str, 'data' => $data); 62 | } 63 | 64 | private function error($info, $data = null) 65 | { 66 | $this->errors[] = array('info' => $info, 'data' => $data); 67 | $this->log('ERROR: '. $info , $data); 68 | } 69 | 70 | public function debug($place, &$after_text) 71 | { 72 | if(!$this->debug_enabled) return; 73 | $this->debug_info[] = array( 74 | 'place' => $place, 75 | 'text' => $after_text, 76 | ); 77 | } 78 | 79 | 80 | /** 81 | * Установить режим разметки для данного Трэта если не было раньше установлено, 82 | * EMT_Lib::LAYOUT_STYLE - с помощью стилей 83 | * EMT_Lib::LAYOUT_CLASS - с помощью классов 84 | * 85 | * @param int $kind 86 | */ 87 | public function set_tag_layout_ifnotset($layout) 88 | { 89 | if($this->use_layout_set) return; 90 | $this->use_layout = $layout; 91 | } 92 | 93 | /** 94 | * Установить режим разметки для данного Трэта, 95 | * EMT_Lib::LAYOUT_STYLE - с помощью стилей 96 | * EMT_Lib::LAYOUT_CLASS - с помощью классов 97 | * EMT_Lib::LAYOUT_STYLE|EMT_Lib::LAYOUT_CLASS - оба метода 98 | * 99 | * @param int $kind 100 | */ 101 | public function set_tag_layout($layout = EMT_Lib::LAYOUT_STYLE) 102 | { 103 | $this->use_layout = $layout; 104 | $this->use_layout_set = true; 105 | } 106 | 107 | public function set_class_layout_prefix($prefix) 108 | { 109 | $this->class_layout_prefix = $prefix; 110 | } 111 | 112 | 113 | public function debug_on() 114 | { 115 | $this->debug_enabled = true; 116 | } 117 | 118 | public function log_on() 119 | { 120 | $this->debug_enabled = true; 121 | } 122 | 123 | 124 | private function getmethod($name) 125 | { 126 | if(!$name) return false; 127 | if(!method_exists($this, $name)) return false; 128 | return array($this, $name); 129 | } 130 | 131 | private function _pre_parse() 132 | { 133 | $this->pre_parse(); 134 | foreach($this->rules as $rule) 135 | { 136 | if(!isset($rule['init'])) continue; 137 | $m = $this->getmethod($rule['init']); 138 | if(!$m) continue; 139 | call_user_func($m); 140 | } 141 | } 142 | private function _post_parse() 143 | { 144 | foreach($this->rules as $rule) 145 | { 146 | if(!isset($rule['deinit'])) continue; 147 | $m = $this->getmethod($rule['deinit']); 148 | if(!$m) continue; 149 | call_user_func($m); 150 | } 151 | $this->post_parse(); 152 | } 153 | 154 | private function rule_order_sort($a, $b) 155 | { 156 | if($a['order'] == $b['order']) return 0; 157 | if($a['order'] < $b['order']) return -1; 158 | return 1; 159 | } 160 | 161 | private function apply_rule($rule) 162 | { 163 | $name = $rule['id']; 164 | //$this->log("Правило $name", "Применяем правило"); 165 | $disabled = (isset($this->disabled[$rule['id']]) && $this->disabled[$rule['id']]) || ((isset($rule['disabled']) && $rule['disabled']) && !(isset($this->enabled[$rule['id']]) && $this->enabled[$rule['id']])); 166 | if($disabled) 167 | { 168 | $this->log("Правило $name", "Правило отключено" . ((isset($rule['disabled']) && $rule['disabled'])? " (по умолчанию)" : "")); 169 | return; 170 | } 171 | if(isset($rule['function']) && $rule['function']) 172 | { 173 | if(!(isset($rule['pattern']) && $rule['pattern'])) 174 | { 175 | if(method_exists($this, $rule['function'])) 176 | { 177 | $this->log("Правило $name", "Используется метод ".$rule['function']." в правиле"); 178 | 179 | call_user_func(array($this, $rule['function'])); 180 | return; 181 | } 182 | if(function_exists($rule['function'])) 183 | { 184 | $this->log("Правило $name", "Используется функция ".$rule['function']." в правиле"); 185 | 186 | call_user_func($rule['function']); 187 | return; 188 | } 189 | 190 | $this->error('Функция '.$rule['function'].' из правила '.$rule['id']. " не найдена"); 191 | return ; 192 | } else { 193 | if(preg_match("/^[a-z_0-9]+$/i", $rule['function'])) 194 | { 195 | if(method_exists($this, $rule['function'])) 196 | { 197 | $this->log("Правило $name", "Замена с использованием preg_replace_callback с методом ".$rule['function'].""); 198 | 199 | $this->_text = preg_replace_callback($rule['pattern'], array($this, $rule['function']), $this->_text); 200 | return; 201 | } 202 | if(function_exists($rule['function'])) 203 | { 204 | $this->log("Правило $name", "Замена с использованием preg_replace_callback с функцией ".$rule['function'].""); 205 | 206 | $this->_text = preg_replace_callback($rule['pattern'], $rule['function'], $this->_text); 207 | return; 208 | } 209 | $this->error('Функция '.$rule['function'].' из правила '.$rule['id']. " не найдена"); 210 | } else { 211 | $this->_text = preg_replace_callback($rule['pattern'], EMT_Lib::create_function('$m', $rule['function']), $this->_text); 212 | $this->log('Замена с использованием preg_replace_callback с инлайн функцией из правила '.$rule['id']); 213 | return; 214 | } 215 | return ; 216 | } 217 | } 218 | 219 | if(isset($rule['simple_replace']) && $rule['simple_replace']) 220 | { 221 | if(isset($rule['case_sensitive']) && $rule['case_sensitive']) 222 | { 223 | $this->log("Правило $name", "Простая замена с использованием str_replace"); 224 | $this->_text = str_replace($rule['pattern'], $rule['replacement'], $this->_text); 225 | return; 226 | } 227 | $this->log("Правило $name", "Простая замена с использованием str_ireplace"); 228 | $this->_text = str_ireplace($rule['pattern'], $rule['replacement'], $this->_text); 229 | return; 230 | } 231 | 232 | $pattern = $rule['pattern']; 233 | if(is_string($pattern)) $pattern = array($pattern); 234 | $eval = false; 235 | foreach($pattern as $patt) 236 | { 237 | $chr = substr($patt,0,1); 238 | $preg_arr = explode($chr, $patt); 239 | if(strpos($preg_arr[count($preg_arr)-1], "e")!==false) 240 | { 241 | $eval = true; 242 | break; 243 | } 244 | } 245 | if(!$eval) 246 | { 247 | $this->log("Правило $name", "Замена с использованием preg_replace"); 248 | 249 | do { 250 | $this->_text = preg_replace($rule['pattern'], $rule['replacement'], $this->_text); 251 | if(!(isset($rule['cycled']) && $rule['cycled'])) break; 252 | } while(preg_match($rule['pattern'], $this->_text)); 253 | 254 | return; 255 | } 256 | 257 | $this->log("Правило $name", "Замена с использованием preg_replace_callback вместо eval"); 258 | $k = 0; 259 | foreach($pattern as $patt) 260 | { 261 | $repl = is_string($rule['replacement']) ? $rule['replacement'] : $rule['replacement'][$k]; 262 | 263 | $chr = substr($patt,0,1); 264 | $preg_arr = explode($chr, $patt); 265 | if(strpos($preg_arr[count($preg_arr)-1], "e")!==false) // eval система 266 | { 267 | $preg_arr[count($preg_arr)-1] = str_replace("e","",$preg_arr[count($preg_arr)-1]); 268 | $patt = implode($chr, $preg_arr); 269 | $this->thereplacement = $repl; 270 | do { 271 | $this->_text = preg_replace_callback($patt, array($this, "thereplcallback"), $this->_text); 272 | if(!(isset($rule['cycled']) && $rule['cycled'])) break; 273 | } while(preg_match($patt, $this->_text)); 274 | 275 | } else { 276 | do { 277 | $this->_text = preg_replace($patt, $repl, $this->_text); 278 | if(!(isset($rule['cycled']) && $rule['cycled'])) break; 279 | } while(preg_match($patt, $this->_text)); 280 | } 281 | $k++; 282 | } 283 | } 284 | 285 | 286 | protected function preg_replace_e($pattern, $replacement, $text) 287 | { 288 | $chr = substr($pattern,0,1); 289 | $preg_arr = explode($chr, $pattern); 290 | if(strpos($preg_arr[count($preg_arr)-1], "e")===false) return preg_replace($pattern, $replacement, $text); 291 | $preg_arr[count($preg_arr)-1] = str_replace("e","",$preg_arr[count($preg_arr)-1]); 292 | $patt = implode($chr, $preg_arr); 293 | $this->thereplacement = $replacement; 294 | return preg_replace_callback($patt, array($this, "thereplcallback"), $text); 295 | } 296 | 297 | private $thereplacement = ""; 298 | private function thereplcallback($m) 299 | { 300 | $x = ""; 301 | eval('$x = '.($this->thereplacement?$this->thereplacement:'""').';'); 302 | return $x; 303 | } 304 | 305 | private function _apply($list) 306 | { 307 | $this->errors = array(); 308 | $this->_pre_parse(); 309 | 310 | $this->log("Применяется набор правил", implode(",",$list)); 311 | 312 | $rulelist = array(); 313 | foreach($list as $k) 314 | { 315 | $rule = $this->rules[$k]; 316 | $rule['id'] = $k; 317 | $rule['order'] = isset($rule['order'])? $rule['order'] : 5 ; 318 | $rulelist[] = $rule; 319 | } 320 | //usort($rulelist, array($this, "rule_order_sort")); 321 | 322 | foreach($rulelist as $rule) 323 | { 324 | $this->apply_rule($rule); 325 | $this->debug($rule['id'], $this->_text); 326 | } 327 | 328 | $this->_post_parse(); 329 | } 330 | 331 | 332 | /** 333 | * Создание защищенного тега с содержимым 334 | * 335 | * @see EMT_lib::build_safe_tag 336 | * @param string $content 337 | * @param string $tag 338 | * @param array $attribute 339 | * @return string 340 | */ 341 | protected function tag($content, $tag = 'span', $attribute = array()) 342 | { 343 | if(isset($attribute['class'])) 344 | { 345 | $classname = $attribute['class']; 346 | if($classname == "nowrap") 347 | { 348 | if(!$this->is_on('nowrap')) 349 | { 350 | $tag = "nobr"; 351 | $attribute = array(); 352 | $classname = ""; 353 | } 354 | } 355 | if(isset($this->classes[$classname])) 356 | { 357 | $style_inline = $this->classes[$classname]; 358 | if($style_inline) $attribute['__style'] = $style_inline; 359 | } 360 | $classname = (isset($this->class_names[$classname]) ? $this->class_names[$classname] :$classname); 361 | $classname = ($this->class_layout_prefix ? $this->class_layout_prefix : "" ).$classname; 362 | $attribute['class'] = $classname; 363 | } 364 | 365 | return EMT_Lib::build_safe_tag($content, $tag, $attribute, 366 | $this->use_layout === false? EMT_Lib::LAYOUT_STYLE : $this->use_layout ); 367 | } 368 | 369 | 370 | /** 371 | * Добавить правило в группу 372 | * 373 | * @param string $name 374 | * @param array $params 375 | */ 376 | public function put_rule($name, $params) 377 | { 378 | $this->rules[$name] = $params; 379 | return $this; 380 | } 381 | 382 | /** 383 | * Отключить правило, в обработке 384 | * 385 | * @param string $name 386 | */ 387 | public function disable_rule($name) 388 | { 389 | $this->disabled[$name] = true; 390 | unset($this->enabled[$name]); 391 | } 392 | 393 | /** 394 | * Включить правило 395 | * 396 | * @param string $name 397 | */ 398 | public function enable_rule($name) 399 | { 400 | $this->enabled[$name] = true; 401 | unset($this->disabled[$name]); 402 | } 403 | 404 | /** 405 | * Добавить настройку в трет 406 | * 407 | * @param string $key ключ 408 | * @param mixed $value значение 409 | */ 410 | public function set($key, $value) 411 | { 412 | $this->settings[$key] = $value; 413 | } 414 | 415 | /** 416 | * Установлена ли настройка 417 | * 418 | * @param string $key 419 | */ 420 | public function is_on($key) 421 | { 422 | if(!isset($this->settings[$key])) return false; 423 | $kk = $this->settings[$key]; 424 | return ((strtolower($kk)=="on") || ($kk === "1") || ($kk === true) || ($kk === 1)); 425 | } 426 | 427 | /** 428 | * Получить строковое значение настройки 429 | * 430 | * @param unknown_type $key 431 | * @return unknown 432 | */ 433 | public function ss($key) 434 | { 435 | if(!isset($this->settings[$key])) return ""; 436 | return strval($this->settings[$key]); 437 | } 438 | 439 | /** 440 | * Добавить настройку в правило 441 | * 442 | * @param string $rulename идентификатор правила 443 | * @param string $key ключ 444 | * @param mixed $value значение 445 | */ 446 | public function set_rule($rulename, $key, $value) 447 | { 448 | $this->rules[$rulename][$key] = $value; 449 | } 450 | 451 | /** 452 | * Включить правила, согласно списку 453 | * 454 | * @param array $list список правил 455 | * @param boolean $disable выкллючить их или включить 456 | * @param boolean $strict строго, т.е. те которые не в списку будут тоже обработаны 457 | */ 458 | public function activate($list,$disable =false, $strict = true) 459 | { 460 | if(!is_array($list)) return ; 461 | 462 | foreach($list as $rulename) 463 | { 464 | if($disable) $this->disable_rule($rulename); else $this->enable_rule($rulename); 465 | } 466 | 467 | if($strict) 468 | { 469 | foreach($this->rules as $rulename => $v) 470 | { 471 | if(in_array($rulename, $list)) continue; 472 | if(!$disable) $this->disable_rule($rulename); else $this->enable_rule($rulename); 473 | } 474 | } 475 | } 476 | 477 | public function set_text(&$text) 478 | { 479 | $this->_text = &$text; 480 | $this->debug_info = array(); 481 | $this->logs = array(); 482 | } 483 | 484 | 485 | /** 486 | * Применить к тексту 487 | * 488 | * @param string $text - текст к которому применить 489 | * @param mixed $list - список правил, null - все правила 490 | * @return string 491 | */ 492 | public function apply($list = null) 493 | { 494 | if(is_string($list)) $rlist = array($list); 495 | elseif(is_array($list)) $rlist = $list; 496 | else $rlist = array_keys($this->rules); 497 | $this->_apply($rlist); 498 | return $this->_text; 499 | } 500 | 501 | 502 | 503 | 504 | /** 505 | * Код, выполняем до того, как применить правила 506 | * 507 | */ 508 | public function pre_parse() 509 | { 510 | } 511 | 512 | /** 513 | * После выполнения всех правил, выполняется этот метод 514 | * 515 | */ 516 | public function post_parse() 517 | { 518 | } 519 | 520 | 521 | } 522 | 523 | 524 | 525 | ?> -------------------------------------------------------------------------------- /tests/test.em.php: -------------------------------------------------------------------------------- 1 | 'off', 'OptAlign.all'=>'off'); 3 | $tester->set_group('R', "Набор Евгения Муравьёва"); 4 | 5 | $tester->add_test("В этом тексте много пробелов.", "В этом тексте много пробелов.", null, "Удаление лишних пробельных символов и табуляций", $no_p); 6 | 7 | $tester->add_test("При при проверке текста обнаружились обнаружились повторяющиеся слова слова. Слова убраны.", "При проверке текста обнаружились повторяющиеся слова. Слова убраны.", null, "Удаление повторяющихся слов (по умолчанию выключено)", array_merge(array('Text.no_repeat_words'=>'on'),$no_p)); 8 | 9 | $tester->add_test("Мало написать а запятые кто за тебя расставит. Я же расставлял но похоже что-то пропустил.", "Мало написать, а запятые кто за тебя расставит. Я же расставлял, но похоже что-то пропустил.", null, "Расстановка запятых перед а, но", $no_p); 10 | 11 | $tester->add_test("Кто то где то когда то как то что то чем то стукнул. И возможно чего нибудь бы получилось если б кто либо пришел", "Кто-то где-то когда-то как-то что-то чем-то стукнул. И возможно чего-нибудь бы получилось если б кто-либо пришел", null, "Автоматическая простановка дефисов в обезличенных местоимениях и междометиях", $no_p); 12 | 13 | $tester->add_test("Кто то где то когда то как то что то чем то стукнул.", "Кто-то где-то когда-то как-то что-то чем-то стукнул.", null, "Обрамление пятисимвольных слов разделенных дефисом в неразрывные блоки (по умолчанию выключено)", array_merge(array('Nobr.hyphen_nowrap_in_small_words'=>'on'),$no_p)); 14 | 15 | $tester->add_test("В смешанных лесах встречаются разнообразные деревья, как то: береза, осина, кедр, сосна.", "В смешанных лесах встречаются разнообразные деревья, как то: береза, осина, кедр, сосна.", null, "Как то:", $no_p); 16 | 17 | $tester->add_test("Опять же что- то получилось, но как- то не так.", "Опять же что-то получилось, но как-то не так.", null, "Лишние пробелы между дефисом в местоимениях и наречиях", $no_p); 18 | 19 | $tester->add_test("Возьми ка детка молока. А коль увижу де, что казнь ему мала, повешу тут же всех судей вокруг стола. Поди кась так", "Возьми-ка детка молока. А коль увижу-де, что казнь ему мала, повешу тут же всех судей вокруг стола. Поди-кась так", null, "??? (не использовать)", array_merge(array('Dash.ka_de_kas'=>'on', 'Nobr.hyphen_nowrap_in_small_words'=>'on'),$no_p)); 20 | 21 | $tester->add_test("Кое как дошли. Кой кого встретили. Кое от кого, кое на чем, кой у кого, кое с чьим.", "Кое-как дошли. Кой-кого встретили. Кое от кого, кое на чем, кой у кого, кое с чьим.", null, "??? (не использовать)", array_merge(array('Nobr.hyphen_nowrap_in_small_words'=>'on'),$no_p)); 22 | 23 | $tester->add_test("Секретарь, хотя и чувствовал свое слабое недовольство, все таки радовался наличию таких старушек в активе района. Но хоть и велик был соблазн, я таки успел себя побороть.", "Секретарь, хотя и чувствовал свое слабое недовольство, все-таки радовался наличию таких старушек в активе района. Но хоть и велик был соблазн, я таки успел себя побороть.", null, "??? (не использовать)", array_merge(array('Nobr.hyphen_nowrap_in_small_words'=>'on'),$no_p)); 24 | 25 | $tester->add_test("Из за леса величаво выплывало солнце. Из под развесистой сирени вдруг с лаем выскочила собака.", "Из-за леса величаво выплывало солнце. Из-под развесистой сирени вдруг с лаем выскочила собака.", null, "Расстановка дефисов между из-за, из-под", $no_p); 26 | 27 | $tester->add_test("Правда? Правда!!! Неправда??? Честно?? Честно!! Проехали.. Задумчиво...", "Правда? Правда!!! Неправда??? Честно? Честно! Проехали. Задумчиво…", null, "Замена трех точек на знак многоточия, сдвоенные знаки препинания на одинарные", $no_p); 28 | 29 | $tester->add_test("Привет!? Как дела? ", "Привет?! Как дела?", null, "Замена восклицательного и вопросительного знаков местами", $no_p); 30 | 31 | $tester->add_test("Некоторые виды деревьев : ель ,сосна , берёза, дуб ; растут в наших лесах .", "Некоторые виды деревьев: ель, сосна, берёза, дуб; растут в наших лесах.", null, "Удаление пробелов перед и после знаков препинания в предложении", $no_p); 32 | 33 | $tester->add_test("Так бывает.И вот так...И ещё вот так!..Бывает же???Что поделать.", "Так бывает. И вот так… И ещё вот так!.. Бывает же??? Что поделать.", null, "Расстановка пробелов после знаков препинания", $no_p); 34 | 35 | $tester->add_test("Вывод:верен.", "Вывод: верен.", null, "Расстановка пробела в словах после двоеточия ", $no_p); 36 | 37 | $tester->add_test("100 %", "100%", null, "Удаление пробела перед символом процента", $no_p); 38 | 39 | $tester->add_test("Текст( ( Внутри ) скобок ).", "Текст ((Внутри) скобок).", null, "Удаление пробелов внутри скобок, а также расстановка пробела перед скобками", $no_p); 40 | 41 | $tester->add_test("- Я пошёл домой... - Может останешься? - Нет, ухожу.", "— Я пошёл домой… — Может останешься? — Нет, ухожу.", null, "Выделение прямой речи", $no_p); 42 | 43 | $tester->add_test("Я бы в лётчики б пошёл, пусть меня научат.", "Я бы в лётчики б пошёл, пусть меня научат.", null, "Привязка союзов, предлогов", $no_p); 44 | 45 | $tester->add_test("3х4, 3 х 6", "3×4, 3×6", null, "Замена x на символ в размерных единицах", $no_p); 46 | 47 | $tester->add_test("1/2, 1/4, 3/4, 123/432", "½, ¼, ¾, 123/432", null, "Замена дробей 1/2, 1/4, 3/4 на соответствующие символы", $no_p); 48 | 49 | $tester->add_test("Coca-Cola(r) - зарегистрированный товарный знак.", "Coca-Cola® — зарегистрированный товарный знак.", null, "Замена (R) на символ зарегистрированной торговой марки", $no_p); 50 | 51 | $tester->add_test("(c) Muravjev Inc.", "© Muravjev Inc.", null, "Замена (c) на символ копирайт", $no_p); 52 | 53 | $tester->add_test("Муравьев(tm)", "Муравьев™", null, "Замена (tm) на символ торговой марки", $no_p); 54 | 55 | $tester->add_test("Раздобудь к утру ковёр - 56 | Шитый золотом узор!.. 57 | Государственное дело, - 58 | Расшибись, а будь добёр!", "Раздобудь к утру ковёр —
\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("Лес, газ, нефть и тд., и т.п.. Перины, подушки в тч. подушки-думки.", "Лес, газ, нефть и т. д., и т. п. Перины, подушки в т. ч. подушки-думки.", null, "Объединение сокращений и т.д., и т.п., в т.ч.", $no_p); 81 | 82 | $tester->add_test("Данные изложены в таблице см.цветной вкладыш. Дом им.Пушкина.", "Данные изложены в таблице см. цветной вкладыш. Дом им. Пушкина.", null, "Расстановка пробелов перед сокращениями см., им.", $no_p); 83 | 84 | $tester->add_test("Инструкцию см. гл. 8, стр.34, рис.3 или илл.3.", "Инструкцию см. гл. 8, стр. 34, рис. 3 или илл. 3.", null, "Расстановка пробелов перед сокращениями гл., стр., рис., илл.", $no_p); 85 | 86 | $tester->add_test("Оля, Иван, Олег и др. ребята.", "Оля, Иван, Олег и др. ребята.", null, "Объединение сокращений и др.", $no_p); 87 | 88 | $tester->add_test("г.Тюмень, ул.Ленина, д. 4", "г. Тюмень, ул. Ленина, д. 4", null, "Расстановка пробелов в сокращениях г., ул., пер., д.", $no_p); 89 | 90 | $tester->add_test("Разрешение 300dpi (для офсета).", "Разрешение 300 dpi (для офсета).", null, "Расстановка пробелов перед сокращениями dpi, lpi", $no_p); 91 | 92 | $tester->add_test("P.S. привет всем. P.P.S. и мне тоже.", "P. S. привет всем. P. P. S. и мне тоже.", null, "Объединение сокращений P.S., P.P.S.", $no_p); 93 | 94 | $tester->add_test("Жёлто-оранжевый цвет. Ростов-на-Дону красивый город.", "Жёлто-оранжевый цвет. Ростов-на-Дону красивый город.", null, "Объединение в неразрывные конструкции слов с дефисом (по умолчанию выключено)", array_merge(array('Nobr.hyphen_nowrap'=>'on'),$no_p)); 95 | 96 | $tester->add_test(<<add_test("+7 (3452) 55-66-77, 8 905 555-55-55", "+7 (3452) 55-66-77, 8 905 555-55-55", null, "Объединение в неразрывные конструкции номеров телефонов", $no_p); 102 | 103 | $tester->add_test("Гост 5773-90 - российские стандартные форматы изданий", "ГОСТ 5773–90 — российские стандартные форматы изданий", null, "Привязка сокращения ГОСТ к номеру", $no_p); 104 | 105 | $tester->add_test("~23,5в", "~23,5 В", null, "Установка пробельных символов в сокращении вольт", $no_p); 106 | 107 | $tester->add_test("123 456 789 руб. В стычке участвовало 3 200 человек.", "123 456 789 руб. В стычке участвовало 3 200 человек.", null, "Объединение триад чисел полупробелом", $no_p); 108 | 109 | $tester->add_test("Адрес localhost - 127.0.0.1", "Адрес localhost — 127.0.0.1", null, "Объединение IP-адресов", $no_p); 110 | 111 | $tester->add_test("Это событие произошло между 1999-2001г.г., на стыке XX-XXIв.", "Это событие произошло между 1999—2001 гг., на стыке XX—XXI вв.", null, "Установка тире и пробельных символов в периодах дат", $no_p); 112 | 113 | $tester->add_test("Документ был подписан 17.02.1983г. И утратил свою силу 07.03.93 года.", "Документ был подписан 17.02.1983 г. И утратил свою силу 07.03.93 года.", null, "Привязка года к дате", $no_p); 114 | 115 | $tester->add_test("Собеседования состоятся 14-24 сентября, в актовом зале с 11:30-13:00.", "Собеседования состоятся 14—24 сентября, в актовом зале с 11:30—13:00.", null, "Расстановка тире и объединение в неразрывные периоды дней (по умолчанию выключено)", array_merge(array('Date.nbsp_and_dash_month_interval'=>'on'), $no_p)); 116 | 117 | $tester->add_test("Выставка пройдёт в апреле-мае этого года.", "Выставка пройдёт в апреле—мае этого года.", null, "Расстановка тире и объединение в неразрывные периоды месяцев (по умолчанию выключено)", array_merge(array('Date.mdash_month_interval'=>'on'), $no_p)); 118 | 119 | // Не имплементировано 120 | //$tester->add_test("IV в до н.э, в V-VIвв до нэ., третий в. н.э.", "IV в. до н. э., в V—VI вв. до н. э., третий в. н. э.", null, "Привязка сокращений до н.э., н.э.", $no_p); 121 | 122 | $tester->add_test("А.С.Пушкин, Пушкин А.С.", "А. С. Пушкин, Пушкин А. С.", null, "Привязка инициалов к фамилиям", $no_p); 123 | 124 | $tester->add_test("...Когда В. И. Пупкин увидел в газете ( это была "Сермяжная правда" № 45) рубрику Weather Forecast(r), он не поверил своим глазам - температуру обещали +-451F.", "…Когда В. И. Пупкин увидел в газете (это была «Сермяжная правда» № 45) рубрику Weather Forecast®, он не поверил своим глазам — температуру обещали ±451 °F.", null, "Замена символа градус, плюс-минус", $no_p); 125 | 126 | $tester->add_test("Пушкин, оценивая всех своих предшественников, писал: "... Некоторые оды Державина, несмотря на неправильность языка и неровность слога, исполнены порывами гения...".", "Пушкин, оценивая всех своих предшественников, писал: «…Некоторые оды Державина, несмотря на неправильность языка и неровность слога, исполнены порывами гения…».", null, "Повторное типографирование текста", $no_p); 127 | 128 | $tester->add_test("На лесопилку завезли 32 м3 леса, из которых 4м3 пустили под распил на 25мм доски, длинной по 6м.", "На лесопилку завезли 32 м³ леса, из которых 4 м³ пустили под распил на 25 мм доски, длинной по 6 м.", null, "Замена символов и привязка сокращений в размерных величинах: м, см, м2...", $no_p); 129 | 130 | $tester->add_test("

dsdsd

131 |

sdsdadadad

", "

dsdsd

132 |

sdsdadadad

", null, "Нет br между параграфами", $no_p); 133 | 134 | $tester->add_test("привет при'в'ет как дела", "привет при’в’ет как дела", null, "Два апострофа в слове", $no_p); 135 | 136 | $tester->add_test("$125, $ 12, € 1, €12", "$ 125, $ 12, € 1, € 12", null, "Привязка доллара и евро к числу", $no_p); 137 | $tester->add_test("+ 7 (495)1234567, +7 (495)1234567, +7(495)1234567, +7(495) 1234567", "+7 495 123-45-67, +7 495 123-45-67, +7 495 123-45-67, +7 495 123-45-67", null, "Неразрывная конструкция для телефонов", $no_p); 138 | 139 | $tester->add_test("Высота 15 000—20 000 м", "Высота 15 000—20 000 м", null, "Тонкие пробелы в числах", $no_p); 140 | 141 | $tester->add_test("Высота 15000—20000 м", "Высота 15 000—20 000 м", null, "Тонкие пробелы в числах с разибвкой", $no_p); 142 | 143 | $tester->add_test("Числа 123456, 1234567, 12345678, 123456789", "Числа 123 456, 1 234 567, 12 345 678, 123 456 789", null, "Тонкие пробелы в больших числах с разибвкой", $no_p); 144 | 145 | $tester->add_test("А.С.Пушкин, Пушкин АС", "А. С. Пушкин, Пушкин А. С.", null, "Привязка инициалов к фамилиям с точками", array_merge(array('Nobr.dots_for_surname_abbr'=>'on'), $no_p)); 146 | 147 | ?> -------------------------------------------------------------------------------- /tests/test.nsys.php: -------------------------------------------------------------------------------- 1 | set_group('T', "Блоки без типографирования и защищённые тэги"); 4 | 5 | $no_p = array('Text.paragraphs'=>'off', 'OptAlign.all'=>'off'); 6 | 7 | $tester->add_test("1 января 2009", "1 января 2009", null, "Отмена типографирования во всём тексте", $no_p); 8 | $tester->add_test("Я пишу текст, и не хочу, чтобы его часть типографировалась.", "Я пишу текст, и не хочу, чтобы его часть типографировалась.", null, "Отмена типографирования на части текста", $no_p); 9 | 10 | $tester->add_test(<<Заяц сидел на опушке. 12 | HTML 13 | , <<Заяц сидел на опушке. 15 | HTML 16 | , null, "Отмена типографирования тэга pre", array('Text.paragraphs'=>'off', 'OptAlign.all'=>'off'), "pre"); 17 | 18 | $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 = ""; 64 | return $h; 65 | } 66 | 67 | $prev = ""; 68 | foreach($typograph->debug_info as $debug) 69 | { 70 | if($debug['tret']) 71 | { 72 | $tr = $typograph->get_tret($debug['class']); 73 | $tt = "Трэт: ".$tr->title; 74 | $tn = $debug['class']; 75 | $rt = $tr->rules[$debug['place']]['description']; 76 | $rn = $debug['place']; 77 | } else { 78 | $rt = ""; 79 | $rn = ""; 80 | $tn = ""; 81 | switch($debug['place']) 82 | { 83 | case "init": $tt = "До обработки типографом"; break; 84 | case "safe_sequences": $tt = "Включение безопасных последовательностей"; break; 85 | case "unsafe_sequences": $tt = "Выключение безопасных последовательностей"; break; 86 | case "safe_blocks": $tt = "Включение безопасных блоков"; break; 87 | case "unsafe_blocks": $tt = "Возврат безопасных блоков"; break; 88 | case "safe_tag_chars": $tt = "Сохранение содержимого тэгов"; break; 89 | case "unsafe_tag_chars": $tt = "Восстановление содержимого тэгов"; break; 90 | case "clear_special_chars": $tt = "Замена всех специальных символов"; break; 91 | default: $tt = $debug['place']; 92 | } 93 | } 94 | $debuglist .= draw_debug_item($tt, $tn, $rt, $rn , $debug['text'], $prev != $debug['text'], $debug['text_raw']); 95 | $prev = $debug['text']; 96 | } 97 | 98 | 99 | $logs = ""; 100 | foreach($typograph->logs as $log) 101 | { 102 | $prefix = ""; 103 | if($log['class']) 104 | { 105 | $tr = $typograph->get_tret($log['class']); 106 | $prefix = "Трэт ".$tr->title . " (".$log['class'].") "; 107 | } 108 | $data = ""; 109 | if($log['data']) 110 | { 111 | $data = (is_string($log['data']) ? $log['data'] : print_r($log['data'], true)); 112 | } 113 | $logs .= $prefix . $log['info'] . ($data ? ": ". $data : "" ). "\n"; 114 | } 115 | 116 | 117 | $html = $result; 118 | $code = htmlspecialchars($html); 119 | } 120 | 121 | $typograph_style = $typograph->get_style(); 122 | 123 | 124 | $typooptions = ""; 125 | $tret_list = $typograph->get_trets_list(); 126 | foreach($tret_list as $tret) 127 | { 128 | $tret_obj = $typograph->get_tret($tret); 129 | 130 | $rhtml = ""; 131 | foreach($tret_obj->rules as $rulename => $rule) 132 | { 133 | $checked = ""; 134 | if($options) 135 | { 136 | if($options[$tret][$rulename]) $checked="checked"; 137 | } else { 138 | if(!$rule['disabled']) $checked="checked"; 139 | } 140 | $rhtml .= " ".$rule['description']."".$rulename."
\n"; 141 | } 142 | $typooptions .= "". 143 | "{$tret_obj->title}".$tret."
\n
$rhtml
\n"; 144 | } 145 | 146 | 147 | $phpself = $_SERVER['PHP_SELF']; 148 | echo << 150 | 151 | Типограф Евгения Муравьёва версия 3.0 152 | 153 | 186 | 189 | 190 | 191 |

Типограф Евгения Муравьёва. Отладчик.

192 |
193 | 194 | 195 | 200 | 211 | 212 |
196 | 197 |
198 | 199 |
201 | Настроить типограф
202 |
203 | 204 |

Вывод

205 | Использовать style    Использовать классы 206 |
207 |

Правила

208 | $typooptions 209 |
210 |
213 |
214 | 215 | 216 | HTML; 217 | 218 | if($ok) 219 | echo << 221 |
$html
222 |
223 |
224 | HTML code:
225 | $code 226 |
227 |
228 |
229 |
230 |

Отладка

показать всё показать необработанные рездуьтаты 231 |
232 | $debuglist 233 |
234 |
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("
\"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 . "

\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 | Типограф Евгения Муравьёва версия 3.0 25 | 26 | 34 | 35 | 36 |
37 | 38 |
39 | 40 |
41 | 42 | Result:
43 |
$html
44 |
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 .= "
  • ".$err['info']."
  • "; 102 | } 103 | $code = htmlspecialchars($result); 104 | $html = $result; 105 | } 106 | } 107 | 108 | $typograph_style = $typograph->get_style(); 109 | 110 | 111 | if($text !== false) 112 | { 113 | $htmlresult = << 115 |
    $html
    116 |
    117 |
    118 | HTML code:
    119 | $code 120 |
    121 |
    122 | HTML; 123 | } 124 | 125 | 126 | $phpself = $_SERVER['PHP_SELF']; 127 | $typograph_style=isset($typograph_style)?$typograph_style:''; 128 | $inputdata=isset($inputdata)?$inputdata:''; 129 | $htmlresult=isset($htmlresult)?$htmlresult:''; 130 | $optdisplay=isset($optdisplay)?$optdisplay:''; 131 | echo << 133 | 134 | Типограф Евгения Муравьёва версия 3.0 135 | 136 | 151 | 154 | 155 | 156 |

    Типограф Евгения Муравьёва.

    157 |
    158 | 159 | 160 | 168 | 172 | 173 |
    161 | 162 |
    163 | 164 |
    165 |
    166 | $htmlresult 167 |
    169 | Настроить типограф
    170 |
    $options_html
    171 |
    174 |
    175 | 176 | 177 | HTML; 178 | 179 | 180 | echo << 182 | 183 | HTML; 184 | 185 | ?> -------------------------------------------------------------------------------- /tools-php/test.php: -------------------------------------------------------------------------------- 1 | Инлайн стили"; 35 | if($type == 2) $text = "Классы"; 36 | 37 | 38 | $razn = ""; 39 | if($raznica) 40 | { 41 | 42 | return << 45 | ПОСЛЕ: $posle
    46 | НУЖНО: $nuzhno
    47 | РАЗЛЧ: $raznica
    48 | 49 | HTML; 50 | } 51 | return << 54 | ПОСЛЕ: $posle
    55 | НУЖНО: $nuzhno
    56 | 57 | HTML; 58 | 59 | 60 | } 61 | 62 | public function on_tested($num, $test, $ret) 63 | { 64 | $cc = urlencode($test['text']); 65 | $text = nl2br(htmlspecialchars($test['text'])); 66 | $result = htmlspecialchars($test['result']); 67 | $out = htmlspecialchars($ret['result']); 68 | 69 | $diffx = new FineDiff($out, $result, FineDiff::$characterGranularity); 70 | $diff = $diffx->renderDiffToHTML2(); 71 | //$diffy = new FineDiff($ret['result'], $test['result'], FineDiff::$characterGranularity); 72 | //$diff_text = $diffy->renderDiffToHTML2(); 73 | 74 | $block_html_inline = $this->build_posle_nuzhno_raznica_block($test['result_classes']? 1: 0, $out, $result, $diff); 75 | $block_text_inline = $this->build_posle_nuzhno_raznica_block($test['result_classes']? 1: 0, $ret['result'], $test['text']); 76 | $block_html_classes = ""; 77 | $block_text_classes = ""; 78 | if(isset($test['result_classes']) && $test['result_classes']) 79 | { 80 | $diffc_h = new FineDiff(htmlspecialchars($ret['result_classes']), htmlspecialchars($test['result_classes']), FineDiff::$characterGranularity); 81 | $diff_h = $diffc_h->renderDiffToHTML2(); 82 | //$diffc_t = new FineDiff($ret['result_classes'], $test['result_classes'], FineDiff::$characterGranularity); 83 | //$diff_t = $diffc_t->renderDiffToHTML2(); 84 | 85 | $block_html_classes = "
    ".$this->build_posle_nuzhno_raznica_block(2, htmlspecialchars($ret['result_classes']), htmlspecialchars($test['result_classes']), $diff_h); 86 | $block_text_classes = "
    ".$this->build_posle_nuzhno_raznica_block(2, $ret['result_classes'], $test['result_classes']); 87 | } 88 | 89 | // повторное тестирование
    90 | $second_html = ""; 91 | $second_text = ""; 92 | if(isset($ret['result_second']) && $ret['result_second']) 93 | { 94 | $out_second = htmlspecialchars($ret['result_second']); 95 | $diffx2 = new FineDiff($out_second, $result, FineDiff::$characterGranularity); 96 | $diff_second = $diffx2->renderDiffToHTML2(); 97 | $block_second_html_inline = $this->build_posle_nuzhno_raznica_block($test['result_classes']? 1: 0, $out_second, $result, $diff_second); 98 | $block_second_text_inline = $this->build_posle_nuzhno_raznica_block($test['result_classes']? 1: 0, $ret['result_second'], $test['text']); 99 | $block_second_html_classes = ""; 100 | $block_second_text_classes = ""; 101 | if(isset($test['result_classes_second']) && $test['result_classes_second']) 102 | { 103 | $diffc_h2 = new FineDiff(htmlspecialchars($ret['result_classes_second']), htmlspecialchars($test['result_classes']), FineDiff::$characterGranularity); 104 | $diff_second_h = $diffc_h2->renderDiffToHTML2(); 105 | 106 | $block_second_html_classes = "
    ".$this->build_posle_nuzhno_raznica_block(2, htmlspecialchars($ret['result_classes_second']), htmlspecialchars($test['result_classes']), $diff_second_h); 107 | $block_second_text_classes = "
    ".$this->build_posle_nuzhno_raznica_block(2, $ret['result_classes_second'], $test['result_classes']); 108 | } 109 | 110 | $second_html = << 112 |

    Повторное типографирование

    113 | $block_second_html_inline 114 | $block_second_html_classes 115 | HTML; 116 | $second_text = << 118 |

    Повторное типографирование

    119 | $block_second_text_inline 120 | $block_second_text_classes 121 | HTML; 122 | } 123 | 124 | 125 | $infoblock = << 127 |
    128 | Посмотреть текст  Открыть в отладчике
    129 | ДО: $text
    130 |
    131 | $block_html_inline 132 | $block_html_classes 133 | $second_html 134 |
    135 | 143 | 144 | 145 | HTML; 146 | $cnt = $this->get_test_count(); 147 | 148 | // первый тест в группе тестов 149 | if($test['grnum'] == 1) 150 | { 151 | echo << 153 |

    $test[grtitle]

    154 |