diff --git a/README.md b/README.md index 2891eed..5ac507e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ddTypograph +# MODXEvo.snippets.ddTypograph Snippet for text typography. The snippet doesn’t use third-party services, also it sends no requests. In other words, everything is performed on your server. ___ diff --git a/assets/snippets/ddTypograph/EMT.php b/assets/snippets/ddTypograph/EMT.php index b14ef0e..8e81587 100644 --- a/assets/snippets/ddTypograph/EMT.php +++ b/assets/snippets/ddTypograph/EMT.php @@ -6,3423 +6,3423 @@ * Release Date: July 2, 2015 * Authors: Evgeny Muravjev & Alexander Drutsa */ - - -class EMT_Lib -{ - const LAYOUT_STYLE = 1; - const LAYOUT_CLASS = 2; - - const INTERNAL_BLOCK_OPEN = '%%%INTBLOCKO235978%%%'; - const INTERNAL_BLOCK_CLOSE = '%%%INTBLOCKC235978%%%'; - /** - * Таблица символов - * - * @var array - */ - public static $_charsTable = array( - '"' => array('html' => array('«', '»', '”', '‘', '„', '“', '"', '«', '»'), - 'utf8' => array(0x201E, 0x201C, 0x201F, 0x201D, 0x00AB, 0x00BB)), - ' ' => array('html' => array(' ', ' ', ' '), - 'utf8' => array(0x00A0, 0x2002, 0x2003, 0x2008, 0x2009)), - '-' => array('html' => array(/*'—',*/ '–', '−', '—', '—', '–'), - 'utf8' => array(0x002D, /*0x2014,*/ 0x2010, 0x2012, 0x2013)), - '—' => array('html' => array('—'), - 'utf8' => array(0x2014)), - '==' => array('html' => array('≡'), - 'utf8' => array(0x2261)), - '...' => array('html' => array('…', '…'), - 'utf8' => array(0x2026)), - '!=' => array('html' => array('≠', '≠'), - 'utf8' => array(0x2260)), - '<=' => array('html' => array('≤', '≤'), - 'utf8' => array(0x2264)), - '>=' => array('html' => array('≥', '≥'), - 'utf8' => array(0x2265)), - '1/2' => array('html' => array('½', '½'), - 'utf8' => array(0x00BD)), - '1/4' => array('html' => array('¼', '¼'), - 'utf8' => array(0x00BC)), - '3/4' => array('html' => array('¾', '¾'), - 'utf8' => array(0x00BE)), - '+-' => array('html' => array('±', '±'), - 'utf8' => array(0x00B1)), - '&' => array('html' => array('&', '&')), - '(tm)' => array('html' => array('™', '™'), - 'utf8' => array(0x2122)), - //'(r)' => array('html' => array('®', '®', '®'), - '(r)' => array('html' => array('®', '®'), - 'utf8' => array(0x00AE)), - '(c)' => array('html' => array('©', '©'), - 'utf8' => array(0x00A9)), - '§' => array('html' => array('§', '§'), - 'utf8' => array(0x00A7)), - '`' => array('html' => array('́')), - '\'' => array('html' => array('’', '’')), - 'x' => array('html' => array('×', '×'), - 'utf8' => array('×') /* какой же у него может быть код? */), - - ); - - /** - * Добавление к тегам атрибута 'id', благодаря которому - * при повторном типографирование текста будут удалены теги, - * расставленные данным типографом - * - * @var array - */ - protected static $_typographSpecificTagId = false; - - - /** - * Костыли для работы с символами UTF-8 - * - * @author somebody? - * @param int $c код символа в кодировке UTF-8 (например, 0x00AB) - * @return bool|string - */ - public static function _getUnicodeChar($c) - { - if ($c <= 0x7F) { - return chr($c); - } else if ($c <= 0x7FF) { - return chr(0xC0 | $c >> 6) - . chr(0x80 | $c & 0x3F); - } else if ($c <= 0xFFFF) { - return chr(0xE0 | $c >> 12) - . chr(0x80 | $c >> 6 & 0x3F) - . chr(0x80 | $c & 0x3F); - } else if ($c <= 0x10FFFF) { - return chr(0xF0 | $c >> 18) - . chr(0x80 | $c >> 12 & 0x3F) - . chr(0x80 | $c >> 6 & 0x3F) - . chr(0x80 | $c & 0x3F); - } else { - return false; - } - } - - - /** - * Удаление кодов HTML из текста - * - * - * // Remove UTF-8 chars: - * $str = EMT_Lib::clear_special_chars('your text', 'utf8'); - * // ... or HTML codes only: - * $str = EMT_Lib::clear_special_chars('your text', 'html'); - * // ... or combo: - * $str = EMT_Lib::clear_special_chars('your text'); - * - * - * @param string $text - * @param mixed $mode - * @return string|bool - */ - public static function clear_special_chars($text, $mode = null) - { - if(is_string($mode)) $mode = array($mode); - if(is_null($mode)) $mode = array('utf8', 'html'); - if(!is_array($mode)) return false; - $moder = array(); - foreach($mode as $mod) if(in_array($mod, array('utf8','html'))) $moder[] = $mod; - if(count($moder)==0) return false; - - foreach (self::$_charsTable as $char => $vals) - { - foreach ($mode as $type) - { - if (isset($vals[$type])) - { - foreach ($vals[$type] as $v) - { - if ('utf8' === $type && is_int($v)) - { - $v = self::_getUnicodeChar($v); - } - if ('html' === $type) - { - if(preg_match("/<[a-z]+>/i",$v)) - { - $v = self::safe_tag_chars($v, true); - } - } - $text = str_replace($v, $char, $text); - } - } - } - } - - return $text; - } - - /** - * Удаление тегов HTML из текста - * Тег
будет преобразов в перенос строки \n, сочетание тегов

- - * в двойной перенос - * - * @param string $text - * @param array $allowableTag массив из тегов, которые будут проигнорированы - * @return string - */ - public static function remove_html_tags($text, $allowableTag = null) - { - $ignore = null; - - if (null !== $allowableTag) - { - if (is_string($allowableTag)) - { - $allowableTag = array($allowableTag); - } - if (is_array($allowableTag)) - { - $tags = array(); - foreach ($allowableTag as $tag) - { - if ('<' !== substr($tag, 0, 1) || '>' !== substr($tag, -1, 1)) continue; - if ('/' === substr($tag, 1, 1)) continue; - $tags [] = $tag; - } - $ignore = implode('', $tags); - } - } - $text = preg_replace(array('/\/i', '/\<\/p\>\s*\/'), array("\n","\n\n"), $text); - $text = strip_tags($text, $ignore); - return $text; - } - - /** - * Сохраняем содержимое тегов HTML - * - * Тег 'a' кодируется со специальным префиксом для дальнейшей - * возможности выносить за него кавычки. - * - * @param string $text - * @param bool $safe - * @return string - */ - public static function safe_tag_chars($text, $way) - { - if ($way) - $text = preg_replace_callback('/(\<\/?)([^<>]+?)(\>)/s', 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); - else - $text = preg_replace_callback('/(\<\/?)([^<>]+?)(\>)/s', 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); - return $text; - } - - - /** - * Декодриует спец блоки - * - * @param string $text - * @return string - */ - public static function decode_internal_blocks($text) - { - $text = preg_replace_callback('/'.EMT_Lib::INTERNAL_BLOCK_OPEN.'([a-zA-Z0-9\/=]+?)'.EMT_Lib::INTERNAL_BLOCK_CLOSE.'/s', create_function('$m','return EMT_Lib::decrypt_tag($m[1]);'), $text); - return $text; - } - - /** - * Кодирует спец блок - * - * @param string $text - * @return string - */ - public static function iblock($text) - { - return EMT_Lib::INTERNAL_BLOCK_OPEN. EMT_Lib::encrypt_tag($text).EMT_Lib::INTERNAL_BLOCK_CLOSE; - } - - - /** - * Создание тега с защищенным содержимым - * - * @param string $content текст, который будет обрамлен тегом - * @param string $tag тэг - * @param array $attribute список атрибутов, где ключ - имя атрибута, а значение - само значение данного атрибута - * @return string - */ - public static function build_safe_tag($content, $tag = 'span', $attribute = array(), $layout = EMT_Lib::LAYOUT_STYLE ) - { - $htmlTag = $tag; - - if (self::$_typographSpecificTagId) - { - if(!isset($attribute['id'])) - { - $attribute['id'] = 'emt-2' . mt_rand(1000,9999); - } - } - - $classname = ""; - if (count($attribute)) - { - - if($layout & EMT_lib::LAYOUT_STYLE) - { - if(isset($attribute['__style']) && $attribute['__style']) - { - if(isset($attribute['style']) && $attribute['style']) - { - $st = trim($attribute['style']); - if(mb_substr($st, -1) != ";") $st .= ";"; - $st .= $attribute['__style']; - $attribute['style'] = $st; - } else { - $attribute['style'] = $attribute['__style']; - } - unset($attribute['__style']); - } - - } - foreach ($attribute as $attr => $value) - { - if($attr == "__style") continue; - if($attr == "class") { - $classname = "$value"; - continue; - } - $htmlTag .= " $attr=\"$value\""; - } - - } - - if( ($layout & EMT_lib::LAYOUT_CLASS ) && $classname) { - $htmlTag .= " class=\"$classname\""; - } - - return "<" . self::encrypt_tag($htmlTag) . ">$content"; - } - - /** - * Метод, осуществляющий кодирование (сохранение) информации - * с целью невозможности типографировать ее - * - * @param string $text - * @return string - */ - public static function encrypt_tag($text) - { - return base64_encode($text)."="; - } - - /** - * Метод, осуществляющий декодирование информации - * - * @param string $text - * @return string - */ - public static function decrypt_tag($text) - { - return base64_decode(substr($text,0,-1)); - } - - - - public static function strpos_ex(&$haystack, $needle, $offset = null) - { - if(is_array($needle)) - { - $m = false; - $w = false; - foreach($needle as $n) - { - $p = strpos($haystack, $n , $offset); - if($p===false) continue; - if($m === false) - { - $m = $p; - $w = $n; - continue; - } - if($p < $m) - { - $m = $p; - $w = $n; - } - } - if($m === false) return false; - return array('pos' => $m, 'str' => $w); - } - return strpos($haystack, $needle, $offset); - } - - public static function _process_selector_pattern(&$pattern) - { - if($pattern===false) return; - $pattern = preg_quote($pattern , '/'); - $pattern = str_replace("\\*", "[a-z0-9_\-]*", $pattern); - $pattern = "/".$pattern."/i"; - } - public static function _test_pattern($pattern, $text) - { - if($pattern === false) return true; - return preg_match($pattern, $text); - } - - public static function strtolower($string) - { - $convert_to = array( - "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", - "v", "w", "x", "y", "z", "à", "á", "â", "ã", "ä", "å", "æ", "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï", - "ð", "ñ", "ò", "ó", "ô", "õ", "ö", "ø", "ù", "ú", "û", "ü", "ý", "а", "б", "в", "г", "д", "е", "ё", "ж", - "з", "и", "й", "к", "л", "м", "н", "о", "п", "р", "с", "т", "у", "ф", "х", "ц", "ч", "ш", "щ", "ъ", "ы", - "ь", "э", "ю", "я" - ); - $convert_from = array( - "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", - "V", "W", "X", "Y", "Z", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", - "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "Ø", "Ù", "Ú", "Û", "Ü", "Ý", "А", "Б", "В", "Г", "Д", "Е", "Ё", "Ж", - "З", "И", "Й", "К", "Л", "М", "Н", "О", "П", "Р", "С", "Т", "У", "Ф", "Х", "Ц", "Ч", "Ш", "Щ", "Ъ", "Ъ", - "Ь", "Э", "Ю", "Я" - ); - - return str_replace($convert_from, $convert_to, $string); - } - - // взято с http://www.w3.org/TR/html4/sgml/entities.html - protected static $html4_char_ents = array( - 'nbsp' => 160, - 'iexcl' => 161, - 'cent' => 162, - 'pound' => 163, - 'curren' => 164, - 'yen' => 165, - 'brvbar' => 166, - 'sect' => 167, - 'uml' => 168, - 'copy' => 169, - 'ordf' => 170, - 'laquo' => 171, - 'not' => 172, - 'shy' => 173, - 'reg' => 174, - 'macr' => 175, - 'deg' => 176, - 'plusmn' => 177, - 'sup2' => 178, - 'sup3' => 179, - 'acute' => 180, - 'micro' => 181, - 'para' => 182, - 'middot' => 183, - 'cedil' => 184, - 'sup1' => 185, - 'ordm' => 186, - 'raquo' => 187, - 'frac14' => 188, - 'frac12' => 189, - 'frac34' => 190, - 'iquest' => 191, - 'Agrave' => 192, - 'Aacute' => 193, - 'Acirc' => 194, - 'Atilde' => 195, - 'Auml' => 196, - 'Aring' => 197, - 'AElig' => 198, - 'Ccedil' => 199, - 'Egrave' => 200, - 'Eacute' => 201, - 'Ecirc' => 202, - 'Euml' => 203, - 'Igrave' => 204, - 'Iacute' => 205, - 'Icirc' => 206, - 'Iuml' => 207, - 'ETH' => 208, - 'Ntilde' => 209, - 'Ograve' => 210, - 'Oacute' => 211, - 'Ocirc' => 212, - 'Otilde' => 213, - 'Ouml' => 214, - 'times' => 215, - 'Oslash' => 216, - 'Ugrave' => 217, - 'Uacute' => 218, - 'Ucirc' => 219, - 'Uuml' => 220, - 'Yacute' => 221, - 'THORN' => 222, - 'szlig' => 223, - 'agrave' => 224, - 'aacute' => 225, - 'acirc' => 226, - 'atilde' => 227, - 'auml' => 228, - 'aring' => 229, - 'aelig' => 230, - 'ccedil' => 231, - 'egrave' => 232, - 'eacute' => 233, - 'ecirc' => 234, - 'euml' => 235, - 'igrave' => 236, - 'iacute' => 237, - 'icirc' => 238, - 'iuml' => 239, - 'eth' => 240, - 'ntilde' => 241, - 'ograve' => 242, - 'oacute' => 243, - 'ocirc' => 244, - 'otilde' => 245, - 'ouml' => 246, - 'divide' => 247, - 'oslash' => 248, - 'ugrave' => 249, - 'uacute' => 250, - 'ucirc' => 251, - 'uuml' => 252, - 'yacute' => 253, - 'thorn' => 254, - 'yuml' => 255, - 'fnof' => 402, - 'Alpha' => 913, - 'Beta' => 914, - 'Gamma' => 915, - 'Delta' => 916, - 'Epsilon' => 917, - 'Zeta' => 918, - 'Eta' => 919, - 'Theta' => 920, - 'Iota' => 921, - 'Kappa' => 922, - 'Lambda' => 923, - 'Mu' => 924, - 'Nu' => 925, - 'Xi' => 926, - 'Omicron' => 927, - 'Pi' => 928, - 'Rho' => 929, - 'Sigma' => 931, - 'Tau' => 932, - 'Upsilon' => 933, - 'Phi' => 934, - 'Chi' => 935, - 'Psi' => 936, - 'Omega' => 937, - 'alpha' => 945, - 'beta' => 946, - 'gamma' => 947, - 'delta' => 948, - 'epsilon' => 949, - 'zeta' => 950, - 'eta' => 951, - 'theta' => 952, - 'iota' => 953, - 'kappa' => 954, - 'lambda' => 955, - 'mu' => 956, - 'nu' => 957, - 'xi' => 958, - 'omicron' => 959, - 'pi' => 960, - 'rho' => 961, - 'sigmaf' => 962, - 'sigma' => 963, - 'tau' => 964, - 'upsilon' => 965, - 'phi' => 966, - 'chi' => 967, - 'psi' => 968, - 'omega' => 969, - 'thetasym' => 977, - 'upsih' => 978, - 'piv' => 982, - 'bull' => 8226, - 'hellip' => 8230, - 'prime' => 8242, - 'Prime' => 8243, - 'oline' => 8254, - 'frasl' => 8260, - 'weierp' => 8472, - 'image' => 8465, - 'real' => 8476, - 'trade' => 8482, - 'alefsym' => 8501, - 'larr' => 8592, - 'uarr' => 8593, - 'rarr' => 8594, - 'darr' => 8595, - 'harr' => 8596, - 'crarr' => 8629, - 'lArr' => 8656, - 'uArr' => 8657, - 'rArr' => 8658, - 'dArr' => 8659, - 'hArr' => 8660, - 'forall' => 8704, - 'part' => 8706, - 'exist' => 8707, - 'empty' => 8709, - 'nabla' => 8711, - 'isin' => 8712, - 'notin' => 8713, - 'ni' => 8715, - 'prod' => 8719, - 'sum' => 8721, - 'minus' => 8722, - 'lowast' => 8727, - 'radic' => 8730, - 'prop' => 8733, - 'infin' => 8734, - 'ang' => 8736, - 'and' => 8743, - 'or' => 8744, - 'cap' => 8745, - 'cup' => 8746, - 'int' => 8747, - 'there4' => 8756, - 'sim' => 8764, - 'cong' => 8773, - 'asymp' => 8776, - 'ne' => 8800, - 'equiv' => 8801, - 'le' => 8804, - 'ge' => 8805, - 'sub' => 8834, - 'sup' => 8835, - 'nsub' => 8836, - 'sube' => 8838, - 'supe' => 8839, - 'oplus' => 8853, - 'otimes' => 8855, - 'perp' => 8869, - 'sdot' => 8901, - 'lceil' => 8968, - 'rceil' => 8969, - 'lfloor' => 8970, - 'rfloor' => 8971, - 'lang' => 9001, - 'rang' => 9002, - 'loz' => 9674, - 'spades' => 9824, - 'clubs' => 9827, - 'hearts' => 9829, - 'diams' => 9830, - 'quot' => 34, - 'amp' => 38, - 'lt' => 60, - 'gt' => 62, - 'OElig' => 338, - 'oelig' => 339, - 'Scaron' => 352, - 'scaron' => 353, - 'Yuml' => 376, - 'circ' => 710, - 'tilde' => 732, - 'ensp' => 8194, - 'emsp' => 8195, - 'thinsp' => 8201, - 'zwnj' => 8204, - 'zwj' => 8205, - 'lrm' => 8206, - 'rlm' => 8207, - 'ndash' => 8211, - 'mdash' => 8212, - 'lsquo' => 8216, - 'rsquo' => 8217, - 'sbquo' => 8218, - 'ldquo' => 8220, - 'rdquo' => 8221, - 'bdquo' => 8222, - 'dagger' => 8224, - 'Dagger' => 8225, - 'permil' => 8240, - 'lsaquo' => 8249, - 'rsaquo' => 8250, - 'euro' => 8364, - ); - /** - * Вернуть уникод символ по html entinty - * - * @param string $entity - * @return string - */ - public static function html_char_entity_to_unicode($entity) - { - if(isset(self::$html4_char_ents[$entity])) return self::_getUnicodeChar(self::$html4_char_ents[$entity]); - return false; - } - - /** - * Сконвериторвать все html entity в соответсвующие юникод символы - * - * @param string $text - */ - public static function convert_html_entities_to_unicode(&$text) - { - $text = preg_replace_callback("/\&#([0-9]+)\;/", - create_function('$m', 'return EMT_Lib::_getUnicodeChar(intval($m[1]));') - , $text); - $text = preg_replace_callback("/\&#x([0-9A-F]+)\;/", - create_function('$m', 'return EMT_Lib::_getUnicodeChar(hexdec($m[1]));') - , $text); - $text = preg_replace_callback("/\&([a-zA-Z0-9]+)\;/", - create_function('$m', '$r = EMT_Lib::html_char_entity_to_unicode($m[1]); return $r ? $r : $m[0];') - , $text); - } - - public static function rstrpos ($haystack, $needle, $offset = 0){ - - if(trim($haystack) != "" && trim($needle) != "" && $offset <= mb_strlen($haystack)) - { - $last_pos = $offset; - $found = false; - while(($curr_pos = mb_strpos($haystack, $needle, $last_pos)) !== false) - { - $found = true; - $last_pos = $curr_pos + 1; - } - if($found) - { - return $last_pos - 1; - } - else - { - return false; - } - } - else - { - return false; - } - } - - public static function ifop($cond, $true, $false) { - return $cond ? $true : $false; - } - - function split_number($num) { - return number_format($num, 0, '', ' '); - } - -} - - - -/** - * Базовый класс для группы правил обработки текста - * Класс группы должен наследовать, данный класс и задавать - * в нём EMT_Tret::rules и EMT_Tret::$name - * - */ -class EMT_Tret { - - /** - * Набор правил в данной группе, который задан изначально - * Его можно менять динамически добавляя туда правила с помощью put_rule - * - * @var unknown_type - */ - public $rules; - public $title; - - - private $disabled = array(); - private $enabled = array(); - protected $_text= ''; - public $logging = false; - public $logs = false; - public $errors = false; - public $debug_enabled = false; - public $debug_info = array(); - - - private $use_layout = false; - private $use_layout_set = false; - private $class_layout_prefix = false; - - public $class_names = array(); - public $classes = array(); - public $settings = array(); - - /** - * Защищенные теги - * - * @todo привязать к методам из Jare_Typograph_Tool - */ - const BASE64_PARAGRAPH_TAG = 'cA==='; // p - const BASE64_BREAKLINE_TAG = 'YnIgLw==='; // br / (с пробелом и слэшем) - const BASE64_NOBR_OTAG = 'bm9icg==='; // nobr - const BASE64_NOBR_CTAG = 'L25vYnI=='; // /nobr - - /** - * Типы кавычек - */ - const QUOTE_FIRS_OPEN = '«'; - const QUOTE_FIRS_CLOSE = '»'; - const QUOTE_CRAWSE_OPEN = '„'; - const QUOTE_CRAWSE_CLOSE = '“'; - - - private function log($str, $data = null) - { - if(!$this->logging) return; - $this->logs[] = array('info' => $str, 'data' => $data); - } - - private function error($info, $data = null) - { - $this->errors[] = array('info' => $info, 'data' => $data); - $this->log('ERROR: '. $info , $data); - } - - public function debug($place, &$after_text) - { - if(!$this->debug_enabled) return; - $this->debug_info[] = array( - 'place' => $place, - 'text' => $after_text, - ); - } - - - /** - * Установить режим разметки для данного Трэта если не было раньше установлено, - * EMT_Lib::LAYOUT_STYLE - с помощью стилей - * EMT_Lib::LAYOUT_CLASS - с помощью классов - * - * @param int $kind - */ - public function set_tag_layout_ifnotset($layout) - { - if($this->use_layout_set) return; - $this->use_layout = $layout; - } - - /** - * Установить режим разметки для данного Трэта, - * EMT_Lib::LAYOUT_STYLE - с помощью стилей - * EMT_Lib::LAYOUT_CLASS - с помощью классов - * EMT_Lib::LAYOUT_STYLE|EMT_Lib::LAYOUT_CLASS - оба метода - * - * @param int $kind - */ - public function set_tag_layout($layout = EMT_Lib::LAYOUT_STYLE) - { - $this->use_layout = $layout; - $this->use_layout_set = true; - } - - public function set_class_layout_prefix($prefix) - { - $this->class_layout_prefix = $prefix; - } - - - public function debug_on() - { - $this->debug_enabled = true; - } - - public function log_on() - { - $this->debug_enabled = true; - } - - - private function getmethod($name) - { - if(!$name) return false; - if(!method_exists($this, $name)) return false; - return array($this, $name); - } - - private function _pre_parse() - { - $this->pre_parse(); - foreach($this->rules as $rule) - { - if(!isset($rule['init'])) continue; - $m = $this->getmethod($rule['init']); - if(!$m) continue; - call_user_func($m); - } - } - private function _post_parse() - { - foreach($this->rules as $rule) - { - if(!isset($rule['deinit'])) continue; - $m = $this->getmethod($rule['deinit']); - if(!$m) continue; - call_user_func($m); - } - $this->post_parse(); - } - - private function rule_order_sort($a, $b) - { - if($a['order'] == $b['order']) return 0; - if($a['order'] < $b['order']) return -1; - return 1; - } - - private function apply_rule($rule) - { - $name = $rule['id']; - //$this->log("Правило $name", "Применяем правило"); - $disabled = (isset($this->disabled[$rule['id']]) && $this->disabled[$rule['id']]) || ((isset($rule['disabled']) && $rule['disabled']) && !(isset($this->enabled[$rule['id']]) && $this->enabled[$rule['id']])); - if($disabled) - { - $this->log("Правило $name", "Правило отключено" . ((isset($rule['disabled']) && $rule['disabled'])? " (по умолчанию)" : "")); - return; - } - if(isset($rule['function']) && $rule['function']) - { - if(!(isset($rule['pattern']) && $rule['pattern'])) - { - if(method_exists($this, $rule['function'])) - { - $this->log("Правило $name", "Используется метод ".$rule['function']." в правиле"); - - call_user_func(array($this, $rule['function'])); - return; - } - if(function_exists($rule['function'])) - { - $this->log("Правило $name", "Используется функция ".$rule['function']." в правиле"); - - call_user_func($rule['function']); - return; - } - - $this->error('Функция '.$rule['function'].' из правила '.$rule['id']. " не найдена"); - return ; - } else { - if(preg_match("/^[a-z_0-9]+$/i", $rule['function'])) - { - if(method_exists($this, $rule['function'])) - { - $this->log("Правило $name", "Замена с использованием preg_replace_callback с методом ".$rule['function'].""); - - $this->_text = preg_replace_callback($rule['pattern'], array($this, $rule['function']), $this->_text); - return; - } - if(function_exists($rule['function'])) - { - $this->log("Правило $name", "Замена с использованием preg_replace_callback с функцией ".$rule['function'].""); - - $this->_text = preg_replace_callback($rule['pattern'], $rule['function'], $this->_text); - return; - } - $this->error('Функция '.$rule['function'].' из правила '.$rule['id']. " не найдена"); - } else { - $this->_text = preg_replace_callback($rule['pattern'], create_function('$m', $rule['function']), $this->_text); - $this->log('Замена с использованием preg_replace_callback с инлайн функцией из правила '.$rule['id']); - return; - } - return ; - } - } - - if(isset($rule['simple_replace']) && $rule['simple_replace']) - { - if(isset($rule['case_sensitive']) && $rule['case_sensitive']) - { - $this->log("Правило $name", "Простая замена с использованием str_replace"); - $this->_text = str_replace($rule['pattern'], $rule['replacement'], $this->_text); - return; - } - $this->log("Правило $name", "Простая замена с использованием str_ireplace"); - $this->_text = str_ireplace($rule['pattern'], $rule['replacement'], $this->_text); - return; - } - - $pattern = $rule['pattern']; - if(is_string($pattern)) $pattern = array($pattern); - $eval = false; - foreach($pattern as $patt) - { - $chr = substr($patt,0,1); - $preg_arr = explode($chr, $patt); - if(strpos($preg_arr[count($preg_arr)-1], "e")!==false) - { - $eval = true; - break; - } - } - if(!$eval) - { - $this->log("Правило $name", "Замена с использованием preg_replace"); - - do { - $this->_text = preg_replace($rule['pattern'], $rule['replacement'], $this->_text); - if(!(isset($rule['cycled']) && $rule['cycled'])) break; - } while(preg_match($rule['pattern'], $this->_text)); - - return; - } - - $this->log("Правило $name", "Замена с использованием preg_replace_callback вместо eval"); - $k = 0; - foreach($pattern as $patt) - { - $repl = is_string($rule['replacement']) ? $rule['replacement'] : $rule['replacement'][$k]; - - $chr = substr($patt,0,1); - $preg_arr = explode($chr, $patt); - if(strpos($preg_arr[count($preg_arr)-1], "e")!==false) // eval система - { - $preg_arr[count($preg_arr)-1] = str_replace("e","",$preg_arr[count($preg_arr)-1]); - $patt = implode($chr, $preg_arr); - $this->thereplacement = $repl; - do { - $this->_text = preg_replace_callback($patt, array($this, "thereplcallback"), $this->_text); - if(!(isset($rule['cycled']) && $rule['cycled'])) break; - } while(preg_match($patt, $this->_text)); - - } else { - do { - $this->_text = preg_replace($patt, $repl, $this->_text); - if(!(isset($rule['cycled']) && $rule['cycled'])) break; - } while(preg_match($patt, $this->_text)); - } - $k++; - } - } - - - protected function preg_replace_e($pattern, $replacement, $text) - { - $chr = substr($pattern,0,1); - $preg_arr = explode($chr, $pattern); - if(strpos($preg_arr[count($preg_arr)-1], "e")===false) return preg_replace($pattern, $replacement, $text); - $preg_arr[count($preg_arr)-1] = str_replace("e","",$preg_arr[count($preg_arr)-1]); - $patt = implode($chr, $preg_arr); - $this->thereplacement = $replacement; - return preg_replace_callback($patt, array($this, "thereplcallback"), $text); - } - - private $thereplacement = ""; - private function thereplcallback($m) - { - $x = ""; - eval('$x = '.($this->thereplacement?$this->thereplacement:'""').';'); - return $x; - } - - private function _apply($list) - { - $this->errors = array(); - $this->_pre_parse(); - - $this->log("Применяется набор правил", implode(",",$list)); - - $rulelist = array(); - foreach($list as $k) - { - $rule = $this->rules[$k]; - $rule['id'] = $k; - $rule['order'] = isset($rule['order'])? $rule['order'] : 5 ; - $rulelist[] = $rule; - } - //usort($rulelist, array($this, "rule_order_sort")); - - foreach($rulelist as $rule) - { - $this->apply_rule($rule); - $this->debug($rule['id'], $this->_text); - } - - $this->_post_parse(); - } - - - /** - * Создание защищенного тега с содержимым - * - * @see EMT_lib::build_safe_tag - * @param string $content - * @param string $tag - * @param array $attribute - * @return string - */ - protected function tag($content, $tag = 'span', $attribute = array()) - { - if(isset($attribute['class'])) - { - $classname = $attribute['class']; - if($classname == "nowrap") - { - if(!$this->is_on('nowrap')) - { - $tag = "nobr"; - $attribute = array(); - $classname = ""; - } - } - if(isset($this->classes[$classname])) - { - $style_inline = $this->classes[$classname]; - if($style_inline) $attribute['__style'] = $style_inline; - } - $classname = (isset($this->class_names[$classname]) ? $this->class_names[$classname] :$classname); - $classname = ($this->class_layout_prefix ? $this->class_layout_prefix : "" ).$classname; - $attribute['class'] = $classname; - } - - return EMT_Lib::build_safe_tag($content, $tag, $attribute, - $this->use_layout === false? EMT_Lib::LAYOUT_STYLE : $this->use_layout ); - } - - - /** - * Добавить правило в группу - * - * @param string $name - * @param array $params - */ - public function put_rule($name, $params) - { - $this->rules[$name] = $params; - return $this; - } - - /** - * Отключить правило, в обработке - * - * @param string $name - */ - public function disable_rule($name) - { - $this->disabled[$name] = true; - unset($this->enabled[$name]); - } - - /** - * Включить правило - * - * @param string $name - */ - public function enable_rule($name) - { - $this->enabled[$name] = true; - unset($this->disabled[$name]); - } - - /** - * Добавить настройку в трет - * - * @param string $key ключ - * @param mixed $value значение - */ - public function set($key, $value) - { - $this->settings[$key] = $value; - } - - /** - * Установлена ли настройка - * - * @param string $key - */ - public function is_on($key) - { - if(!isset($this->settings[$key])) return false; - $kk = $this->settings[$key]; - return ((strtolower($kk)=="on") || ($kk === "1") || ($kk === true) || ($kk === 1)); - } - - /** - * Получить строковое значение настройки - * - * @param unknown_type $key - * @return unknown - */ - public function ss($key) - { - if(!isset($this->settings[$key])) return ""; - return strval($this->settings[$key]); - } - - /** - * Добавить настройку в правило - * - * @param string $rulename идентификатор правила - * @param string $key ключ - * @param mixed $value значение - */ - public function set_rule($rulename, $key, $value) - { - $this->rules[$rulename][$key] = $value; - } - - /** - * Включить правила, согласно списку - * - * @param array $list список правил - * @param boolean $disable выкллючить их или включить - * @param boolean $strict строго, т.е. те которые не в списку будут тоже обработаны - */ - public function activate($list,$disable =false, $strict = true) - { - if(!is_array($list)) return ; - - foreach($list as $rulename) - { - if($disable) $this->disable_rule($rulename); else $this->enable_rule($rulename); - } - - if($strict) - { - foreach($this->rules as $rulename => $v) - { - if(in_array($rulename, $list)) continue; - if(!$disable) $this->disable_rule($rulename); else $this->enable_rule($rulename); - } - } - } - - public function set_text(&$text) - { - $this->_text = &$text; - $this->debug_info = array(); - $this->logs = array(); - } - - - /** - * Применить к тексту - * - * @param string $text - текст к которому применить - * @param mixed $list - список правил, null - все правила - * @return string - */ - public function apply($list = null) - { - if(is_string($list)) $rlist = array($list); - elseif(is_array($list)) $rlist = $list; - else $rlist = array_keys($this->rules); - $this->_apply($rlist); - return $this->_text; - } - - - - - /** - * Код, выполняем до того, как применить правила - * - */ - public function pre_parse() - { - } - - /** - * После выполнения всех правил, выполняется этот метод - * - */ - public function post_parse() - { - } - - -} - - - - -/** - * @see EMT_Tret - */ - -class EMT_Tret_Abbr extends EMT_Tret -{ - public $title = "Сокращения"; - - public $domain_zones = array('ru','ру','com','ком','org','орг', 'уа', 'ua'); - - public $classes = array( - 'nowrap' => 'word-spacing:nowrap;', - ); - - public $rules = array( - 'nobr_abbreviation' => array( - 'description' => 'Расстановка пробелов перед сокращениями dpi, lpi', - 'pattern' => '/(\s+|^|\>)(\d+)(\040|\t)*(dpi|lpi)([\s\;\.\?\!\:\(]|$)/i', - 'replacement' => '\1\2 \4\5' - ), - 'nobr_acronym' => array( - 'description' => 'Расстановка пробелов перед сокращениями гл., стр., рис., илл., ст., п.', - 'pattern' => '/(\s|^|\>|\()(гл|стр|рис|илл?|ст|п|с)\.(\040|\t)*(\d+)(\ \;|\s|\.|\,|\?|\!|$)/iu', - 'replacement' => '\1\2. \4\5' - ), - 'nobr_sm_im' => array( - 'description' => 'Расстановка пробелов перед сокращениями см., им.', - 'pattern' => '/(\s|^|\>|\()(см|им)\.(\040|\t)*([а-яё0-9a-z]+)(\s|\.|\,|\?|\!|$)/iu', - 'replacement' => '\1\2. \4\5' - ), - 'nobr_locations' => array( - 'description' => 'Расстановка пробелов в сокращениях г., ул., пер., д.', - 'pattern' => array( - '/(\s|^|\>)(г|ул|пер|просп|пл|бул|наб|пр|ш|туп)\.(\040|\t)*([а-яё0-9a-z]+)(\s|\.|\,|\?|\!|$)/iu', - '/(\s|^|\>)(б\-р|пр\-кт)(\040|\t)*([а-яё0-9a-z]+)(\s|\.|\,|\?|\!|$)/iu', - '/(\s|^|\>)(д|кв|эт)\.(\040|\t)*(\d+)(\s|\.|\,|\?|\!|$)/iu', - ), - 'replacement' => array( - '\1\2. \4\5', - '\1\2 \4\5', - '\1\2. \4\5', - ) - ), - 'nbsp_before_unit' => array( - 'description' => 'Замена символов и привязка сокращений в размерных величинах: м, см, м2…', - 'pattern' => array( - '/(\s|^|\>|\ \;|\,)(\d+)( |\ \;)?(м|мм|см|дм|км|гм|km|dm|cm|mm)(\s|\.|\!|\?|\,|$|\±\;|\;|\<)/iu', - '/(\s|^|\>|\ \;|\,)(\d+)( |\ \;)?(м|мм|см|дм|км|гм|km|dm|cm|mm)([32]|³|²)(\s|\.|\!|\?|\,|$|\±\;|\;|\<)/iue' - ), - 'replacement' => array( - '\1\2 \4\5', - '$m[1].$m[2]." ".$m[4].($m[5]=="3"||$m[5]=="2"? "&sup".$m[5].";" : $m[5] ).$m[6]' - ), - ), - 'nbsp_before_weight_unit' => array( - 'description' => 'Замена символов и привязка сокращений в весовых величинах: г, кг, мг…', - 'pattern' => '/(\s|^|\>|\ \;|\,)(\d+)( |\ \;)?(г|кг|мг|т)(\s|\.|\!|\?|\,|$|\ \;|\;)/iu', - 'replacement' => '\1\2 \4\5', - ), - 'nobr_before_unit_volt' => array( - 'description' => 'Установка пробельных символов в сокращении вольт', - 'pattern' => '/(\d+)([вВ]| В)(\s|\.|\!|\?|\,|$)/u', - 'replacement' => '\1 В\3' - ), - 'ps_pps' => array( - 'description' => 'Объединение сокращений P.S., P.P.S.', - 'pattern' => '/(^|\040|\t|\>|\r|\n)(p\.\040?)(p\.\040?)?(s\.)([^\<])/ie', - 'replacement' => '$m[1] . $this->tag(trim($m[2]) . " " . ($m[3] ? trim($m[3]) . " " : ""). $m[4], "span", array("class" => "nowrap") ).$m[5] ' - ), - 'nobr_vtch_itd_itp' => array( - 'description' => 'Объединение сокращений и т.д., и т.п., в т.ч.', - 'cycled' => true, - 'pattern' => array( - '/(^|\s|\ \;)и( |\ \;)т\.?[ ]?д(\.|$|\s|\ \;)/ue', - '/(^|\s|\ \;)и( |\ \;)т\.?[ ]?п(\.|$|\s|\ \;)/ue', - '/(^|\s|\ \;)в( |\ \;)т\.?[ ]?ч(\.|$|\s|\ \;)/ue', - ), - 'replacement' => array( - '$m[1].$this->tag("и т. д.", "span", array("class" => "nowrap")).($m[3]!="."? $m[3] : "" )', - '$m[1].$this->tag("и т. п.", "span", array("class" => "nowrap")).($m[3]!="."? $m[3] : "" )', - '$m[1].$this->tag("в т. ч.", "span", array("class" => "nowrap")).($m[3]!="."? $m[3] : "" )', - ) - ), - 'nbsp_te' => array( - 'description' => 'Обработка т.е.', - 'pattern' => '/(^|\s|\ \;)([тТ])\.?[ ]?е\./ue', - 'replacement' => '$m[1].$this->tag($m[2].". е.", "span", array("class" => "nowrap"))', - ), - 'nbsp_money_abbr' => array( - 'description' => 'Форматирование денежных сокращений (расстановка пробелов и привязка названия валюты к числу)', - 'pattern' => '/(\d)((\040|\ \;)?(тыс|млн|млрд)\.?(\040|\ \;)?)?(\040|\ \;)?(руб\.|долл\.|евро|€|€|\$|у[\.]? ?е[\.]?)/ieu', - 'replacement' => '$m[1].($m[4]?" ".$m[4].($m[4]=="тыс"?".":""):"")." ".(!preg_match("#у[\\\\.]? ?е[\\\\.]?#iu",$m[7])?$m[7]:"у.е.")', - ), - 'nbsp_money_abbr_rev' => array( - 'description' => 'Привязка валюты к числу спереди', - 'pattern' => '/(€|€|\$)\s?(\d)/iu', - 'replacement' => '\1 \2' - ), - 'nbsp_org_abbr' => array( - 'description' => 'Привязка сокращений форм собственности к названиям организаций', - 'pattern' => '/([^a-zA-Zа-яёА-ЯЁ]|^)(ООО|ЗАО|ОАО|НИИ|ПБОЮЛ) ([a-zA-Zа-яёА-ЯЁ]|\"|\«\;|\&bdquo\;|<)/u', - 'replacement' => '\1\2 \3' - ), - 'nobr_gost' => array( - 'description' => 'Привязка сокращения ГОСТ к номеру', - 'pattern' => array( - '/(\040|\t|\ \;|^)ГОСТ( |\ \;)?(\d+)((\-|\&minus\;|\&mdash\;)(\d+))?(( |\ \;)(\-|\&mdash\;))?/ieu', - '/(\040|\t|\ \;|^|\>)ГОСТ( |\ \;)?(\d+)(\-|\&minus\;|\&mdash\;)(\d+)/ieu', - ), - 'replacement' => array( - '$m[1].$this->tag("ГОСТ ".$m[3].(isset($m[6])?"–".$m[6]:"").(isset($m[7])?" —":""),"span", array("class"=>"nowrap"))', - '$m[1]."ГОСТ ".$m[3]."–".$m[5]', - ), - ), - /* - 'nobr_vtch_itd_itp' => array( - 'description' => 'Привязка сокращений до н.э., н.э.', - 'pattern' => array( - - //IV в до н.э, в V-VIвв до нэ., третий в. н.э. - - '/(\s|\ \;)и( |\ \;)т\.?[ ]?д\./ue', - '/(\s|\ \;)и( |\ \;)т\.?[ ]?п\./ue', - '/(\s|\ \;)в( |\ \;)т\.?[ ]?ч\./ue', - ), - 'replacement' => array( - '$m[1].$this->tag("и т. д.", "span", array("class" => "nowrap"))', - '$m[1].$this->tag("и т. п.", "span", array("class" => "nowrap"))', - '$m[1].$this->tag("в т. ч.", "span", array("class" => "nowrap"))', - ) - ), - */ - - - ); -} - - -/** - * @see EMT_Tret - */ - -class EMT_Tret_Dash extends EMT_Tret -{ - public $title = "Дефисы и тире"; - public $rules = array( - 'mdash_symbol_to_html_mdash' => array( - 'description' => 'Замена символа тире на html конструкцию', - 'pattern' => '/—/iu', - 'replacement' => '—' - ), - 'mdash' => array( - 'description' => 'Тире после кавычек, скобочек, пунктуации', - 'pattern' => array( - '/([a-zа-яё0-9]+|\,|\:|\)|\&(ra|ld)quo\;|\|\"|\>)(\040|\t)(—|\-|\&mdash\;)(\s|$|\<)/ui', - '/(\,|\:|\)|\")(—|\-|\&mdash\;)(\s|$|\<)/ui', - ), - 'replacement' => array( - '\1 —\5', - '\1 —\3', - ), - ), - 'mdash_2' => array( - 'description' => 'Тире после переноса строки', - 'pattern' => '/(\n|\r|^|\>)(\-|\&mdash\;)(\t|\040)/', - 'replacement' => '\1— ' - ), - 'mdash_3' => array( - 'description' => 'Тире после знаков восклицания, троеточия и прочее', - 'pattern' => '/(\.|\!|\?|\&hellip\;)(\040|\t|\ \;)(\-|\&mdash\;)(\040|\t|\ \;)/', - 'replacement' => '\1 — ' - ), - 'iz_za_pod' => array( - 'description' => 'Расстановка дефисов между из-за, из-под', - 'pattern' => '/(\s|\ \;|\>|^)(из)(\040|\t|\ \;)\-?(за|под)([\.\,\!\?\:\;]|\040|\ \;)/uie', - 'replacement' => '($m[1] == " " ? " " : $m[1]) . $m[2]."-".$m[4] . ($m[5] == " "? " " : $m[5])' - ), - 'to_libo_nibud' => array( - 'description' => 'Автоматическая простановка дефисов в обезличенных местоимениях и междометиях', - 'cycled' => true, - 'pattern' => '/(\s|^|\ \;|\>)(кто|кем|когда|зачем|почему|как|что|чем|где|чего|кого)\-?(\040|\t|\ \;)\-?(то|либо|нибудь)([\.\,\!\?\;]|\040|\ \;|$)/uie', - 'replacement' => '($m[1] == " " ? " " : $m[1]) . $m[2]."-".$m[4] . ($m[5] == " "? " " : $m[5])' - ), - 'koe_kak' => array( - 'description' => 'Кое-как, кой-кого, все-таки', - 'cycled' => true, - 'pattern' => array( - '/(\s|^|\ \;|\>)(кое)\-?(\040|\t|\ \;)\-?(как)([\.\,\!\?\;]|\040|\ \;|$)/uie', - '/(\s|^|\ \;|\>)(кой)\-?(\040|\t|\ \;)\-?(кого)([\.\,\!\?\;]|\040|\ \;|$)/uie', - '/(\s|^|\ \;|\>)(вс[её])\-?(\040|\t|\ \;)\-?(таки)([\.\,\!\?\;]|\040|\ \;|$)/uie', - ), - 'replacement' => '($m[1] == " " ? " " : $m[1]) . $m[2]."-".$m[4] . ($m[5] == " "? " " : $m[5])' - ), - 'ka_de_kas' => array( - 'description' => 'Расстановка дефисов с частицами ка, де, кась', - 'disabled' => true, - 'pattern' => array( - '/(\s|^|\ \;|\>)([а-яё]+)(\040|\t|\ \;)(ка)([\.\,\!\?\;]|\040|\ \;|$)/uie', - '/(\s|^|\ \;|\>)([а-яё]+)(\040|\t|\ \;)(де)([\.\,\!\?\;]|\040|\ \;|$)/uie', - '/(\s|^|\ \;|\>)([а-яё]+)(\040|\t|\ \;)(кась)([\.\,\!\?\;]|\040|\ \;|$)/uie', - ), - 'replacement' => '($m[1] == " " ? " " : $m[1]) . $m[2]."-".$m[4] . ($m[5] == " "? " " : $m[5])' - ), - - - - ); - -} - - -/** - * @see EMT_Tret - */ - -class EMT_Tret_Date extends EMT_Tret -{ - public $title = "Даты и дни"; - - public $classes = array( - 'nowrap' => 'word-spacing:nowrap;', - ); - - public $rules = array( - 'years' => array( - 'description' => 'Установка тире и пробельных символов в периодах дат', - 'pattern' => '/(с|по|период|середины|начала|начало|конца|конец|половины|в|между|\([cс]\)|\©\;)(\s+|\ \;)([\d]{4})(-|\&mdash\;|\&minus\;)([\d]{4})(( |\ \;)?(г\.г\.|гг\.|гг|г\.|г)([^а-яёa-z]))?/eui', - '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]:"")' - ), - 'mdash_month_interval' => array( - 'description' => 'Расстановка тире и объединение в неразрывные периоды месяцев', - 'disabled' => true, - 'pattern' => '/((январ|феврал|сентябр|октябр|ноябр|декабр)([ьяюе]|[её]м)|(апрел|июн|июл)([ьяюе]|ем)|(март|август)([ауе]|ом)?|ма[йяюе]|маем)\-((январ|феврал|сентябр|октябр|ноябр|декабр)([ьяюе]|[её]м)|(апрел|июн|июл)([ьяюе]|ем)|(март|август)([ауе]|ом)?|ма[йяюе]|маем)/iu', - 'replacement' => '\1—\8' - ), - 'nbsp_and_dash_month_interval' => array( - 'description' => 'Расстановка тире и объединение в неразрывные периоды дней', - 'disabled' => true, - 'pattern' => '/([^\>]|^)(\d+)(\-|\&minus\;|\&mdash\;)(\d+)( |\ \;)(января|февраля|марта|апреля|мая|июня|июля|августа|сентября|октября|ноября|декабря)([^\<]|$)/ieu', - 'replacement' => '$m[1].$this->tag($m[2]."—".$m[4]." ".$m[6],"span", array("class"=>"nowrap")).$m[7]' - ), - 'nobr_year_in_date' => array( - 'description' => 'Привязка года к дате', - 'pattern' => array( - '/(\s|\ \;)([0-9]{2}\.[0-9]{2}\.([0-9]{2})?[0-9]{2})(\s|\ \;)?г(\.|\s|\ \;)/eiu', - '/(\s|\ \;)([0-9]{2}\.[0-9]{2}\.([0-9]{2})?[0-9]{2})(\s|\ \;|\.(\s|\ \;|$)|$)/eiu', - ), - 'replacement' => array( - '$m[1].$this->tag($m[2]." г.","span", array("class"=>"nowrap")).($m[5]==="."?"":" ")', - '$m[1].$this->tag($m[2],"span", array("class"=>"nowrap")).$m[4]', - ), - ), - 'space_posle_goda' => array( - 'description' => 'Пробел после года', - 'pattern' => '/(^|\040|\ \;)([0-9]{3,4})(год([ауе]|ом)?)([^a-zа-яё]|$)/ui', - 'replacement' => '\1\2 \3\5' - ), - 'nbsp_posle_goda_abbr' => array( - 'description' => 'Пробел после года', - 'pattern' => '/(^|\040|\ \;|\"|\«\;)([0-9]{3,4})[ ]?(г\.)([^a-zа-яё]|$)/ui', - 'replacement' => '\1\2 \3\4' - ), - - ); -} - - -/** - * @see EMT_Tret - */ - -class EMT_Tret_Etc extends EMT_Tret -{ - - - public $classes = array( - 'nowrap' => 'word-spacing:nowrap;', - ); - - - /** - * Базовые параметры тофа - * - * @var array - */ - public $title = "Прочее"; - public $rules = array( - 'acute_accent' => array( - 'description' => 'Акцент', - 'pattern' => '/(у|е|ы|а|о|э|я|и|ю|ё)\`(\w)/i', - 'replacement' => '\1́\2' - ), - - - - 'word_sup' => array( - 'description' => 'Надстрочный текст после символа ^', - 'pattern' => '/((\s|\ \;|^)+)\^([a-zа-яё0-9\.\:\,\-]+)(\s|\ \;|$|\.$)/ieu', - 'replacement' => '"" . $this->tag($this->tag($m[3],"small"),"sup") . $m[4]' - ), - 'century_period' => array( - 'description' => 'Тире между диапозоном веков', - 'pattern' => '/(\040|\t|\ \;|^)([XIV]{1,5})(-|\&mdash\;)([XIV]{1,5})(( |\ \;)?(в\.в\.|вв\.|вв|в\.|в))/eu', - 'replacement' => '$m[1] .$this->tag($m[2]."—".$m[4]." вв.","span", array("class"=>"nowrap"))' - ), - 'time_interval' => array( - 'description' => 'Тире и отмена переноса между диапозоном времени', - 'pattern' => '/([^\d\>]|^)([\d]{1,2}\:[\d]{2})(-|\&mdash\;|\&minus\;)([\d]{1,2}\:[\d]{2})([^\d\<]|$)/eui', - 'replacement' => '$m[1] . $this->tag($m[2]."—".$m[4],"span", array("class"=>"nowrap")).$m[5]' - ), - 'split_number_to_triads' => array( - 'description' => 'Разбиение числа на триады', - 'pattern' => '/([^a-zA-Z0-9<\)]|^)([0-9]{5,})([^a-zA-Z>\(]|$)/eu', - 'replacement' => '$m[1].str_replace(" "," ",EMT_Lib::split_number($m[2])).$m[3] ' - //'function' => 'split_number' - ), - 'expand_no_nbsp_in_nobr' => array( - 'description' => 'Удаление nbsp в nobr/nowrap тэгах', - 'function' => 'remove_nbsp' - ), - 'nobr_to_nbsp' => array( - 'description' => 'Преобразование nobr в nbsp', - 'disabled' => true, - 'function' => 'nobr_to_nbsp' - ), - ); - - protected function remove_nbsp() - { - $thetag = $this->tag("###", 'span', array('class' => "nowrap")); - $arr = explode("###", $thetag); - $b = preg_quote($arr[0], '/'); - $e = preg_quote($arr[1], '/'); - - $match = '/(^|[^a-zа-яё])([a-zа-яё]+)\ \;('.$b.')/iu'; - do { - $this->_text = preg_replace($match, '\1\3\2 ', $this->_text); - } while(preg_match($match, $this->_text)); - - $match = '/('.$e.')\ \;([a-zа-яё]+)($|[^a-zа-яё])/iu'; - do { - $this->_text = preg_replace($match, ' \2\1\3', $this->_text); - } while(preg_match($match, $this->_text)); - - $this->_text = $this->preg_replace_e('/'.$b.'.*?'.$e.'/iue', 'str_replace(" "," ",$m[0]);' , $this->_text ); - } - - protected function nobr_to_nbsp() - { - $thetag = $this->tag("###", 'span', array('class' => "nowrap")); - $arr = explode("###", $thetag); - $b = preg_quote($arr[0], '/'); - $e = preg_quote($arr[1], '/'); - $this->_text = $this->preg_replace_e('/'.$b.'(.*?)'.$e.'/iue', 'str_replace(" "," ",$m[1]);' , $this->_text ); - } - /* - protected function split_number () { - - $this->preg_replace_e("/([^a-zA-Z<]|^)([0-9]{5,})([^a-zA-Z>]|$)/u", ) - - $match = ; - while(preg_match($match, $this->_text, $m)) { - $repl = ""; - for($i = strlen($m[2]); $i >=0 ; $i-=3) - if($i-3>=0) $repl = ($i>3?" ":"").substr($m[2], $i-3, 3) . $repl; else $repl = substr($m[2], 0, $i) . $repl; - $this->_text = str_replace($m[1], $repl, $this->_text); - } - }*/ - -} - - - - -/** - * @see EMT_Tret - */ - -class EMT_Tret_Nobr extends EMT_Tret -{ - public $title = "Неразрывные конструкции"; - - public $classes = array( - 'nowrap' => 'word-spacing:nowrap;', - ); - - public $rules = array( - - 'super_nbsp' => array( - 'description' => 'Привязка союзов и предлогов к написанным после словам', - 'pattern' => '/(\s|^|\&(la|bd)quo\;|\>|\(|\&mdash\;\ \;)([a-zа-яё]{1,2}\s+)([a-zа-яё]{1,2}\s+)?([a-zа-яё0-9\-]{2,}|[0-9])/ieu', - 'replacement' => '$m[1] . trim($m[3]) . " " . ($m[4] ? trim($m[4]) . " " : "") . $m[5]' - ), - 'nbsp_in_the_end' => array( - 'description' => 'Привязка союзов и предлогов к предыдущим словам в случае конца предложения', - 'pattern' => '/([a-zа-яё0-9\-]{3,}) ([a-zа-яё]{1,2})\.( [A-ZА-ЯЁ]|$)/u', - 'replacement' => '\1 \2.\3' - ), - 'phone_builder' => array( - 'description' => 'Объединение в неразрывные конструкции номеров телефонов', - 'pattern' => - array( - '/([^\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', - '/([^\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', - ), - 'replacement' => - array( - '$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]', - '$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]', - ), - ), - 'phone_builder_v2' => array( - 'description' => 'Дополнительный формат номеров телефонов', - 'pattern' => '/([^\d]|^)\+\s?([0-9]{1})\s?\(([0-9]{3,4})\)\s?(\d{3})(\d{2})(\d{2})([^\d]|$)/ie', - 'replacement' => '$m[1].$this->tag("+".$m[2]." ".$m[3]." ".$m[4]."-".$m[5]."-".$m[6], "span", array("class" => "nowrap")).$m[7]', - ), - 'ip_address' => array( - 'description' => 'Объединение IP-адресов', - 'pattern' => '/(\s|\ \;|^)(\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3})/ie', - 'replacement' => '$m[1] . $this->nowrap_ip_address($m[2])' - ), - 'dots_for_surname_abbr' => array( - 'disabled' => true, - 'description' => 'Простановка точек к инициалам у фамилии', - 'pattern' => - array( - '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ])\.?(\s|\ \;)?([А-ЯЁ])(\s|\ \;)([А-ЯЁ][а-яё]+)(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', - '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ][а-яё]+)(\s|\ \;)([А-ЯЁ])\.?(\s|\ \;)?([А-ЯЁ])\.?(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', - ), - 'replacement' => - array( - '$m[1].$this->tag($m[2].". ".$m[4].". ".$m[6], "span", array("class" => "nowrap")).$m[7]', - '$m[1].$this->tag($m[2]." ".$m[4].". ".$m[6].".", "span", array("class" => "nowrap")).$m[7]', - ), - ), - 'spaces_nobr_in_surname_abbr' => array( - 'description' => 'Привязка инициалов к фамилиям', - 'pattern' => - array( - '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ])\.(\s|\ \;)?([А-ЯЁ])\.(\s|\ \;)?([А-ЯЁ][а-яё]+)(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', - '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ][а-яё]+)(\s|\ \;)([А-ЯЁ])\.(\s|\ \;)?([А-ЯЁ])\.(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', - '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ])(\s|\ \;)?([А-ЯЁ])(\s|\ \;)([А-ЯЁ][а-яё]+)(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', - '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ][а-яё]+)(\s|\ \;)([А-ЯЁ])(\s|\ \;)?([А-ЯЁ])(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', - //'/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([A-Z])\.?(\s|\ \;)?([A-Z])(\.(\s|\ \;)?|(\s|\ \;))([A-Z][a-z]+)(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', - //'/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([A-Z][a-z]+)(\s|\ \;)([A-Z])\.?(\s|\ \;)?([A-Z])\.?(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', - ), - 'replacement' => - array( - '$m[1].$this->tag($m[2].". ".$m[4].". ".$m[6], "span", array("class" => "nowrap")).$m[7]', - '$m[1].$this->tag($m[2]." ".$m[4].". ".$m[6].".", "span", array("class" => "nowrap")).$m[7]', - '$m[1].$this->tag($m[2].(isset($m[3])? " " : "" ).$m[4].(isset($m[5])? " " : "" ).$m[6], "span", array("class" => "nowrap")).$m[7]', - '$m[1].$this->tag($m[2]." ".$m[4].(isset($m[5])? " " : "" ).$m[6], "span", array("class" => "nowrap")).$m[7]', - //'$m[1].$this->tag($m[2].". ".$m[4].". ".$m[8], "span", array("class" => "nowrap")).$m[9]', - //'$m[1].$this->tag($m[2]." ".$m[4].". ".$m[6].".", "span", array("class" => "nowrap")).$m[7]', - ), - ), - 'nbsp_before_particle' => array( - 'description' => 'Неразрывный пробел перед частицей', - 'pattern' => '/(\040|\t)+(ли|бы|б|же|ж)(\ \;|\.|\,|\:|\;|\&hellip\;|\?|\s)/iue', - 'replacement' => '" ".$m[2] . ($m[3] == " " ? " " : $m[3])' - ), - 'nbsp_v_kak_to' => array( - 'description' => 'Неразрывный пробел в как то', - 'pattern' => '/как то\:/ui', - 'replacement' => 'как то:' - ), - 'nbsp_celcius' => array( - 'description' => 'Привязка градусов к числу', - 'pattern' => '/(\s|^|\>|\ \;)(\d+)( |\ \;)?(°|\°\;)(C|С)(\s|\.|\!|\?|\,|$|\ \;|\;)/iu', - 'replacement' => '\1\2 \4C\6' - ), - 'hyphen_nowrap_in_small_words' => array( - 'description' => 'Обрамление пятисимвольных слов разделенных дефисом в неразрывные блоки', - 'disabled' => true, - 'cycled' => true, - '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', - 'replacement' => '$m[1] . $this->tag($m[2], "span", array("class"=>"nowrap")) . $m[4]', - ), - 'hyphen_nowrap' => array( - 'description' => 'Отмена переноса слова с дефисом', - 'disabled' => true, - 'cycled' => true, - 'pattern' => '/(\ \;|\s|\>|^)([a-zа-яё]+)((\-([a-zа-яё]+)){1,2})(\s|\.|\,|\!|\?|\ \;|\&hellip\;|$)/uie', - 'replacement' => '$m[1] . $this->tag($m[2].$m[3], "span", array("class"=>"nowrap")) . $m[6]' - ), - ); - - /** - * Объединение IP-адрессов в неразрывные конструкции (IPv4 only) - * - * @param unknown_type $triads - * @return unknown - */ - protected function nowrap_ip_address($triads) - { - $triad = explode('.', $triads); - $addTag = true; - - foreach ($triad as $value) { - $value = (int) $value; - if ($value > 255) { - $addTag = false; - break; - } - } - - if (true === $addTag) { - $triads = $this->tag($triads, 'span', array('class' => "nowrap")); - } - - return $triads; - } -} - - -/** - * @see EMT_Tret - */ - -class EMT_Tret_Number extends EMT_Tret -{ - public $title = "Числа, дроби, математические знаки"; - - - public $rules = array( - 'minus_between_nums' => array( - 'description' => 'Расстановка знака минус между числами', - 'pattern' => '/(\d+)\-(\d)/i', - 'replacement' => '\1−\2' - ), - 'minus_in_numbers_range' => array( - 'description' => 'Расстановка знака минус между диапозоном чисел', - 'pattern' => '/(^|\s|\ \;)(\&minus\;|\-)(\d+)(\.\.\.|\&hellip\;)(\s|\ \;)?(\+|\-|\&minus\;)?(\d+)/ie', - 'replacement' => '$m[1] ."−".$m[3] . $m[4].$m[5].($m[6]=="+"?$m[6]:"−").$m[7]' - ), - 'auto_times_x' => array( - 'description' => 'Замена x на символ × в размерных единицах', - 'cycled' => true, - 'pattern' => '/([^a-zA-Z><]|^)(\×\;)?(\d+)(\040*)(x|х)(\040*)(\d+)([^a-zA-Z><]|$)/u', - 'replacement' => '\1\2\3×\7\8' - ), - 'numeric_sub' => array( - 'description' => 'Нижний индекс', - 'pattern' => '/([a-zа-яё0-9])\_([\d]{1,3})([^@а-яёa-z0-9]|$)/ieu', - 'replacement' => '$m[1] . $this->tag($this->tag($m[2],"small"),"sub") . $m[3]' - ), - 'numeric_sup' => array( - 'description' => 'Верхний индекс', - 'pattern' => '/([a-zа-яё0-9])\^([\d]{1,3})([^а-яёa-z0-9]|$)/ieu', - 'replacement' => '$m[1] . $this->tag($this->tag($m[2],"small"),"sup") . $m[3]' - ), - 'simple_fraction' => array( - 'description' => 'Замена дробей 1/2, 1/4, 3/4 на соответствующие символы', - 'pattern' => array('/(^|\D)1\/(2|4)(\D)/', '/(^|\D)3\/4(\D)/'), - 'replacement' => array('\1&frac1\2;\3', '\1¾\2') - ), - 'math_chars' => array( - 'description' => 'Математические знаки больше/меньше/плюс минус/неравно', - 'pattern' => array('/!=/', '/\<=/', '/([^=]|^)\>=/', '/~=/', '/\+-/'), - 'replacement' => array('≠', '≤', '\1≥', '≅', '±' ) - ), - - 'thinsp_between_number_triads' => array( - 'description' => 'Объединение триад чисел полупробелом', - 'pattern' => '/([0-9]{1,3}( [0-9]{3}){1,})(.|$)/ue', - 'replacement' => '($m[3]=="-"? $m[0]:str_replace(" "," ",$m[1]).$m[3])' - ), - 'thinsp_between_no_and_number' => array( - 'description' => 'Пробел между симоволом номера и числом', - 'pattern' => '/(№|\№\;)(\s| )*(\d)/iu', - 'replacement' => '№ \3' - ), - 'thinsp_between_sect_and_number' => array( - 'description' => 'Пробел между параграфом и числом', - 'pattern' => '/(§|\§\;)(\s| )*(\d+|[IVX]+|[a-zа-яё]+)/ui', - 'replacement' => '§ \3' - ), - ); -} - - -/** - * @see EMT_Tret - */ - -class EMT_Tret_OptAlign extends EMT_Tret -{ - - public $classes = array( - 'oa_obracket_sp_s' => "margin-right:0.3em;", - "oa_obracket_sp_b" => "margin-left:-0.3em;", - "oa_obracket_nl_b" => "margin-left:-0.3em;", - "oa_comma_b" => "margin-right:-0.2em;", - "oa_comma_e" => "margin-left:0.2em;", - 'oa_oquote_nl' => "margin-left:-0.44em;", - 'oa_oqoute_sp_s' => "margin-right:0.44em;", - 'oa_oqoute_sp_q' => "margin-left:-0.44em;", - ); - - /** - * Базовые параметры тофа - * - * @var array - */ - public $title = "Оптическое выравнивание"; - public $rules = array( - 'oa_oquote' => array( - 'description' => 'Оптическое выравнивание открывающей кавычки', - //'disabled' => true, - 'pattern' => array( - '/([a-zа-яё\-]{3,})(\040|\ \;|\t)(\«\;)/uie', - '/(\n|\r|^)(\«\;)/ei' - ), - 'replacement' => array( - '$m[1] . $this->tag($m[2], "span", array("class"=>"oa_oqoute_sp_s")) . $this->tag($m[3], "span", array("class"=>"oa_oqoute_sp_q"))', - '$m[1] . $this->tag($m[2], "span", array("class"=>"oa_oquote_nl"))', - ), - ), - 'oa_oquote_extra' => array( - 'description' => 'Оптическое выравнивание кавычки', - //'disabled' => true, - 'function' => 'oaquote_extra' - ), - 'oa_obracket_coma' => array( - 'description' => 'Оптическое выравнивание для пунктуации (скобка)', - //'disabled' => true, - 'pattern' => array( - '/(\040|\ \;|\t)\(/ei', - '/(\n|\r|^)\(/ei', - //'/([а-яёa-z0-9]+)\,(\040+)/iue', - ), - 'replacement' => array( - '$this->tag($m[1], "span", array("class"=>"oa_obracket_sp_s")) . $this->tag("(", "span", array("class"=>"oa_obracket_sp_b"))', - '$m[1] . $this->tag("(", "span", array("class"=>"oa_obracket_nl_b"))', - //'$m[1] . $this->tag(",", "span", array("class"=>"oa_comma_b")) . $this->tag(" ", "span", array("class"=>"oa_comma_e"))', - ), - ), - - ); - - /** - * Если стоит открывающая кавычка после

надо делать её висячей - * - * @return void - */ - protected function oaquote_extra() - { - $this->_text = $this->preg_replace_e( - '/(<' .self::BASE64_PARAGRAPH_TAG . '>)([\040\t]+)?(\«\;)/e', - '$m[1] . $this->tag($m[3], "span", array("class"=>"oa_oquote_nl"))', - $this->_text); - } - - -} - - -/** - * @see EMT_Tret - */ - -class EMT_Tret_Punctmark extends EMT_Tret -{ - public $title = "Пунктуация и знаки препинания"; - - public $rules = array( - 'auto_comma' => array( - 'description' => 'Расстановка запятых перед а, но', - 'pattern' => '/([a-zа-яё])(\s| )(но|а)(\s| )/iu', - 'replacement' => '\1,\2\3\4' - ), - 'punctuation_marks_limit' => array( - 'description' => 'Лишние восклицательные, вопросительные знаки и точки', - 'pattern' => '/([\!\.\?]){4,}/', - 'replacement' => '\1\1\1' - ), - 'punctuation_marks_base_limit' => array( - 'description' => 'Лишние запятые, двоеточия, точки с запятой', - 'pattern' => '/([\,]|[\:]|[\;]]){2,}/', - 'replacement' => '\1' - ), - 'hellip' => array( - 'description' => 'Замена трех точек на знак многоточия', - 'simple_replace'=> true, - 'pattern' => '...', - 'replacement' => '…' - ), - 'fix_excl_quest_marks' => array( - 'description' => 'Замена восклицательного и вопросительного знаков местами', - 'pattern' => '/([a-zа-яё0-9])\!\?(\s|$|\<)/ui', - 'replacement' => '\1?!\2' - ), - 'fix_pmarks' => array( - 'description' => 'Замена сдвоенных знаков препинания на одинарные', - 'pattern' => array( - '/([^\!\?])\.\./', - '/([a-zа-яё0-9])(\!|\.)(\!|\.|\?)(\s|$|\<)/ui', - '/([a-zа-яё0-9])(\?)(\?)(\s|$|\<)/ui', - ), - 'replacement' => array( - '\1.', - '\1\2\4', - '\1\2\4' - ), - ), - 'fix_brackets' => array( - 'description' => 'Лишние пробелы после открывающей скобочки и перед закрывающей', - 'pattern' => array('/(\()(\040|\t)+/', '/(\040|\t)+(\))/'), - 'replacement' => array('\1', '\2') - ), - 'fix_brackets_space' => array( - 'description' => 'Пробел перед открывающей скобочкой', - 'pattern' => '/([a-zа-яё])(\()/iu', - 'replacement' => '\1 \2' - ), - 'dot_on_end' => array( - 'description' => 'Точка в конце текста, если её там нет', - 'disabled' => true, - 'pattern' => '/([a-zа-яё0-9])(\040|\t|\ \;)*$/ui', - //'pattern' => '/(([^\.\!\?])|(&(ra|ld)quo;))$/', - 'replacement' => '\1.' - ), - - ); -} - - -/** - * @see EMT_Tret - */ - -class EMT_Tret_Quote extends EMT_Tret -{ - /** - * Базовые параметры тофа - * - * @var array - */ - public $title = "Кавычки"; - - - public $rules = array( - 'quotes_outside_a' => array( - 'description' => 'Кавычки вне тэга ', - //'pattern' => '/(\<%%\_\_.+?\>)\"(.+?)\"(\<\/%%\_\_.+?\>)/s', - 'pattern' => '/(\<%%\_\_[^\>]+\>)\"(.+?)\"(\<\/%%\_\_[^\>]+\>)/s', - 'replacement' => '"\1\2\3"' - ), - - 'open_quote' => array( - 'description' => 'Открывающая кавычка', - 'pattern' => '/(^|\(|\s|\>|-)((\"|\\\")+)(\S+)/iue', - 'replacement' => '$m[1] . str_repeat(self::QUOTE_FIRS_OPEN, substr_count($m[2],"\"") ) . $m[4]' - ), - 'close_quote' => array( - 'description' => 'Закрывающая кавычка', - 'pattern' => '/([a-zа-яё0-9]|\.|\&hellip\;|\!|\?|\>|\)|\:|\+|\%|\@|\#|\$|\*)((\"|\\\")+)(\.|\&hellip\;|\;|\:|\?|\!|\,|\s|\)|\<\/|\<|$)/uie', - 'replacement' => '$m[1] . str_repeat(self::QUOTE_FIRS_CLOSE, substr_count($m[2],"\"") ) . $m[4]' - ), - 'close_quote_adv' => array( - 'description' => 'Закрывающая кавычка особые случаи', - //'pattern' => '/([a-zа-яё0-9]|\.|\&hellip\;|\!|\?|\>|\)|\:)((\"|\\\"|\«\;)+)(\<.+?\>)(\.|\&hellip\;|\;|\:|\?|\!|\,|\s|\)|\<\/|$)/uie', - 'pattern' => - array( - '/([a-zа-яё0-9]|\.|\&hellip\;|\!|\?|\>|\)|\:|\+|\%|\@|\#|\$|\*)((\"|\\\"|\«\;)+)(\<[^\>]+\>)(\.|\&hellip\;|\;|\:|\?|\!|\,|\)|\<\/|$| )/uie', - '/([a-zа-яё0-9]|\.|\&hellip\;|\!|\?|\>|\)|\:|\+|\%|\@|\#|\$|\*)(\s+)((\"|\\\")+)(\s+)(\.|\&hellip\;|\;|\:|\?|\!|\,|\)|\<\/|$| )/uie', - '/\>(\«\;)\.($|\s|\<)/ui', - '/\>(\«\;),($|\s|\<|\S)/ui', - '/\>(\«\;):($|\s|\<|\S)/ui', - '/\>(\«\;);($|\s|\<|\S)/ui', - '/\>(\«\;)\)($|\s|\<|\S)/ui', - '/((\"|\\\")+)$/uie', - ), - 'replacement' => - array( - '$m[1] . str_repeat(self::QUOTE_FIRS_CLOSE, substr_count($m[2],"\"")+substr_count($m[2],"«") ) . $m[4]. $m[5]', - '$m[1] .$m[2]. str_repeat(self::QUOTE_FIRS_CLOSE, substr_count($m[3],"\"")+substr_count($m[3],"«") ) . $m[5]. $m[6]', - '>».\2', - '>»,\2', - '>»:\2', - '>»;\2', - '>»)\2', - 'str_repeat(self::QUOTE_FIRS_CLOSE, substr_count($m[1],"\"") )', - ), - ), - 'open_quote_adv' => array( - 'description' => 'Открывающая кавычка особые случаи', - 'pattern' => '/(^|\(|\s|\>)(\"|\\\")(\s)(\S+)/iue', - 'replacement' => '$m[1] . self::QUOTE_FIRS_OPEN .$m[4]' - ), - 'close_quote_adv_2' => array( - 'description' => 'Закрывающая кавычка последний шанс', - 'pattern' => '/(\S)((\"|\\\")+)(\.|\&hellip\;|\;|\:|\?|\!|\,|\s|\)|\<\/|\<|$)/uie', - 'replacement' => '$m[1] . str_repeat(self::QUOTE_FIRS_CLOSE, substr_count($m[2],"\"") ) . $m[4]' - ), - 'quotation' => array( - 'description' => 'Внутренние кавычки-лапки и дюймы', - 'function' => 'build_sub_quotations' - ), - ); - - protected function inject_in($pos, $text, &$thetext) - { - for($i=0;$i_text, "")!==false ? "" : (strpos($this->_text,"\r\n")!==false ? "\r\n\r\n" :"\n\n"); - - $texts_in = explode($exp, $this->_text); - $texts_out = array(); - - foreach($texts_in as $textx) { - - $okposstack = array('0'); - $okpos = 0; - $level = 0; - $off = 0; - while(true) - { - $p = EMT_Lib::strpos_ex($textx, array("«", "»"), $off); - if($p===false) break; - if($p['str'] == "«") - { - if($level>0) if(!$this->is_on('no_bdquotes')) $this->inject_in($p['pos'], self::QUOTE_CRAWSE_OPEN, $textx); - $level++; - } - if($p['str'] == "»") - { - $level--; - if($level>0) if(!$this->is_on('no_bdquotes')) $this->inject_in($p['pos'], self::QUOTE_CRAWSE_CLOSE, $textx); - } - $off = $p['pos']+strlen($p['str']); - if($level == 0) - { - $okpos = $off; - array_push($okposstack, $okpos); - } elseif($level<0) // уровень стал меньше нуля - { - if(!$this->is_on('no_inches')) - { - do{ - $lokpos = array_pop($okposstack); - $k = substr($textx, $lokpos, $off-$lokpos); - $k = str_replace(self::QUOTE_CRAWSE_OPEN, self::QUOTE_FIRS_OPEN, $k); - $k = str_replace(self::QUOTE_CRAWSE_CLOSE, self::QUOTE_FIRS_CLOSE, $k); - //$k = preg_replace("/(^|[^0-9])([0-9]+)\»\;/ui", '\1\2″', $k, 1, $amount); - - $amount = 0; - $__ax = preg_match_all("/(^|[^0-9])([0-9]+)\»\;/ui", $k, $m); - $__ay = 0; - if($__ax) - { - $k = preg_replace_callback("/(^|[^0-9])([0-9]+)\»\;/ui", - create_function('$m','global $__ax,$__ay; $__ay++; if($__ay==$__ax){ return $m[1].$m[2]."″";} return $m[0];'), - $k); - $amount = 1; - } - - - - } while(($amount==0) && count($okposstack)); - - // успешно сделали замену - if($amount == 1) - { - // заново просмотрим содержимое - $textx = substr($textx, 0, $lokpos). $k . substr($textx, $off); - $off = $lokpos; - $level = 0; - continue; - } - - // иначе просто заменим последнюю явно на " от отчаяния - if($amount == 0) - { - // говорим, что всё в порядке - $level = 0; - $textx = substr($textx, 0, $p['pos']). '"' . substr($textx, $off); - $off = $p['pos'] + strlen('"'); - $okposstack = array($off); - continue; - } - } - } - - - } - // не совпало количество, отменяем все подкавычки - if($level != 0 ){ - - // закрывающих меньше, чем надо - if($level>0) - { - $k = substr($textx, $okpos); - $k = str_replace(self::QUOTE_CRAWSE_OPEN, self::QUOTE_FIRS_OPEN, $k); - $k = str_replace(self::QUOTE_CRAWSE_CLOSE, self::QUOTE_FIRS_CLOSE, $k); - $textx = substr($textx, 0, $okpos). $k; - } - } - $texts_out[] = $textx; - } - $this->_text = implode($exp, $texts_out); - } - -} - - - - - - - -/** - * @see EMT_Tret - */ - -class EMT_Tret_Space extends EMT_Tret -{ - public $title = "Расстановка и удаление пробелов"; - - public $domain_zones = array('ru','ру','ком','орг', 'уа', 'ua', 'uk', 'co', 'fr', - 'com', 'net', 'edu', 'gov', 'org', 'mil', 'int', 'info', 'biz', 'info', 'name', 'pro'); - - public $classes = array( - 'nowrap' => 'word-spacing:nowrap;', - ); - - public $rules = array( - 'nobr_twosym_abbr' => array( - 'description' => 'Неразрывный перед 2х символьной аббревиатурой', - 'pattern' => '/([a-zA-Zа-яёА-ЯЁ])(\040|\t)+([A-ZА-ЯЁ]{2})([\s\;\.\?\!\:\(\"]|\&(ra|ld)quo\;|$)/u', - 'replacement' => '\1 \3\4' - ), - 'remove_space_before_punctuationmarks' => array( - 'description' => 'Удаление пробела перед точкой, запятой, двоеточием, точкой с запятой', - 'pattern' => '/((\040|\t|\ \;)+)([\,\:\.\;\?])(\s+|$)/', - 'replacement' => '\3\4' - ), - 'autospace_after_comma' => array( - 'description' => 'Пробел после запятой', - 'pattern' => array( - '/(\040|\t|\ \;)\,([а-яёa-z0-9])/iu', - '/([^0-9])\,([а-яёa-z0-9])/iu', - ), - 'replacement' => array( - ', \2', - '\1, \2' - ), - ), - 'autospace_after_pmarks' => array( - 'description' => 'Пробел после знаков пунктуации, кроме точки', - 'pattern' => '/(\040|\t|\ \;|^|\n)([a-zа-яё0-9]+)(\040|\t|\ \;)?(\:|\)|\,|\&hellip\;|(?:\!|\?)+)([а-яёa-z])/iu', - 'replacement' => '\1\2\4 \5' - ), - 'autospace_after_dot' => array( - 'description' => 'Пробел после точки', - 'pattern' => array( - '/(\040|\t|\ \;|^)([a-zа-яё0-9]+)(\040|\t|\ \;)?\.([а-яёa-z]{5,})($|[^a-zа-яё])/iue', - '/(\040|\t|\ \;|^)([a-zа-яё0-9]+)\.([а-яёa-z]{1,4})($|[^a-zа-яё])/iue', - ), - 'replacement' => array( - //'\1\2. \4', - '$m[1].$m[2]."." .( $m[5] == "." ? "" : " ").$m[4].$m[5]', - '$m[1].$m[2]."." .(in_array(EMT_Lib::strtolower($m[3]), $this->domain_zones)? "":( $m[4] == "." ? "" : " ")). $m[3].$m[4]' - ), - ), - 'autospace_after_hellips' => array( - 'description' => 'Пробел после знаков троеточий с вопросительным или восклицательными знаками', - 'pattern' => '/([\?\!]\.\.)([а-яёa-z])/iu', - 'replacement' => '\1 \2' - ), - 'many_spaces_to_one' => array( - 'description' => 'Удаление лишних пробельных символов и табуляций', - 'pattern' => '/(\040|\t)+/', - 'replacement' => ' ' - ), - 'clear_percent' => array( - 'description' => 'Удаление пробела перед символом процента', - 'pattern' => '/(\d+)([\t\040]+)\%/', - 'replacement' => '\1%' - ), - 'nbsp_before_open_quote' => array( - 'description' => 'Неразрывный пробел перед открывающей скобкой', - 'pattern' => '/(^|\040|\t|>)([a-zа-яё]{1,2})\040(\«\;|\&bdquo\;)/u', - 'replacement' => '\1\2 \3' - ), - - 'nbsp_before_month' => array( - 'description' => 'Неразрывный пробел в датах перед числом и месяцем', - 'pattern' => '/(\d)(\s)+(января|февраля|марта|апреля|мая|июня|июля|августа|сентября|октября|ноября|декабря)([^\<]|$)/iu', - 'replacement' => '\1 \3\4' - ), - 'spaces_on_end' => array( - 'description' => 'Удаление пробелов в конце текста', - 'pattern' => '/ +$/', - 'replacement' => '' - ), - 'no_space_posle_hellip' => array( - 'description' => 'Отсутстввие пробела после троеточия после открывающей кавычки', - 'pattern' => '/(\«\;|\&bdquo\;)( |\ \;)?\&hellip\;( |\ \;)?([a-zа-яё])/ui', - 'replacement' => '\1…\4' - ), - 'space_posle_goda' => array( - 'description' => 'Пробел после года', - 'pattern' => '/(^|\040|\ \;)([0-9]{3,4})(год([ауе]|ом)?)([^a-zа-яё]|$)/ui', - 'replacement' => '\1\2 \3\5' - ), - ); -} - - -/** - * @see EMT_Tret - */ - -class EMT_Tret_Symbol extends EMT_Tret -{ - /** - * Базовые параметры тофа - * - * @var array - */ - public $classes = array( - 'nowrap' => 'word-spacing:nowrap;', - ); - - - public $title = "Специальные символы"; - public $rules = array( - 'tm_replace' => array( - 'description' => 'Замена (tm) на символ торговой марки', - 'pattern' => '/([\040\t])?\(tm\)/i', - 'replacement' => '™' - ), - 'r_sign_replace' => array( - 'description' => 'Замена (R) на символ зарегистрированной торговой марки', - 'pattern' => array( - '/(.|^)\(r\)(.|$)/ie', - //'/([^\>]|^)\(r\)([^\<]|$)/ie', - //'/\>\(r\)\ array( - //'$m[1].$this->tag("®", "sup").$m[2]', - '$m[1]."®".$m[2]', - //'>®<' - ), - ), - 'copy_replace' => array( - 'description' => 'Замена (c) на символ копирайт', - 'pattern' => array( - '/\((c|с)\)\s+/iu', - '/\((c|с)\)($|\.|,|!|\?)/iu', - ), - 'replacement' => array( - '© ', - '©\2', - ), - ), - 'apostrophe' => array( - 'description' => 'Расстановка правильного апострофа в текстах', - 'pattern' => '/(\s|^|\>|\&rsquo\;)([a-zа-яё]{1,})\'([a-zа-яё]+)/ui', - 'replacement' => '\1\2’\3', - 'cycled' => true - ), - /* - 'ru_apostrophe' => array( - 'description' => 'Расстановка правильного апострофа в русских текстах', - 'pattern' => '/(\s|^|\>)([а-яё]+)\'([а-яё]+)/iu', - 'replacement' => '\1\2’\3' - ), - */ - 'degree_f' => array( - 'description' => 'Градусы по Фаренгейту', - 'pattern' => '/([0-9]+)F($|\s|\.|\,|\;|\:|\ \;|\?|\!)/eu', - 'replacement' => '"".$this->tag($m[1]." °F","span", array("class"=>"nowrap")) .$m[2]' - ), - 'euro_symbol' => array( - 'description' => 'Символ евро', - 'simple_replace' => true, - 'pattern' => '€', - 'replacement' => '€' - ), - 'arrows_symbols' => array( - 'description' => 'Замена стрелок вправо-влево на html коды', - //'pattern' => array('/(\s|\>|\ \;|^)\-\>($|\s|\ \;|\<)/', '/(\s|\>|\ \;|^|;)\<\-(\s|\ \;|$|\<)/', '/→/u', '/←/u'), - //'pattern' => array('/\-\>($|\s|\ \;|\<)/', '/(\s|\>|\ \;|^|;)\<\-(\s|\ \;|$|\<)/', '/→/u', '/←/u'), - 'pattern' => array('/\-\>/', '/\<\-/', '/→/u', '/←/u'), - //'replacement' => array('\1→\2', '\1←\2', '→', '←' ), - 'replacement' => array('→', '←', '→', '←' ), - ), - ); -} - - -/** - * @see EMT_Tret - */ - -class EMT_Tret_Text extends EMT_Tret -{ - public $classes = array( - 'nowrap' => 'word-spacing:nowrap;', - ); - - /** - * Базовые параметры тофа - * - * @var array - */ - public $title = "Текст и абзацы"; - public $rules = array( - 'auto_links' => array( - 'description' => 'Выделение ссылок из текста', - 'pattern' => '/(\s|^)(http|ftp|mailto|https)(:\/\/)([^\s\,\!\<]{4,})(\s|\.|\,|\!|\?|\<|$)/ieu', - '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]' - ), - 'email' => array( - 'description' => 'Выделение эл. почты из текста', - 'pattern' => '/(\s|^|\ \;|\()([a-z0-9\-\_\.]{2,})\@([a-z0-9\-\.]{2,})\.([a-z]{2,6})(\)|\s|\.|\,|\!|\?|$|\<)/e', - 'replacement' => '$m[1] . $this->tag($m[2]."@".$m[3].".".$m[4], "a", array("href" => "mailto:".$m[2]."@".$m[3].".".$m[4])) . $m[5]' - ), - 'no_repeat_words' => array( - 'description' => 'Удаление повторяющихся слов', - 'disabled' => true, - 'pattern' => array( - '/([а-яё]{3,})( |\t|\ \;)\1/iu', - '/(\s|\ \;|^|\.|\!|\?)(([А-ЯЁ])([а-яё]{2,}))( |\t|\ \;)(([а-яё])\4)/eu', - ), - 'replacement' => array( - '\1', - '$m[1].($m[7] === EMT_Lib::strtolower($m[3]) ? $m[2] : $m[2].$m[5].$m[6] )', - ) - ), - 'paragraphs' => array( - 'description' => 'Простановка параграфов', - 'function' => 'build_paragraphs' - ), - 'breakline' => array( - 'description' => 'Простановка переносов строк', - 'function' => 'build_brs' - ), - - ); - - /** - * Расстановка защищенных тегов параграфа (

...

) и переноса строки - * - * @return void - */ - protected function do_paragraphs($text) { - $text = str_replace("\r\n","\n",$text); - $text = str_replace("\r","\n",$text); - $text = '<' . self::BASE64_PARAGRAPH_TAG . '>' . trim($text) . ''; - //$text = $this->preg_replace_e('/([\040\t]+)?(\n|\r){2,}/e', '"<" .self::BASE64_PARAGRAPH_TAG . ">"', $text); - //$text = $this->preg_replace_e('/([\040\t]+)?(\n){2,}/e', '"<" .self::BASE64_PARAGRAPH_TAG . ">"', $text); - $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); - //$text = $this->preg_replace_e('/([\040\t]+)?(\n)+([\040\t]*)(\n)+/e', '""."<" .self::BASE64_PARAGRAPH_TAG . ">"', $text); - //может от открвающего до закрывающего ?! - $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); - return $text; - } - - /** - * Расстановка защищенных тегов параграфа (

...

) и переноса строки - * - * @return void - */ - protected function build_paragraphs() - { - $r = mb_strpos($this->_text, '<' . self::BASE64_PARAGRAPH_TAG . '>' ); - $p = EMT_Lib::rstrpos($this->_text, '' ) ; - if(($r!== false) && ($p !== false)) { - - $beg = mb_substr($this->_text,0,$r); - $end = mb_substr($this->_text,$p+mb_strlen('')); - $this->_text = - (trim($beg) ? $this->do_paragraphs($beg). "\n":"") .'<' . self::BASE64_PARAGRAPH_TAG . '>'. - mb_substr($this->_text,$r + mb_strlen('<' . self::BASE64_PARAGRAPH_TAG . '>'),$p -($r + mb_strlen('<' . self::BASE64_PARAGRAPH_TAG . '>')) ).''. - (trim($end) ? "\n".$this->do_paragraphs($end) :"") ; - } else { - $this->_text = $this->do_paragraphs($this->_text); - } - } - - /** - * Расстановка защищенных тегов параграфа (

...

) и переноса строки - * - * @return void - */ - protected function build_brs() - { - $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); - - if (!preg_match('/\<' . self::BASE64_BREAKLINE_TAG . '\>/', $this->_text)) { - $this->_text = str_replace("\r\n","\n",$this->_text); - $this->_text = str_replace("\r","\n",$this->_text); - //$this->_text = $this->preg_replace_e('/(\n|\r)/e', '"<" . self::BASE64_BREAKLINE_TAG . ">"', $this->_text); - $this->_text = $this->preg_replace_e('/(\n)/e', '"<" . self::BASE64_BREAKLINE_TAG . ">\n"', $this->_text); - } - } -} - - -/** -* Evgeny Muravjev Typograph, http://mdash.ru -* Version: 3.5 Gold Master -* Release Date: July 2, 2015 -* Authors: Evgeny Muravjev & Alexander Drutsa -*/ - - - -/** - * Основной класс типографа Евгения Муравьёва - * реализует основные методы запуска и работы типографа - * - */ -class EMT_Base -{ - private $_text = ""; - private $inited = false; - - /** - * Список Трэтов, которые надо применить к типографированию - * - * @var array - */ - protected $trets = array() ; - protected $trets_index = array() ; - protected $tret_objects = array() ; - - public $ok = false; - public $debug_enabled = false; - public $logging = false; - public $logs = array(); - public $errors = array(); - public $debug_info = array(); - - private $use_layout = false; - private $class_layout_prefix = false; - private $use_layout_set = false; - public $disable_notg_replace = false; - public $remove_notg = false; - - public $settings = array(); - - protected function log($str, $data = null) - { - if(!$this->logging) return; - $this->logs[] = array('class' => '', 'info' => $str, 'data' => $data); - } - - protected function tret_log($tret, $str, $data = null) - { - $this->logs[] = array('class' => $tret, 'info' => $str, 'data' => $data); - } - - protected function error($info, $data = null) - { - $this->errors[] = array('class' => '', 'info' => $info, 'data' => $data); - $this->log("ERROR $info", $data ); - } - - protected function tret_error($tret, $info, $data = null) - { - $this->errors[] = array('class' => $tret, 'info' => $info, 'data' => $data); - } - - protected function debug($class, $place, &$after_text, $after_text_raw = "") - { - if(!$this->debug_enabled) return; - $this->debug_info[] = array( - 'tret' => $class == $this ? false: true, - 'class' => is_object($class)? get_class($class) : $class, - 'place' => $place, - 'text' => $after_text, - 'text_raw' => $after_text_raw, - ); - } - - - - protected $_safe_blocks = array(); - - - /** - * Включить режим отладки, чтобы посмотреть последовательность вызовов - * третов и правил после - * - */ - public function debug_on() - { - $this->debug_enabled = true; - } - - /** - * Включить режим отладки, чтобы посмотреть последовательность вызовов - * третов и правил после - * - */ - public function log_on() - { - $this->logging = true; - } - - /** - * Добавление защищенного блока - * - * - * Jare_Typograph_Tool::addCustomBlocks('', ''); - * Jare_Typograph_Tool::addCustomBlocks('\', '\<\/span\>', true); - * - * - * @param string $id идентификатор - * @param string $open начало блока - * @param string $close конец защищенного блока - * @param string $tag тэг - * @return void - */ - private function _add_safe_block($id, $open, $close, $tag) - { - $this->_safe_blocks[] = array( - 'id' => $id, - 'tag' => $tag, - 'open' => $open, - 'close' => $close, - ); - } - - /** - * Список защищенных блоков - * - * @return array - */ - public function get_all_safe_blocks() - { - return $this->_safe_blocks; - } - - /** - * Удаленного блока по его номеру ключа - * - * @param string $id идентифиактор защищённого блока - * @return void - */ - public function remove_safe_block($id) - { - foreach($this->_safe_blocks as $k => $block) { - if($block['id']==$id) unset($this->_safe_blocks[$k]); - } - } - - - /** - * Добавление защищенного блока - * - * @param string $tag тэг, который должен быть защищён - * @return void - */ - public function add_safe_tag($tag) - { - $open = preg_quote("<", '/'). $tag."[^>]*?" . preg_quote(">", '/'); - $close = preg_quote("", '/'); - $this->_add_safe_block($tag, $open, $close, $tag); - return true; - } - - - /** - * Добавление защищенного блока - * - * @param string $open начало блока - * @param string $close конец защищенного блока - * @param bool $quoted специальные символы в начале и конце блока экранированы - * @return void - */ - public function add_safe_block($id, $open, $close, $quoted = false) - { - $open = trim($open); - $close = trim($close); - - if (empty($open) || empty($close)) - { - return false; - } - - if (false === $quoted) - { - $open = preg_quote($open, '/'); - $close = preg_quote($close, '/'); - } - - $this->_add_safe_block($id, $open, $close, ""); - return true; - } - - - /** - * Сохранение содержимого защищенных блоков - * - * @param string $text - * @param bool $safe если true, то содержимое блоков будет сохранено, иначе - раскодировано. - * @return string - */ - public function safe_blocks($text, $way, $show = true) - { - if (count($this->_safe_blocks)) - { - $safeType = true === $way ? "EMT_Lib::encrypt_tag(\$m[2])" : "stripslashes(EMT_Lib::decrypt_tag(\$m[2]))"; - $safeblocks = true === $way ? $this->_safe_blocks : array_reverse($this->_safe_blocks); - foreach ($safeblocks as $block) - { - $text = preg_replace_callback("/({$block['open']})(.+?)({$block['close']})/s", create_function('$m','return $m[1].'.$safeType . '.$m[3];') , $text); - } - } - - return $text; - } - - - /** - * Декодирование блоков, которые были скрыты в момент типографирования - * - * @param string $text - * @return string - */ - public function decode_internal_blocks($text) - { - return EMT_Lib::decode_internal_blocks($text); - } - - - private function create_object($tret) - { - // если класса нету, попытаемся его прогрузить, например, если стандартный - if(!class_exists($tret)) - { - if(preg_match("/^EMT_Tret_([a-zA-Z0-9_]+)$/",$tret, $m)) - { - $tname = $m[1]; - $fname = str_replace("_"," ",$tname); - $fname = ucwords($fname); - $fname = str_replace(" ",".",$fname); - //if(file_exists("EMT.Tret.".$fname.".php")) - { - } - } - } - if(!class_exists($tret)) - { - $this->error("Класс $tret не найден. Пожалуйста, подргузите нужный файл."); - return null; - } - - $obj = new $tret(); - $obj->EMT = $this; - $obj->logging = $this->logging; - return $obj; - } - - private function get_short_tret($tretname) - { - if(preg_match("/^EMT_Tret_([a-zA-Z0-9_]+)$/",$tretname, $m)) - { - return $m[1]; - } - return $tretname; - } - - private function _init() - { - foreach($this->trets as $tret) - { - if(isset($this->tret_objects[$tret])) continue; - $obj = $this->create_object($tret); - if($obj == null) continue; - $this->tret_objects[$tret] = $obj; - } - - if(!$this->inited) - { - $this->add_safe_tag('pre'); - $this->add_safe_tag('script'); - $this->add_safe_tag('style'); - $this->add_safe_tag('notg'); - $this->add_safe_block('span-notg', '', ''); - } - $this->inited = true; - } - - - - - - /** - * Инициализация класса, используется чтобы задать список третов или - * список защищённых блоков, которые можно использовать. - * Также здесь можно отменить защищённые блоки по умлочнаию - * - */ - public function init() - { - - } - - /** - * Добавить Трэт, - * - * @param mixed $class - имя класса трета, или сам объект - * @param string $altname - альтернативное имя, если хотим например иметь два одинаоковых терта в обработке - * @return unknown - */ - public function add_tret($class, $altname = false) - { - if(is_object($class)) - { - if(!is_a($class, "EMT_Tret")) - { - $this->error("You are adding Tret that doesn't inherit base class EMT_Tret", get_class($class)); - return false; - } - - $class->EMT = $this; - $class->logging = $this->logging; - $this->tret_objects[($altname ? $altname : get_class($class))] = $class; - $this->trets[] = ($altname ? $altname : get_class($class)); - return true; - } - if(is_string($class)) - { - $obj = $this->create_object($class); - if($obj === null) - return false; - $this->tret_objects[($altname ? $altname : $class)] = $obj; - $this->trets[] = ($altname ? $altname : $class); - return true; - } - $this->error("Чтобы добавить трэт необходимо передать имя или объект"); - return false; - } - - /** - * Получаем ТРЕТ по идентификатору, т.е. названию класса - * - * @param unknown_type $name - */ - public function get_tret($name) - { - if(isset($this->tret_objects[$name])) return $this->tret_objects[$name]; - foreach($this->trets as $tret) - { - if($tret == $name) - { - $this->_init(); - return $this->tret_objects[$name]; - } - if($this->get_short_tret($tret) == $name) - { - $this->_init(); - return $this->tret_objects[$tret]; - } - } - $this->error("Трэт с идентификатором $name не найден"); - return false; - } - - /** - * Задаём текст для применения типографа - * - * @param string $text - */ - public function set_text($text) - { - $this->_text = $text; - } - - - - /** - * Запустить типограф на выполнение - * - */ - public function apply($trets = null) - { - $this->ok = false; - - $this->init(); - $this->_init(); - - $atrets = $this->trets; - if(is_string($trets)) $atrets = array($trets); - elseif(is_array($trets)) $atrets = $trets; - - $this->debug($this, 'init', $this->_text); - - $this->_text = $this->safe_blocks($this->_text, true); - $this->debug($this, 'safe_blocks', $this->_text); - - $this->_text = EMT_Lib::safe_tag_chars($this->_text, true); - $this->debug($this, 'safe_tag_chars', $this->_text); - - $this->_text = EMT_Lib::clear_special_chars($this->_text); - $this->debug($this, 'clear_special_chars', $this->_text); - - foreach ($atrets as $tret) - { - // если установлен режим разметки тэгов то выставим его - if($this->use_layout_set) - $this->tret_objects[$tret]->set_tag_layout_ifnotset($this->use_layout); - - if($this->class_layout_prefix) - $this->tret_objects[$tret]->set_class_layout_prefix($this->class_layout_prefix); - - // влючаем, если нужно - if($this->debug_enabled) $this->tret_objects[$tret]->debug_on(); - if($this->logging) $this->tret_objects[$tret]->logging = true; - - // применяем трэт - //$this->tret_objects[$tret]->set_text(&$this->_text); - $this->tret_objects[$tret]->set_text($this->_text); - $this->tret_objects[$tret]->apply(); - - // соберём ошибки если таковые есть - if(count($this->tret_objects[$tret]->errors)>0) - foreach($this->tret_objects[$tret]->errors as $err ) - $this->tret_error($tret, $err['info'], $err['data']); - - // логгирование - if($this->logging) - if(count($this->tret_objects[$tret]->logs)>0) - foreach($this->tret_objects[$tret]->logs as $log ) - $this->tret_log($tret, $log['info'], $log['data']); - - // отладка - if($this->debug_enabled) - foreach($this->tret_objects[$tret]->debug_info as $di) - { - $unsafetext = $di['text']; - $unsafetext = EMT_Lib::safe_tag_chars($unsafetext, false); - $unsafetext = $this->safe_blocks($unsafetext, false); - $this->debug($tret, $di['place'], $unsafetext, $di['text']); - } - - - } - - - $this->_text = $this->decode_internal_blocks($this->_text); - $this->debug($this, 'decode_internal_blocks', $this->_text); - - if($this->is_on('dounicode')) - { - EMT_Lib::convert_html_entities_to_unicode($this->_text); - } - - $this->_text = EMT_Lib::safe_tag_chars($this->_text, false); - $this->debug($this, 'unsafe_tag_chars', $this->_text); - - $this->_text = $this->safe_blocks($this->_text, false); - $this->debug($this, 'unsafe_blocks', $this->_text); - - if(!$this->disable_notg_replace) - { - $repl = array('', ''); - if($this->remove_notg) $repl = ""; - $this->_text = str_replace( array('',''), $repl , $this->_text); - } - $this->_text = trim($this->_text); - $this->ok = (count($this->errors)==0); - return $this->_text; - } - - /** - * Получить содержимое при использовании классов - * - * @param bool $list false - вернуть в виде строки для style или как массив - * @param bool $compact не выводить пустые классы - * @return string|array - */ - public function get_style($list = false, $compact = false) - { - $this->_init(); - - $res = array(); - foreach ($this->trets as $tret) - { - $arr =$this->tret_objects[$tret]->classes; - if(!is_array($arr)) continue; - foreach($arr as $classname => $str) - { - if(($compact) && (!$str)) continue; - $clsname = ($this->class_layout_prefix ? $this->class_layout_prefix : "" ).(isset($this->tret_objects[$tret]->class_names[$classname]) ? $this->tret_objects[$tret]->class_names[$classname] :$classname); - $res[$clsname] = $str; - } - } - if($list) return $res; - $str = ""; - foreach($res as $k => $v) - { - $str .= ".$k { $v }\n"; - } - return $str; - } - - - - - - /** - * Установить режим разметки, - * EMT_Lib::LAYOUT_STYLE - с помощью стилей - * EMT_Lib::LAYOUT_CLASS - с помощью классов - * EMT_Lib::LAYOUT_STYLE|EMT_Lib::LAYOUT_CLASS - оба метода - * - * @param int $layout - */ - public function set_tag_layout($layout = EMT_Lib::LAYOUT_STYLE) - { - $this->use_layout = $layout; - $this->use_layout_set = true; - } - - /** - * Установить префикс для классов - * - * @param string|bool $prefix если true то префикс 'emt_', иначе то, что передали - */ - public function set_class_layout_prefix($prefix ) - { - $this->class_layout_prefix = $prefix === true ? "emt_" : $prefix; - } - - /** - * Включить/отключить правила, согласно карте - * Формат карты: - * 'Название трэта 1' => array ( 'правило1', 'правило2' , ... ) - * 'Название трэта 2' => array ( 'правило1', 'правило2' , ... ) - * - * @param array $map - * @param boolean $disable если ложно, то $map соотвествует тем правилам, которые надо включить - * иначе это список правил, которые надо выключить - * @param boolean $strict строго, т.е. те которые не в списке будут тоже обработаны - */ - public function set_enable_map($map, $disable = false, $strict = true) - { - if(!is_array($map)) return; - $trets = array(); - foreach($map as $tret => $list) - { - $tretx = $this->get_tret($tret); - if(!$tretx) - { - $this->log("Трэт $tret не найден при применении карты включаемых правил"); - continue; - } - $trets[] = $tretx; - - if($list === true) // все - { - $tretx->activate(array(), !$disable , true); - } elseif(is_string($list)) { - $tretx->activate(array($list), $disable , $strict); - } elseif(is_array($list)) { - $tretx->activate($list, $disable , $strict); - } - } - if($strict) - { - foreach($this->trets as $tret) - { - if(in_array($this->tret_objects[$tret], $trets)) continue; - $this->tret_objects[$tret]->activate(array(), $disable , true); - } - } - - } - - - /** - * Установлена ли настройка - * - * @param string $key - */ - public function is_on($key) - { - if(!isset($this->settings[$key])) return false; - $kk = $this->settings[$key]; - return ((strtolower($kk)=="on") || ($kk === "1") || ($kk === true) || ($kk === 1)); - } - - - /** - * Установить настройку - * - * @param mixed $selector - * @param string $setting - * @param mixed $value - */ - protected function doset($selector, $key, $value) - { - $tret_pattern = false; - $rule_pattern = false; - //if(($selector === false) || ($selector === null) || ($selector === false) || ($selector === "*")) $type = 0; - if(is_string($selector)) - { - if(strpos($selector,".")===false) - { - $tret_pattern = $selector; - } else { - $pa = explode(".", $selector); - $tret_pattern = $pa[0]; - array_shift($pa); - $rule_pattern = implode(".", $pa); - } - } - EMT_Lib::_process_selector_pattern($tret_pattern); - EMT_Lib::_process_selector_pattern($rule_pattern); - if($selector == "*") $this->settings[$key] = $value; - - foreach ($this->trets as $tret) - { - $t1 = $this->get_short_tret($tret); - if(!EMT_Lib::_test_pattern($tret_pattern, $t1)) if(!EMT_Lib::_test_pattern($tret_pattern, $tret)) continue; - $tret_obj = $this->get_tret($tret); - if($key == "active") - { - foreach($tret_obj->rules as $rulename => $v) - { - if(!EMT_Lib::_test_pattern($rule_pattern, $rulename)) continue; - if((strtolower($value) === "on") || ($value===1) || ($value === true) || ($value=="1")) $tret_obj->enable_rule($rulename); - if((strtolower($value) === "off") || ($value===0) || ($value === false) || ($value=="0")) $tret_obj->disable_rule($rulename); - } - } else { - if($rule_pattern===false) - { - $tret_obj->set($key, $value); - } else { - foreach($tret_obj->rules as $rulename => $v) - { - if(!EMT_Lib::_test_pattern($rule_pattern, $rulename)) continue; - $tret_obj->set_rule($rulename, $key, $value); - } - } - } - } - } - - - /** - * Установить настройки для тертов и правил - * 1. если селектор является массивом, то тогда установка правил будет выполнена для каждого - * элемента этого массива, как отдельного селектора. - * 2. Если $key не является массивом, то эта настройка будет проставлена согласно селектору - * 3. Если $key массив - то будет задана группа настроек - * - если $value массив , то настройки определяются по ключам из массива $key, а значения из $value - * - иначе, $key содержит ключ-значение как массив - * 4. $exact_match - если true тогда array selector будет соответсвовать array $key, а не произведению массивов - * - * @param mixed $selector - * @param mixed $key - * @param mixed $value - * @param mixed $exact_match - */ - public function set($selector, $key , $value = false, $exact_match = false) - { - if($exact_match && is_array($selector) && is_array($key) && count($selector)==count($key)) { - $idx = 0; - foreach($key as $x => $y){ - if(is_array($value)) - { - $kk = $y; - $vv = $value[$x]; - } else { - $kk = ( $value ? $y : $x ); - $vv = ( $value ? $value : $y ); - } - $this->set($selector[$idx], $kk , $vv); - $idx++; - } - return ; - } - if(is_array($selector)) - { - foreach($selector as $val) $this->set($val, $key, $value); - return; - } - if(is_array($key)) - { - foreach($key as $x => $y) - { - if(is_array($value)) - { - $kk = $y; - $vv = $value[$x]; - } else { - $kk = ( $value ? $y : $x ); - $vv = ( $value ? $value : $y ); - } - $this->set($selector, $kk, $vv); - } - return ; - } - $this->doset($selector, $key, $value); - } - - - /** - * Возвращает список текущих третов, которые установлены - * - */ - public function get_trets_list() - { - return $this->trets; - } - - /** - * Установка одной метанастройки - * - * @param string $name - * @param mixed $value - */ - public function do_setup($name, $value) - { - - } - - - /** - * Установить настройки - * - * @param array $setupmap - */ - public function setup($setupmap) - { - if(!is_array($setupmap)) return; - - if(isset($setupmap['map']) || isset($setupmap['maps'])) - { - if(isset($setupmap['map'])) - { - $ret['map'] = $test['params']['map']; - $ret['disable'] = $test['params']['map_disable']; - $ret['strict'] = $test['params']['map_strict']; - $test['params']['maps'] = array($ret); - unset($setupmap['map']); - unset($setupmap['map_disable']); - unset($setupmap['map_strict']); - } - if(is_array($setupmap['maps'])) - { - foreach($setupmap['maps'] as $map) - { - $this->set_enable_map - ($map['map'], - isset($map['disable']) ? $map['disable'] : false, - isset($map['strict']) ? $map['strict'] : false - ); - } - } - unset($setupmap['maps']); - } - - - foreach($setupmap as $k => $v) $this->do_setup($k , $v); - } - - - - -} - - -class EMTypograph extends EMT_Base -{ - public $trets = array('EMT_Tret_Quote', 'EMT_Tret_Dash', 'EMT_Tret_Symbol', 'EMT_Tret_Punctmark', 'EMT_Tret_Number', 'EMT_Tret_Space', 'EMT_Tret_Abbr', 'EMT_Tret_Nobr', 'EMT_Tret_Date', 'EMT_Tret_OptAlign', 'EMT_Tret_Etc', 'EMT_Tret_Text'); - - - protected $group_list = array( - 'Quote' => true, - 'Dash' => true, - 'Nobr' => true, - 'Symbol' => true, - 'Punctmark' => true, - 'Number' => true, - 'Date' => true, - 'Space' => true, - 'Abbr' => true, - 'OptAlign' => true, - 'Text' => true, - 'Etc' => true, - ); - protected $all_options = array( - - 'Quote.quotes' => array( 'description' => 'Расстановка «кавычек-елочек» первого уровня', 'selector' => "Quote.*quote" ), - 'Quote.quotation' => array( 'description' => 'Внутренние кавычки-лапки', 'selector' => "Quote", 'setting' => 'no_bdquotes', 'reversed' => true ), - - 'Dash.to_libo_nibud' => 'direct', - 'Dash.iz_za_pod' => 'direct', - 'Dash.ka_de_kas' => 'direct', - - 'Nobr.super_nbsp' => 'direct', - 'Nobr.nbsp_in_the_end' => 'direct', - 'Nobr.phone_builder' => 'direct', - 'Nobr.phone_builder_v2' => 'direct', - 'Nobr.ip_address' => 'direct', - 'Nobr.spaces_nobr_in_surname_abbr' => 'direct', - 'Nobr.dots_for_surname_abbr' => 'direct', - 'Nobr.nbsp_celcius' => 'direct', - 'Nobr.hyphen_nowrap_in_small_words' => 'direct', - 'Nobr.hyphen_nowrap' => 'direct', - 'Nobr.nowrap' => array('description' => 'Nobr (по умолчанию) & nowrap', 'disabled' => true, 'selector' => '*', 'setting' => 'nowrap' ), - - 'Symbol.tm_replace' => 'direct', - 'Symbol.r_sign_replace' => 'direct', - 'Symbol.copy_replace' => 'direct', - 'Symbol.apostrophe' => 'direct', - 'Symbol.degree_f' => 'direct', - 'Symbol.arrows_symbols' => 'direct', - 'Symbol.no_inches' => array( 'description' => 'Расстановка дюйма после числа', 'selector' => "Quote", 'setting' => 'no_inches', 'reversed' => true ), - - 'Punctmark.auto_comma' => 'direct', - 'Punctmark.hellip' => 'direct', - 'Punctmark.fix_pmarks' => 'direct', - 'Punctmark.fix_excl_quest_marks' => 'direct', - 'Punctmark.dot_on_end' => 'direct', - - 'Number.minus_between_nums' => 'direct', - 'Number.minus_in_numbers_range' => 'direct', - 'Number.auto_times_x' => 'direct', - 'Number.simple_fraction' => 'direct', - 'Number.math_chars' => 'direct', - 'Number.thinsp_between_number_triads' => 'direct', - 'Number.thinsp_between_no_and_number' => 'direct', - 'Number.thinsp_between_sect_and_number' => 'direct', - - 'Date.years' => 'direct', - 'Date.mdash_month_interval' => 'direct', - 'Date.nbsp_and_dash_month_interval' => 'direct', - 'Date.nobr_year_in_date' => 'direct', - - 'Space.many_spaces_to_one' => 'direct', - 'Space.clear_percent' => 'direct', - 'Space.clear_before_after_punct' => array( 'description' => 'Удаление пробелов перед и после знаков препинания в предложении', 'selector' => 'Space.remove_space_before_punctuationmarks'), - 'Space.autospace_after' => array( 'description' => 'Расстановка пробелов после знаков препинания', 'selector' => 'Space.autospace_after_*'), - 'Space.bracket_fix' => array( 'description' => 'Удаление пробелов внутри скобок, а также расстановка пробела перед скобками', - 'selector' => array('Space.nbsp_before_open_quote', 'Punctmark.fix_brackets')), - - 'Abbr.nbsp_money_abbr' => array( 'description' => 'Форматирование денежных сокращений (расстановка пробелов и привязка названия валюты к числу)', - 'selector' => array('Abbr.nbsp_money_abbr', 'Abbr.nbsp_money_abbr_rev')), - 'Abbr.nobr_vtch_itd_itp' => 'direct', - 'Abbr.nobr_sm_im' => 'direct', - 'Abbr.nobr_acronym' => 'direct', - 'Abbr.nobr_locations' => 'direct', - 'Abbr.nobr_abbreviation' => 'direct', - 'Abbr.ps_pps' => 'direct', - 'Abbr.nbsp_org_abbr' => 'direct', - 'Abbr.nobr_gost' => 'direct', - 'Abbr.nobr_before_unit_volt' => 'direct', - 'Abbr.nbsp_before_unit' => 'direct', - - 'OptAlign.all' => array( 'description' => 'Все настройки оптического выравнивания', 'hide' => true, 'selector' => 'OptAlign.*'), - 'OptAlign.oa_oquote' => 'direct', - 'OptAlign.oa_obracket_coma' => 'direct', - 'OptAlign.oa_oquote_extra' => 'direct', - 'OptAlign.layout' => array( 'description' => 'Inline стили или CSS' ), - - 'Text.paragraphs' => 'direct', - 'Text.auto_links' => 'direct', - 'Text.email' => 'direct', - 'Text.breakline' => 'direct', - 'Text.no_repeat_words' => 'direct', - - - //'Etc.no_nbsp_in_nobr' => 'direct', - 'Etc.unicode_convert' => array('description' => 'Преобразовывать html-сущности в юникод', 'selector' => array('*', 'Etc.nobr_to_nbsp'), 'setting' => array('dounicode','active'), 'exact_selector' => true ,'disabled' => true), - 'Etc.nobr_to_nbsp' => 'direct', - 'Etc.split_number_to_triads' => 'direct', - - ); - - /** - * Получить список имеющихся опций - * - * @return array - * all - полный список - * group - сгруппированный по группам - */ - public function get_options_list() - { - $arr['all'] = array(); - $bygroup = array(); - foreach($this->all_options as $opt => $op) - { - $arr['all'][$opt] = $this->get_option_info($opt); - $x = explode(".",$opt); - $bygroup[$x[0]][] = $opt; - } - $arr['group'] = array(); - foreach($this->group_list as $group => $ginfo) - { - if($ginfo === true) - { - $tret = $this->get_tret($group); - if($tret) $info['title'] = $tret->title; else $info['title'] = "Не определено"; - } else { - $info = $ginfo; - } - $info['name'] = $group; - $info['options'] = array(); - if(is_array($bygroup[$group])) foreach($bygroup[$group] as $opt) $info['options'][] = $opt; - $arr['group'][] = $info; - } - return $arr; - } - - - /** - * Получить информацию о настройке - * - * @param string $key - * @return array|false - */ - protected function get_option_info($key) - { - if(!isset($this->all_options[$key])) return false; - if(is_array($this->all_options[$key])) return $this->all_options[$key]; - - if(($this->all_options[$key] == "direct") || ($this->all_options[$key] == "reverse")) - { - $pa = explode(".", $key); - $tret_pattern = $pa[0]; - $tret = $this->get_tret($tret_pattern); - if(!$tret) return false; - if(!isset($tret->rules[$pa[1]])) return false; - $array = $tret->rules[$pa[1]]; - $array['way'] = $this->all_options[$key]; - return $array; - } - return false; - } - - - /** - * Установка одной метанастройки - * - * @param string $name - * @param mixed $value - */ - public function do_setup($name, $value) - { - if(!isset($this->all_options[$name])) return; - - // эта настрока связана с правилом ядра - if(is_string($this->all_options[$name])) - { - $this->set($name, "active", $value ); - return ; - } - if(is_array($this->all_options[$name])) - { - if(isset($this->all_options[$name]['selector'])) - { - $settingname = "active"; - if(isset($this->all_options[$name]['setting'])) $settingname = $this->all_options[$name]['setting']; - $this->set($this->all_options[$name]['selector'], $settingname, $value, isset($this->all_options[$name]['exact_selector'])); - } - } - - if($name == "OptAlign.layout") - { - if($value == "style") $this->set_tag_layout(EMT_Lib::LAYOUT_STYLE); - if($value == "class") $this->set_tag_layout(EMT_Lib::LAYOUT_CLASS); - } - - } - - /** - * Запустить типограф со стандартными параметрами - * - * @param string $text - * @param array $options - * @return string - */ - public static function fast_apply($text, $options = null) - { - $obj = new self(); - if(is_array($options)) $obj->setup($options); - $obj->set_text($text); - return $obj->apply(); - } -} - - + + +class EMT_Lib +{ + const LAYOUT_STYLE = 1; + const LAYOUT_CLASS = 2; + + const INTERNAL_BLOCK_OPEN = '%%%INTBLOCKO235978%%%'; + const INTERNAL_BLOCK_CLOSE = '%%%INTBLOCKC235978%%%'; + /** + * Таблица символов + * + * @var array + */ + public static $_charsTable = array( + '"' => array('html' => array('«', '»', '”', '‘', '„', '“', '"', '«', '»'), + 'utf8' => array(0x201E, 0x201C, 0x201F, 0x201D, 0x00AB, 0x00BB)), + ' ' => array('html' => array(' ', ' ', ' '), + 'utf8' => array(0x00A0, 0x2002, 0x2003, 0x2008, 0x2009)), + '-' => array('html' => array(/*'—',*/ '–', '−', '—', '—', '–'), + 'utf8' => array(0x002D, /*0x2014,*/ 0x2010, 0x2012, 0x2013)), + '—' => array('html' => array('—'), + 'utf8' => array(0x2014)), + '==' => array('html' => array('≡'), + 'utf8' => array(0x2261)), + '...' => array('html' => array('…', '…'), + 'utf8' => array(0x2026)), + '!=' => array('html' => array('≠', '≠'), + 'utf8' => array(0x2260)), + '<=' => array('html' => array('≤', '≤'), + 'utf8' => array(0x2264)), + '>=' => array('html' => array('≥', '≥'), + 'utf8' => array(0x2265)), + '1/2' => array('html' => array('½', '½'), + 'utf8' => array(0x00BD)), + '1/4' => array('html' => array('¼', '¼'), + 'utf8' => array(0x00BC)), + '3/4' => array('html' => array('¾', '¾'), + 'utf8' => array(0x00BE)), + '+-' => array('html' => array('±', '±'), + 'utf8' => array(0x00B1)), + '&' => array('html' => array('&', '&')), + '(tm)' => array('html' => array('™', '™'), + 'utf8' => array(0x2122)), + //'(r)' => array('html' => array('®', '®', '®'), + '(r)' => array('html' => array('®', '®'), + 'utf8' => array(0x00AE)), + '(c)' => array('html' => array('©', '©'), + 'utf8' => array(0x00A9)), + '§' => array('html' => array('§', '§'), + 'utf8' => array(0x00A7)), + '`' => array('html' => array('́')), + '\'' => array('html' => array('’', '’')), + 'x' => array('html' => array('×', '×'), + 'utf8' => array('×') /* какой же у него может быть код? */), + + ); + + /** + * Добавление к тегам атрибута 'id', благодаря которому + * при повторном типографирование текста будут удалены теги, + * расставленные данным типографом + * + * @var array + */ + protected static $_typographSpecificTagId = false; + + + /** + * Костыли для работы с символами UTF-8 + * + * @author somebody? + * @param int $c код символа в кодировке UTF-8 (например, 0x00AB) + * @return bool|string + */ + public static function _getUnicodeChar($c) + { + if ($c <= 0x7F) { + return chr($c); + } else if ($c <= 0x7FF) { + return chr(0xC0 | $c >> 6) + . chr(0x80 | $c & 0x3F); + } else if ($c <= 0xFFFF) { + return chr(0xE0 | $c >> 12) + . chr(0x80 | $c >> 6 & 0x3F) + . chr(0x80 | $c & 0x3F); + } else if ($c <= 0x10FFFF) { + return chr(0xF0 | $c >> 18) + . chr(0x80 | $c >> 12 & 0x3F) + . chr(0x80 | $c >> 6 & 0x3F) + . chr(0x80 | $c & 0x3F); + } else { + return false; + } + } + + + /** + * Удаление кодов HTML из текста + * + * + * // Remove UTF-8 chars: + * $str = EMT_Lib::clear_special_chars('your text', 'utf8'); + * // ... or HTML codes only: + * $str = EMT_Lib::clear_special_chars('your text', 'html'); + * // ... or combo: + * $str = EMT_Lib::clear_special_chars('your text'); + * + * + * @param string $text + * @param mixed $mode + * @return string|bool + */ + public static function clear_special_chars($text, $mode = null) + { + if(is_string($mode)) $mode = array($mode); + if(is_null($mode)) $mode = array('utf8', 'html'); + if(!is_array($mode)) return false; + $moder = array(); + foreach($mode as $mod) if(in_array($mod, array('utf8','html'))) $moder[] = $mod; + if(count($moder)==0) return false; + + foreach (self::$_charsTable as $char => $vals) + { + foreach ($mode as $type) + { + if (isset($vals[$type])) + { + foreach ($vals[$type] as $v) + { + if ('utf8' === $type && is_int($v)) + { + $v = self::_getUnicodeChar($v); + } + if ('html' === $type) + { + if(preg_match("/<[a-z]+>/i",$v)) + { + $v = self::safe_tag_chars($v, true); + } + } + $text = str_replace($v, $char, $text); + } + } + } + } + + return $text; + } + + /** + * Удаление тегов HTML из текста + * Тег
будет преобразов в перенос строки \n, сочетание тегов

- + * в двойной перенос + * + * @param string $text + * @param array $allowableTag массив из тегов, которые будут проигнорированы + * @return string + */ + public static function remove_html_tags($text, $allowableTag = null) + { + $ignore = null; + + if (null !== $allowableTag) + { + if (is_string($allowableTag)) + { + $allowableTag = array($allowableTag); + } + if (is_array($allowableTag)) + { + $tags = array(); + foreach ($allowableTag as $tag) + { + if ('<' !== substr($tag, 0, 1) || '>' !== substr($tag, -1, 1)) continue; + if ('/' === substr($tag, 1, 1)) continue; + $tags [] = $tag; + } + $ignore = implode('', $tags); + } + } + $text = preg_replace(array('/\/i', '/\<\/p\>\s*\/'), array("\n","\n\n"), $text); + $text = strip_tags($text, $ignore); + return $text; + } + + /** + * Сохраняем содержимое тегов HTML + * + * Тег 'a' кодируется со специальным префиксом для дальнейшей + * возможности выносить за него кавычки. + * + * @param string $text + * @param bool $safe + * @return string + */ + public static function safe_tag_chars($text, $way) + { + if ($way) + $text = preg_replace_callback('/(\<\/?)([^<>]+?)(\>)/s', 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); + else + $text = preg_replace_callback('/(\<\/?)([^<>]+?)(\>)/s', 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); + return $text; + } + + + /** + * Декодриует спец блоки + * + * @param string $text + * @return string + */ + public static function decode_internal_blocks($text) + { + $text = preg_replace_callback('/'.EMT_Lib::INTERNAL_BLOCK_OPEN.'([a-zA-Z0-9\/=]+?)'.EMT_Lib::INTERNAL_BLOCK_CLOSE.'/s', create_function('$m','return EMT_Lib::decrypt_tag($m[1]);'), $text); + return $text; + } + + /** + * Кодирует спец блок + * + * @param string $text + * @return string + */ + public static function iblock($text) + { + return EMT_Lib::INTERNAL_BLOCK_OPEN. EMT_Lib::encrypt_tag($text).EMT_Lib::INTERNAL_BLOCK_CLOSE; + } + + + /** + * Создание тега с защищенным содержимым + * + * @param string $content текст, который будет обрамлен тегом + * @param string $tag тэг + * @param array $attribute список атрибутов, где ключ - имя атрибута, а значение - само значение данного атрибута + * @return string + */ + public static function build_safe_tag($content, $tag = 'span', $attribute = array(), $layout = EMT_Lib::LAYOUT_STYLE ) + { + $htmlTag = $tag; + + if (self::$_typographSpecificTagId) + { + if(!isset($attribute['id'])) + { + $attribute['id'] = 'emt-2' . mt_rand(1000,9999); + } + } + + $classname = ""; + if (count($attribute)) + { + + if($layout & EMT_lib::LAYOUT_STYLE) + { + if(isset($attribute['__style']) && $attribute['__style']) + { + if(isset($attribute['style']) && $attribute['style']) + { + $st = trim($attribute['style']); + if(mb_substr($st, -1) != ";") $st .= ";"; + $st .= $attribute['__style']; + $attribute['style'] = $st; + } else { + $attribute['style'] = $attribute['__style']; + } + unset($attribute['__style']); + } + + } + foreach ($attribute as $attr => $value) + { + if($attr == "__style") continue; + if($attr == "class") { + $classname = "$value"; + continue; + } + $htmlTag .= " $attr=\"$value\""; + } + + } + + if( ($layout & EMT_lib::LAYOUT_CLASS ) && $classname) { + $htmlTag .= " class=\"$classname\""; + } + + return "<" . self::encrypt_tag($htmlTag) . ">$content"; + } + + /** + * Метод, осуществляющий кодирование (сохранение) информации + * с целью невозможности типографировать ее + * + * @param string $text + * @return string + */ + public static function encrypt_tag($text) + { + return base64_encode($text)."="; + } + + /** + * Метод, осуществляющий декодирование информации + * + * @param string $text + * @return string + */ + public static function decrypt_tag($text) + { + return base64_decode(substr($text,0,-1)); + } + + + + public static function strpos_ex(&$haystack, $needle, $offset = null) + { + if(is_array($needle)) + { + $m = false; + $w = false; + foreach($needle as $n) + { + $p = strpos($haystack, $n , $offset); + if($p===false) continue; + if($m === false) + { + $m = $p; + $w = $n; + continue; + } + if($p < $m) + { + $m = $p; + $w = $n; + } + } + if($m === false) return false; + return array('pos' => $m, 'str' => $w); + } + return strpos($haystack, $needle, $offset); + } + + public static function _process_selector_pattern(&$pattern) + { + if($pattern===false) return; + $pattern = preg_quote($pattern , '/'); + $pattern = str_replace("\\*", "[a-z0-9_\-]*", $pattern); + $pattern = "/".$pattern."/i"; + } + public static function _test_pattern($pattern, $text) + { + if($pattern === false) return true; + return preg_match($pattern, $text); + } + + public static function strtolower($string) + { + $convert_to = array( + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", + "v", "w", "x", "y", "z", "à", "á", "â", "ã", "ä", "å", "æ", "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï", + "ð", "ñ", "ò", "ó", "ô", "õ", "ö", "ø", "ù", "ú", "û", "ü", "ý", "а", "б", "в", "г", "д", "е", "ё", "ж", + "з", "и", "й", "к", "л", "м", "н", "о", "п", "р", "с", "т", "у", "ф", "х", "ц", "ч", "ш", "щ", "ъ", "ы", + "ь", "э", "ю", "я" + ); + $convert_from = array( + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", + "V", "W", "X", "Y", "Z", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", + "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "Ø", "Ù", "Ú", "Û", "Ü", "Ý", "А", "Б", "В", "Г", "Д", "Е", "Ё", "Ж", + "З", "И", "Й", "К", "Л", "М", "Н", "О", "П", "Р", "С", "Т", "У", "Ф", "Х", "Ц", "Ч", "Ш", "Щ", "Ъ", "Ъ", + "Ь", "Э", "Ю", "Я" + ); + + return str_replace($convert_from, $convert_to, $string); + } + + // взято с http://www.w3.org/TR/html4/sgml/entities.html + protected static $html4_char_ents = array( + 'nbsp' => 160, + 'iexcl' => 161, + 'cent' => 162, + 'pound' => 163, + 'curren' => 164, + 'yen' => 165, + 'brvbar' => 166, + 'sect' => 167, + 'uml' => 168, + 'copy' => 169, + 'ordf' => 170, + 'laquo' => 171, + 'not' => 172, + 'shy' => 173, + 'reg' => 174, + 'macr' => 175, + 'deg' => 176, + 'plusmn' => 177, + 'sup2' => 178, + 'sup3' => 179, + 'acute' => 180, + 'micro' => 181, + 'para' => 182, + 'middot' => 183, + 'cedil' => 184, + 'sup1' => 185, + 'ordm' => 186, + 'raquo' => 187, + 'frac14' => 188, + 'frac12' => 189, + 'frac34' => 190, + 'iquest' => 191, + 'Agrave' => 192, + 'Aacute' => 193, + 'Acirc' => 194, + 'Atilde' => 195, + 'Auml' => 196, + 'Aring' => 197, + 'AElig' => 198, + 'Ccedil' => 199, + 'Egrave' => 200, + 'Eacute' => 201, + 'Ecirc' => 202, + 'Euml' => 203, + 'Igrave' => 204, + 'Iacute' => 205, + 'Icirc' => 206, + 'Iuml' => 207, + 'ETH' => 208, + 'Ntilde' => 209, + 'Ograve' => 210, + 'Oacute' => 211, + 'Ocirc' => 212, + 'Otilde' => 213, + 'Ouml' => 214, + 'times' => 215, + 'Oslash' => 216, + 'Ugrave' => 217, + 'Uacute' => 218, + 'Ucirc' => 219, + 'Uuml' => 220, + 'Yacute' => 221, + 'THORN' => 222, + 'szlig' => 223, + 'agrave' => 224, + 'aacute' => 225, + 'acirc' => 226, + 'atilde' => 227, + 'auml' => 228, + 'aring' => 229, + 'aelig' => 230, + 'ccedil' => 231, + 'egrave' => 232, + 'eacute' => 233, + 'ecirc' => 234, + 'euml' => 235, + 'igrave' => 236, + 'iacute' => 237, + 'icirc' => 238, + 'iuml' => 239, + 'eth' => 240, + 'ntilde' => 241, + 'ograve' => 242, + 'oacute' => 243, + 'ocirc' => 244, + 'otilde' => 245, + 'ouml' => 246, + 'divide' => 247, + 'oslash' => 248, + 'ugrave' => 249, + 'uacute' => 250, + 'ucirc' => 251, + 'uuml' => 252, + 'yacute' => 253, + 'thorn' => 254, + 'yuml' => 255, + 'fnof' => 402, + 'Alpha' => 913, + 'Beta' => 914, + 'Gamma' => 915, + 'Delta' => 916, + 'Epsilon' => 917, + 'Zeta' => 918, + 'Eta' => 919, + 'Theta' => 920, + 'Iota' => 921, + 'Kappa' => 922, + 'Lambda' => 923, + 'Mu' => 924, + 'Nu' => 925, + 'Xi' => 926, + 'Omicron' => 927, + 'Pi' => 928, + 'Rho' => 929, + 'Sigma' => 931, + 'Tau' => 932, + 'Upsilon' => 933, + 'Phi' => 934, + 'Chi' => 935, + 'Psi' => 936, + 'Omega' => 937, + 'alpha' => 945, + 'beta' => 946, + 'gamma' => 947, + 'delta' => 948, + 'epsilon' => 949, + 'zeta' => 950, + 'eta' => 951, + 'theta' => 952, + 'iota' => 953, + 'kappa' => 954, + 'lambda' => 955, + 'mu' => 956, + 'nu' => 957, + 'xi' => 958, + 'omicron' => 959, + 'pi' => 960, + 'rho' => 961, + 'sigmaf' => 962, + 'sigma' => 963, + 'tau' => 964, + 'upsilon' => 965, + 'phi' => 966, + 'chi' => 967, + 'psi' => 968, + 'omega' => 969, + 'thetasym' => 977, + 'upsih' => 978, + 'piv' => 982, + 'bull' => 8226, + 'hellip' => 8230, + 'prime' => 8242, + 'Prime' => 8243, + 'oline' => 8254, + 'frasl' => 8260, + 'weierp' => 8472, + 'image' => 8465, + 'real' => 8476, + 'trade' => 8482, + 'alefsym' => 8501, + 'larr' => 8592, + 'uarr' => 8593, + 'rarr' => 8594, + 'darr' => 8595, + 'harr' => 8596, + 'crarr' => 8629, + 'lArr' => 8656, + 'uArr' => 8657, + 'rArr' => 8658, + 'dArr' => 8659, + 'hArr' => 8660, + 'forall' => 8704, + 'part' => 8706, + 'exist' => 8707, + 'empty' => 8709, + 'nabla' => 8711, + 'isin' => 8712, + 'notin' => 8713, + 'ni' => 8715, + 'prod' => 8719, + 'sum' => 8721, + 'minus' => 8722, + 'lowast' => 8727, + 'radic' => 8730, + 'prop' => 8733, + 'infin' => 8734, + 'ang' => 8736, + 'and' => 8743, + 'or' => 8744, + 'cap' => 8745, + 'cup' => 8746, + 'int' => 8747, + 'there4' => 8756, + 'sim' => 8764, + 'cong' => 8773, + 'asymp' => 8776, + 'ne' => 8800, + 'equiv' => 8801, + 'le' => 8804, + 'ge' => 8805, + 'sub' => 8834, + 'sup' => 8835, + 'nsub' => 8836, + 'sube' => 8838, + 'supe' => 8839, + 'oplus' => 8853, + 'otimes' => 8855, + 'perp' => 8869, + 'sdot' => 8901, + 'lceil' => 8968, + 'rceil' => 8969, + 'lfloor' => 8970, + 'rfloor' => 8971, + 'lang' => 9001, + 'rang' => 9002, + 'loz' => 9674, + 'spades' => 9824, + 'clubs' => 9827, + 'hearts' => 9829, + 'diams' => 9830, + 'quot' => 34, + 'amp' => 38, + 'lt' => 60, + 'gt' => 62, + 'OElig' => 338, + 'oelig' => 339, + 'Scaron' => 352, + 'scaron' => 353, + 'Yuml' => 376, + 'circ' => 710, + 'tilde' => 732, + 'ensp' => 8194, + 'emsp' => 8195, + 'thinsp' => 8201, + 'zwnj' => 8204, + 'zwj' => 8205, + 'lrm' => 8206, + 'rlm' => 8207, + 'ndash' => 8211, + 'mdash' => 8212, + 'lsquo' => 8216, + 'rsquo' => 8217, + 'sbquo' => 8218, + 'ldquo' => 8220, + 'rdquo' => 8221, + 'bdquo' => 8222, + 'dagger' => 8224, + 'Dagger' => 8225, + 'permil' => 8240, + 'lsaquo' => 8249, + 'rsaquo' => 8250, + 'euro' => 8364, + ); + /** + * Вернуть уникод символ по html entinty + * + * @param string $entity + * @return string + */ + public static function html_char_entity_to_unicode($entity) + { + if(isset(self::$html4_char_ents[$entity])) return self::_getUnicodeChar(self::$html4_char_ents[$entity]); + return false; + } + + /** + * Сконвериторвать все html entity в соответсвующие юникод символы + * + * @param string $text + */ + public static function convert_html_entities_to_unicode(&$text) + { + $text = preg_replace_callback("/\&#([0-9]+)\;/", + create_function('$m', 'return EMT_Lib::_getUnicodeChar(intval($m[1]));') + , $text); + $text = preg_replace_callback("/\&#x([0-9A-F]+)\;/", + create_function('$m', 'return EMT_Lib::_getUnicodeChar(hexdec($m[1]));') + , $text); + $text = preg_replace_callback("/\&([a-zA-Z0-9]+)\;/", + create_function('$m', '$r = EMT_Lib::html_char_entity_to_unicode($m[1]); return $r ? $r : $m[0];') + , $text); + } + + public static function rstrpos ($haystack, $needle, $offset = 0){ + + if(trim($haystack) != "" && trim($needle) != "" && $offset <= mb_strlen($haystack)) + { + $last_pos = $offset; + $found = false; + while(($curr_pos = mb_strpos($haystack, $needle, $last_pos)) !== false) + { + $found = true; + $last_pos = $curr_pos + 1; + } + if($found) + { + return $last_pos - 1; + } + else + { + return false; + } + } + else + { + return false; + } + } + + public static function ifop($cond, $true, $false) { + return $cond ? $true : $false; + } + + function split_number($num) { + return number_format($num, 0, '', ' '); + } + +} + + + +/** + * Базовый класс для группы правил обработки текста + * Класс группы должен наследовать, данный класс и задавать + * в нём EMT_Tret::rules и EMT_Tret::$name + * + */ +class EMT_Tret { + + /** + * Набор правил в данной группе, который задан изначально + * Его можно менять динамически добавляя туда правила с помощью put_rule + * + * @var unknown_type + */ + public $rules; + public $title; + + + private $disabled = array(); + private $enabled = array(); + protected $_text= ''; + public $logging = false; + public $logs = false; + public $errors = false; + public $debug_enabled = false; + public $debug_info = array(); + + + private $use_layout = false; + private $use_layout_set = false; + private $class_layout_prefix = false; + + public $class_names = array(); + public $classes = array(); + public $settings = array(); + + /** + * Защищенные теги + * + * @todo привязать к методам из Jare_Typograph_Tool + */ + const BASE64_PARAGRAPH_TAG = 'cA==='; // p + const BASE64_BREAKLINE_TAG = 'YnIgLw==='; // br / (с пробелом и слэшем) + const BASE64_NOBR_OTAG = 'bm9icg==='; // nobr + const BASE64_NOBR_CTAG = 'L25vYnI=='; // /nobr + + /** + * Типы кавычек + */ + const QUOTE_FIRS_OPEN = '«'; + const QUOTE_FIRS_CLOSE = '»'; + const QUOTE_CRAWSE_OPEN = '„'; + const QUOTE_CRAWSE_CLOSE = '“'; + + + private function log($str, $data = null) + { + if(!$this->logging) return; + $this->logs[] = array('info' => $str, 'data' => $data); + } + + private function error($info, $data = null) + { + $this->errors[] = array('info' => $info, 'data' => $data); + $this->log('ERROR: '. $info , $data); + } + + public function debug($place, &$after_text) + { + if(!$this->debug_enabled) return; + $this->debug_info[] = array( + 'place' => $place, + 'text' => $after_text, + ); + } + + + /** + * Установить режим разметки для данного Трэта если не было раньше установлено, + * EMT_Lib::LAYOUT_STYLE - с помощью стилей + * EMT_Lib::LAYOUT_CLASS - с помощью классов + * + * @param int $kind + */ + public function set_tag_layout_ifnotset($layout) + { + if($this->use_layout_set) return; + $this->use_layout = $layout; + } + + /** + * Установить режим разметки для данного Трэта, + * EMT_Lib::LAYOUT_STYLE - с помощью стилей + * EMT_Lib::LAYOUT_CLASS - с помощью классов + * EMT_Lib::LAYOUT_STYLE|EMT_Lib::LAYOUT_CLASS - оба метода + * + * @param int $kind + */ + public function set_tag_layout($layout = EMT_Lib::LAYOUT_STYLE) + { + $this->use_layout = $layout; + $this->use_layout_set = true; + } + + public function set_class_layout_prefix($prefix) + { + $this->class_layout_prefix = $prefix; + } + + + public function debug_on() + { + $this->debug_enabled = true; + } + + public function log_on() + { + $this->debug_enabled = true; + } + + + private function getmethod($name) + { + if(!$name) return false; + if(!method_exists($this, $name)) return false; + return array($this, $name); + } + + private function _pre_parse() + { + $this->pre_parse(); + foreach($this->rules as $rule) + { + if(!isset($rule['init'])) continue; + $m = $this->getmethod($rule['init']); + if(!$m) continue; + call_user_func($m); + } + } + private function _post_parse() + { + foreach($this->rules as $rule) + { + if(!isset($rule['deinit'])) continue; + $m = $this->getmethod($rule['deinit']); + if(!$m) continue; + call_user_func($m); + } + $this->post_parse(); + } + + private function rule_order_sort($a, $b) + { + if($a['order'] == $b['order']) return 0; + if($a['order'] < $b['order']) return -1; + return 1; + } + + private function apply_rule($rule) + { + $name = $rule['id']; + //$this->log("Правило $name", "Применяем правило"); + $disabled = (isset($this->disabled[$rule['id']]) && $this->disabled[$rule['id']]) || ((isset($rule['disabled']) && $rule['disabled']) && !(isset($this->enabled[$rule['id']]) && $this->enabled[$rule['id']])); + if($disabled) + { + $this->log("Правило $name", "Правило отключено" . ((isset($rule['disabled']) && $rule['disabled'])? " (по умолчанию)" : "")); + return; + } + if(isset($rule['function']) && $rule['function']) + { + if(!(isset($rule['pattern']) && $rule['pattern'])) + { + if(method_exists($this, $rule['function'])) + { + $this->log("Правило $name", "Используется метод ".$rule['function']." в правиле"); + + call_user_func(array($this, $rule['function'])); + return; + } + if(function_exists($rule['function'])) + { + $this->log("Правило $name", "Используется функция ".$rule['function']." в правиле"); + + call_user_func($rule['function']); + return; + } + + $this->error('Функция '.$rule['function'].' из правила '.$rule['id']. " не найдена"); + return ; + } else { + if(preg_match("/^[a-z_0-9]+$/i", $rule['function'])) + { + if(method_exists($this, $rule['function'])) + { + $this->log("Правило $name", "Замена с использованием preg_replace_callback с методом ".$rule['function'].""); + + $this->_text = preg_replace_callback($rule['pattern'], array($this, $rule['function']), $this->_text); + return; + } + if(function_exists($rule['function'])) + { + $this->log("Правило $name", "Замена с использованием preg_replace_callback с функцией ".$rule['function'].""); + + $this->_text = preg_replace_callback($rule['pattern'], $rule['function'], $this->_text); + return; + } + $this->error('Функция '.$rule['function'].' из правила '.$rule['id']. " не найдена"); + } else { + $this->_text = preg_replace_callback($rule['pattern'], create_function('$m', $rule['function']), $this->_text); + $this->log('Замена с использованием preg_replace_callback с инлайн функцией из правила '.$rule['id']); + return; + } + return ; + } + } + + if(isset($rule['simple_replace']) && $rule['simple_replace']) + { + if(isset($rule['case_sensitive']) && $rule['case_sensitive']) + { + $this->log("Правило $name", "Простая замена с использованием str_replace"); + $this->_text = str_replace($rule['pattern'], $rule['replacement'], $this->_text); + return; + } + $this->log("Правило $name", "Простая замена с использованием str_ireplace"); + $this->_text = str_ireplace($rule['pattern'], $rule['replacement'], $this->_text); + return; + } + + $pattern = $rule['pattern']; + if(is_string($pattern)) $pattern = array($pattern); + $eval = false; + foreach($pattern as $patt) + { + $chr = substr($patt,0,1); + $preg_arr = explode($chr, $patt); + if(strpos($preg_arr[count($preg_arr)-1], "e")!==false) + { + $eval = true; + break; + } + } + if(!$eval) + { + $this->log("Правило $name", "Замена с использованием preg_replace"); + + do { + $this->_text = preg_replace($rule['pattern'], $rule['replacement'], $this->_text); + if(!(isset($rule['cycled']) && $rule['cycled'])) break; + } while(preg_match($rule['pattern'], $this->_text)); + + return; + } + + $this->log("Правило $name", "Замена с использованием preg_replace_callback вместо eval"); + $k = 0; + foreach($pattern as $patt) + { + $repl = is_string($rule['replacement']) ? $rule['replacement'] : $rule['replacement'][$k]; + + $chr = substr($patt,0,1); + $preg_arr = explode($chr, $patt); + if(strpos($preg_arr[count($preg_arr)-1], "e")!==false) // eval система + { + $preg_arr[count($preg_arr)-1] = str_replace("e","",$preg_arr[count($preg_arr)-1]); + $patt = implode($chr, $preg_arr); + $this->thereplacement = $repl; + do { + $this->_text = preg_replace_callback($patt, array($this, "thereplcallback"), $this->_text); + if(!(isset($rule['cycled']) && $rule['cycled'])) break; + } while(preg_match($patt, $this->_text)); + + } else { + do { + $this->_text = preg_replace($patt, $repl, $this->_text); + if(!(isset($rule['cycled']) && $rule['cycled'])) break; + } while(preg_match($patt, $this->_text)); + } + $k++; + } + } + + + protected function preg_replace_e($pattern, $replacement, $text) + { + $chr = substr($pattern,0,1); + $preg_arr = explode($chr, $pattern); + if(strpos($preg_arr[count($preg_arr)-1], "e")===false) return preg_replace($pattern, $replacement, $text); + $preg_arr[count($preg_arr)-1] = str_replace("e","",$preg_arr[count($preg_arr)-1]); + $patt = implode($chr, $preg_arr); + $this->thereplacement = $replacement; + return preg_replace_callback($patt, array($this, "thereplcallback"), $text); + } + + private $thereplacement = ""; + private function thereplcallback($m) + { + $x = ""; + eval('$x = '.($this->thereplacement?$this->thereplacement:'""').';'); + return $x; + } + + private function _apply($list) + { + $this->errors = array(); + $this->_pre_parse(); + + $this->log("Применяется набор правил", implode(",",$list)); + + $rulelist = array(); + foreach($list as $k) + { + $rule = $this->rules[$k]; + $rule['id'] = $k; + $rule['order'] = isset($rule['order'])? $rule['order'] : 5 ; + $rulelist[] = $rule; + } + //usort($rulelist, array($this, "rule_order_sort")); + + foreach($rulelist as $rule) + { + $this->apply_rule($rule); + $this->debug($rule['id'], $this->_text); + } + + $this->_post_parse(); + } + + + /** + * Создание защищенного тега с содержимым + * + * @see EMT_lib::build_safe_tag + * @param string $content + * @param string $tag + * @param array $attribute + * @return string + */ + protected function tag($content, $tag = 'span', $attribute = array()) + { + if(isset($attribute['class'])) + { + $classname = $attribute['class']; + if($classname == "nowrap") + { + if(!$this->is_on('nowrap')) + { + $tag = "nobr"; + $attribute = array(); + $classname = ""; + } + } + if(isset($this->classes[$classname])) + { + $style_inline = $this->classes[$classname]; + if($style_inline) $attribute['__style'] = $style_inline; + } + $classname = (isset($this->class_names[$classname]) ? $this->class_names[$classname] :$classname); + $classname = ($this->class_layout_prefix ? $this->class_layout_prefix : "" ).$classname; + $attribute['class'] = $classname; + } + + return EMT_Lib::build_safe_tag($content, $tag, $attribute, + $this->use_layout === false? EMT_Lib::LAYOUT_STYLE : $this->use_layout ); + } + + + /** + * Добавить правило в группу + * + * @param string $name + * @param array $params + */ + public function put_rule($name, $params) + { + $this->rules[$name] = $params; + return $this; + } + + /** + * Отключить правило, в обработке + * + * @param string $name + */ + public function disable_rule($name) + { + $this->disabled[$name] = true; + unset($this->enabled[$name]); + } + + /** + * Включить правило + * + * @param string $name + */ + public function enable_rule($name) + { + $this->enabled[$name] = true; + unset($this->disabled[$name]); + } + + /** + * Добавить настройку в трет + * + * @param string $key ключ + * @param mixed $value значение + */ + public function set($key, $value) + { + $this->settings[$key] = $value; + } + + /** + * Установлена ли настройка + * + * @param string $key + */ + public function is_on($key) + { + if(!isset($this->settings[$key])) return false; + $kk = $this->settings[$key]; + return ((strtolower($kk)=="on") || ($kk === "1") || ($kk === true) || ($kk === 1)); + } + + /** + * Получить строковое значение настройки + * + * @param unknown_type $key + * @return unknown + */ + public function ss($key) + { + if(!isset($this->settings[$key])) return ""; + return strval($this->settings[$key]); + } + + /** + * Добавить настройку в правило + * + * @param string $rulename идентификатор правила + * @param string $key ключ + * @param mixed $value значение + */ + public function set_rule($rulename, $key, $value) + { + $this->rules[$rulename][$key] = $value; + } + + /** + * Включить правила, согласно списку + * + * @param array $list список правил + * @param boolean $disable выкллючить их или включить + * @param boolean $strict строго, т.е. те которые не в списку будут тоже обработаны + */ + public function activate($list,$disable =false, $strict = true) + { + if(!is_array($list)) return ; + + foreach($list as $rulename) + { + if($disable) $this->disable_rule($rulename); else $this->enable_rule($rulename); + } + + if($strict) + { + foreach($this->rules as $rulename => $v) + { + if(in_array($rulename, $list)) continue; + if(!$disable) $this->disable_rule($rulename); else $this->enable_rule($rulename); + } + } + } + + public function set_text(&$text) + { + $this->_text = &$text; + $this->debug_info = array(); + $this->logs = array(); + } + + + /** + * Применить к тексту + * + * @param string $text - текст к которому применить + * @param mixed $list - список правил, null - все правила + * @return string + */ + public function apply($list = null) + { + if(is_string($list)) $rlist = array($list); + elseif(is_array($list)) $rlist = $list; + else $rlist = array_keys($this->rules); + $this->_apply($rlist); + return $this->_text; + } + + + + + /** + * Код, выполняем до того, как применить правила + * + */ + public function pre_parse() + { + } + + /** + * После выполнения всех правил, выполняется этот метод + * + */ + public function post_parse() + { + } + + +} + + + + +/** + * @see EMT_Tret + */ + +class EMT_Tret_Abbr extends EMT_Tret +{ + public $title = "Сокращения"; + + public $domain_zones = array('ru','ру','com','ком','org','орг', 'уа', 'ua'); + + public $classes = array( + 'nowrap' => 'word-spacing:nowrap;', + ); + + public $rules = array( + 'nobr_abbreviation' => array( + 'description' => 'Расстановка пробелов перед сокращениями dpi, lpi', + 'pattern' => '/(\s+|^|\>)(\d+)(\040|\t)*(dpi|lpi)([\s\;\.\?\!\:\(]|$)/i', + 'replacement' => '\1\2 \4\5' + ), + 'nobr_acronym' => array( + 'description' => 'Расстановка пробелов перед сокращениями гл., стр., рис., илл., ст., п.', + 'pattern' => '/(\s|^|\>|\()(гл|стр|рис|илл?|ст|п|с)\.(\040|\t)*(\d+)(\ \;|\s|\.|\,|\?|\!|$)/iu', + 'replacement' => '\1\2. \4\5' + ), + 'nobr_sm_im' => array( + 'description' => 'Расстановка пробелов перед сокращениями см., им.', + 'pattern' => '/(\s|^|\>|\()(см|им)\.(\040|\t)*([а-яё0-9a-z]+)(\s|\.|\,|\?|\!|$)/iu', + 'replacement' => '\1\2. \4\5' + ), + 'nobr_locations' => array( + 'description' => 'Расстановка пробелов в сокращениях г., ул., пер., д.', + 'pattern' => array( + '/(\s|^|\>)(г|ул|пер|просп|пл|бул|наб|пр|ш|туп)\.(\040|\t)*([а-яё0-9a-z]+)(\s|\.|\,|\?|\!|$)/iu', + '/(\s|^|\>)(б\-р|пр\-кт)(\040|\t)*([а-яё0-9a-z]+)(\s|\.|\,|\?|\!|$)/iu', + '/(\s|^|\>)(д|кв|эт)\.(\040|\t)*(\d+)(\s|\.|\,|\?|\!|$)/iu', + ), + 'replacement' => array( + '\1\2. \4\5', + '\1\2 \4\5', + '\1\2. \4\5', + ) + ), + 'nbsp_before_unit' => array( + 'description' => 'Замена символов и привязка сокращений в размерных величинах: м, см, м2…', + 'pattern' => array( + '/(\s|^|\>|\ \;|\,)(\d+)( |\ \;)?(м|мм|см|дм|км|гм|km|dm|cm|mm)(\s|\.|\!|\?|\,|$|\±\;|\;|\<)/iu', + '/(\s|^|\>|\ \;|\,)(\d+)( |\ \;)?(м|мм|см|дм|км|гм|km|dm|cm|mm)([32]|³|²)(\s|\.|\!|\?|\,|$|\±\;|\;|\<)/iue' + ), + 'replacement' => array( + '\1\2 \4\5', + '$m[1].$m[2]." ".$m[4].($m[5]=="3"||$m[5]=="2"? "&sup".$m[5].";" : $m[5] ).$m[6]' + ), + ), + 'nbsp_before_weight_unit' => array( + 'description' => 'Замена символов и привязка сокращений в весовых величинах: г, кг, мг…', + 'pattern' => '/(\s|^|\>|\ \;|\,)(\d+)( |\ \;)?(г|кг|мг|т)(\s|\.|\!|\?|\,|$|\ \;|\;)/iu', + 'replacement' => '\1\2 \4\5', + ), + 'nobr_before_unit_volt' => array( + 'description' => 'Установка пробельных символов в сокращении вольт', + 'pattern' => '/(\d+)([вВ]| В)(\s|\.|\!|\?|\,|$)/u', + 'replacement' => '\1 В\3' + ), + 'ps_pps' => array( + 'description' => 'Объединение сокращений P.S., P.P.S.', + 'pattern' => '/(^|\040|\t|\>|\r|\n)(p\.\040?)(p\.\040?)?(s\.)([^\<])/ie', + 'replacement' => '$m[1] . $this->tag(trim($m[2]) . " " . ($m[3] ? trim($m[3]) . " " : ""). $m[4], "span", array("class" => "nowrap") ).$m[5] ' + ), + 'nobr_vtch_itd_itp' => array( + 'description' => 'Объединение сокращений и т.д., и т.п., в т.ч.', + 'cycled' => true, + 'pattern' => array( + '/(^|\s|\ \;)и( |\ \;)т\.?[ ]?д(\.|$|\s|\ \;)/ue', + '/(^|\s|\ \;)и( |\ \;)т\.?[ ]?п(\.|$|\s|\ \;)/ue', + '/(^|\s|\ \;)в( |\ \;)т\.?[ ]?ч(\.|$|\s|\ \;)/ue', + ), + 'replacement' => array( + '$m[1].$this->tag("и т. д.", "span", array("class" => "nowrap")).($m[3]!="."? $m[3] : "" )', + '$m[1].$this->tag("и т. п.", "span", array("class" => "nowrap")).($m[3]!="."? $m[3] : "" )', + '$m[1].$this->tag("в т. ч.", "span", array("class" => "nowrap")).($m[3]!="."? $m[3] : "" )', + ) + ), + 'nbsp_te' => array( + 'description' => 'Обработка т.е.', + 'pattern' => '/(^|\s|\ \;)([тТ])\.?[ ]?е\./ue', + 'replacement' => '$m[1].$this->tag($m[2].". е.", "span", array("class" => "nowrap"))', + ), + 'nbsp_money_abbr' => array( + 'description' => 'Форматирование денежных сокращений (расстановка пробелов и привязка названия валюты к числу)', + 'pattern' => '/(\d)((\040|\ \;)?(тыс|млн|млрд)\.?(\040|\ \;)?)?(\040|\ \;)?(руб\.|долл\.|евро|€|€|\$|у[\.]? ?е[\.]?)/ieu', + 'replacement' => '$m[1].($m[4]?" ".$m[4].($m[4]=="тыс"?".":""):"")." ".(!preg_match("#у[\\\\.]? ?е[\\\\.]?#iu",$m[7])?$m[7]:"у.е.")', + ), + 'nbsp_money_abbr_rev' => array( + 'description' => 'Привязка валюты к числу спереди', + 'pattern' => '/(€|€|\$)\s?(\d)/iu', + 'replacement' => '\1 \2' + ), + 'nbsp_org_abbr' => array( + 'description' => 'Привязка сокращений форм собственности к названиям организаций', + 'pattern' => '/([^a-zA-Zа-яёА-ЯЁ]|^)(ООО|ЗАО|ОАО|НИИ|ПБОЮЛ) ([a-zA-Zа-яёА-ЯЁ]|\"|\«\;|\&bdquo\;|<)/u', + 'replacement' => '\1\2 \3' + ), + 'nobr_gost' => array( + 'description' => 'Привязка сокращения ГОСТ к номеру', + 'pattern' => array( + '/(\040|\t|\ \;|^)ГОСТ( |\ \;)?(\d+)((\-|\&minus\;|\&mdash\;)(\d+))?(( |\ \;)(\-|\&mdash\;))?/ieu', + '/(\040|\t|\ \;|^|\>)ГОСТ( |\ \;)?(\d+)(\-|\&minus\;|\&mdash\;)(\d+)/ieu', + ), + 'replacement' => array( + '$m[1].$this->tag("ГОСТ ".$m[3].(isset($m[6])?"–".$m[6]:"").(isset($m[7])?" —":""),"span", array("class"=>"nowrap"))', + '$m[1]."ГОСТ ".$m[3]."–".$m[5]', + ), + ), + /* + 'nobr_vtch_itd_itp' => array( + 'description' => 'Привязка сокращений до н.э., н.э.', + 'pattern' => array( + + //IV в до н.э, в V-VIвв до нэ., третий в. н.э. + + '/(\s|\ \;)и( |\ \;)т\.?[ ]?д\./ue', + '/(\s|\ \;)и( |\ \;)т\.?[ ]?п\./ue', + '/(\s|\ \;)в( |\ \;)т\.?[ ]?ч\./ue', + ), + 'replacement' => array( + '$m[1].$this->tag("и т. д.", "span", array("class" => "nowrap"))', + '$m[1].$this->tag("и т. п.", "span", array("class" => "nowrap"))', + '$m[1].$this->tag("в т. ч.", "span", array("class" => "nowrap"))', + ) + ), + */ + + + ); +} + + +/** + * @see EMT_Tret + */ + +class EMT_Tret_Dash extends EMT_Tret +{ + public $title = "Дефисы и тире"; + public $rules = array( + 'mdash_symbol_to_html_mdash' => array( + 'description' => 'Замена символа тире на html конструкцию', + 'pattern' => '/—/iu', + 'replacement' => '—' + ), + 'mdash' => array( + 'description' => 'Тире после кавычек, скобочек, пунктуации', + 'pattern' => array( + '/([a-zа-яё0-9]+|\,|\:|\)|\&(ra|ld)quo\;|\|\"|\>)(\040|\t)(—|\-|\&mdash\;)(\s|$|\<)/ui', + '/(\,|\:|\)|\")(—|\-|\&mdash\;)(\s|$|\<)/ui', + ), + 'replacement' => array( + '\1 —\5', + '\1 —\3', + ), + ), + 'mdash_2' => array( + 'description' => 'Тире после переноса строки', + 'pattern' => '/(\n|\r|^|\>)(\-|\&mdash\;)(\t|\040)/', + 'replacement' => '\1— ' + ), + 'mdash_3' => array( + 'description' => 'Тире после знаков восклицания, троеточия и прочее', + 'pattern' => '/(\.|\!|\?|\&hellip\;)(\040|\t|\ \;)(\-|\&mdash\;)(\040|\t|\ \;)/', + 'replacement' => '\1 — ' + ), + 'iz_za_pod' => array( + 'description' => 'Расстановка дефисов между из-за, из-под', + 'pattern' => '/(\s|\ \;|\>|^)(из)(\040|\t|\ \;)\-?(за|под)([\.\,\!\?\:\;]|\040|\ \;)/uie', + 'replacement' => '($m[1] == " " ? " " : $m[1]) . $m[2]."-".$m[4] . ($m[5] == " "? " " : $m[5])' + ), + 'to_libo_nibud' => array( + 'description' => 'Автоматическая простановка дефисов в обезличенных местоимениях и междометиях', + 'cycled' => true, + 'pattern' => '/(\s|^|\ \;|\>)(кто|кем|когда|зачем|почему|как|что|чем|где|чего|кого)\-?(\040|\t|\ \;)\-?(то|либо|нибудь)([\.\,\!\?\;]|\040|\ \;|$)/uie', + 'replacement' => '($m[1] == " " ? " " : $m[1]) . $m[2]."-".$m[4] . ($m[5] == " "? " " : $m[5])' + ), + 'koe_kak' => array( + 'description' => 'Кое-как, кой-кого, все-таки', + 'cycled' => true, + 'pattern' => array( + '/(\s|^|\ \;|\>)(кое)\-?(\040|\t|\ \;)\-?(как)([\.\,\!\?\;]|\040|\ \;|$)/uie', + '/(\s|^|\ \;|\>)(кой)\-?(\040|\t|\ \;)\-?(кого)([\.\,\!\?\;]|\040|\ \;|$)/uie', + '/(\s|^|\ \;|\>)(вс[её])\-?(\040|\t|\ \;)\-?(таки)([\.\,\!\?\;]|\040|\ \;|$)/uie', + ), + 'replacement' => '($m[1] == " " ? " " : $m[1]) . $m[2]."-".$m[4] . ($m[5] == " "? " " : $m[5])' + ), + 'ka_de_kas' => array( + 'description' => 'Расстановка дефисов с частицами ка, де, кась', + 'disabled' => true, + 'pattern' => array( + '/(\s|^|\ \;|\>)([а-яё]+)(\040|\t|\ \;)(ка)([\.\,\!\?\;]|\040|\ \;|$)/uie', + '/(\s|^|\ \;|\>)([а-яё]+)(\040|\t|\ \;)(де)([\.\,\!\?\;]|\040|\ \;|$)/uie', + '/(\s|^|\ \;|\>)([а-яё]+)(\040|\t|\ \;)(кась)([\.\,\!\?\;]|\040|\ \;|$)/uie', + ), + 'replacement' => '($m[1] == " " ? " " : $m[1]) . $m[2]."-".$m[4] . ($m[5] == " "? " " : $m[5])' + ), + + + + ); + +} + + +/** + * @see EMT_Tret + */ + +class EMT_Tret_Date extends EMT_Tret +{ + public $title = "Даты и дни"; + + public $classes = array( + 'nowrap' => 'word-spacing:nowrap;', + ); + + public $rules = array( + 'years' => array( + 'description' => 'Установка тире и пробельных символов в периодах дат', + 'pattern' => '/(с|по|период|середины|начала|начало|конца|конец|половины|в|между|\([cс]\)|\©\;)(\s+|\ \;)([\d]{4})(-|\&mdash\;|\&minus\;)([\d]{4})(( |\ \;)?(г\.г\.|гг\.|гг|г\.|г)([^а-яёa-z]))?/eui', + '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]:"")' + ), + 'mdash_month_interval' => array( + 'description' => 'Расстановка тире и объединение в неразрывные периоды месяцев', + 'disabled' => true, + 'pattern' => '/((январ|феврал|сентябр|октябр|ноябр|декабр)([ьяюе]|[её]м)|(апрел|июн|июл)([ьяюе]|ем)|(март|август)([ауе]|ом)?|ма[йяюе]|маем)\-((январ|феврал|сентябр|октябр|ноябр|декабр)([ьяюе]|[её]м)|(апрел|июн|июл)([ьяюе]|ем)|(март|август)([ауе]|ом)?|ма[йяюе]|маем)/iu', + 'replacement' => '\1—\8' + ), + 'nbsp_and_dash_month_interval' => array( + 'description' => 'Расстановка тире и объединение в неразрывные периоды дней', + 'disabled' => true, + 'pattern' => '/([^\>]|^)(\d+)(\-|\&minus\;|\&mdash\;)(\d+)( |\ \;)(января|февраля|марта|апреля|мая|июня|июля|августа|сентября|октября|ноября|декабря)([^\<]|$)/ieu', + 'replacement' => '$m[1].$this->tag($m[2]."—".$m[4]." ".$m[6],"span", array("class"=>"nowrap")).$m[7]' + ), + 'nobr_year_in_date' => array( + 'description' => 'Привязка года к дате', + 'pattern' => array( + '/(\s|\ \;)([0-9]{2}\.[0-9]{2}\.([0-9]{2})?[0-9]{2})(\s|\ \;)?г(\.|\s|\ \;)/eiu', + '/(\s|\ \;)([0-9]{2}\.[0-9]{2}\.([0-9]{2})?[0-9]{2})(\s|\ \;|\.(\s|\ \;|$)|$)/eiu', + ), + 'replacement' => array( + '$m[1].$this->tag($m[2]." г.","span", array("class"=>"nowrap")).($m[5]==="."?"":" ")', + '$m[1].$this->tag($m[2],"span", array("class"=>"nowrap")).$m[4]', + ), + ), + 'space_posle_goda' => array( + 'description' => 'Пробел после года', + 'pattern' => '/(^|\040|\ \;)([0-9]{3,4})(год([ауе]|ом)?)([^a-zа-яё]|$)/ui', + 'replacement' => '\1\2 \3\5' + ), + 'nbsp_posle_goda_abbr' => array( + 'description' => 'Пробел после года', + 'pattern' => '/(^|\040|\ \;|\"|\«\;)([0-9]{3,4})[ ]?(г\.)([^a-zа-яё]|$)/ui', + 'replacement' => '\1\2 \3\4' + ), + + ); +} + + +/** + * @see EMT_Tret + */ + +class EMT_Tret_Etc extends EMT_Tret +{ + + + public $classes = array( + 'nowrap' => 'word-spacing:nowrap;', + ); + + + /** + * Базовые параметры тофа + * + * @var array + */ + public $title = "Прочее"; + public $rules = array( + 'acute_accent' => array( + 'description' => 'Акцент', + 'pattern' => '/(у|е|ы|а|о|э|я|и|ю|ё)\`(\w)/i', + 'replacement' => '\1́\2' + ), + + + + 'word_sup' => array( + 'description' => 'Надстрочный текст после символа ^', + 'pattern' => '/((\s|\ \;|^)+)\^([a-zа-яё0-9\.\:\,\-]+)(\s|\ \;|$|\.$)/ieu', + 'replacement' => '"" . $this->tag($this->tag($m[3],"small"),"sup") . $m[4]' + ), + 'century_period' => array( + 'description' => 'Тире между диапозоном веков', + 'pattern' => '/(\040|\t|\ \;|^)([XIV]{1,5})(-|\&mdash\;)([XIV]{1,5})(( |\ \;)?(в\.в\.|вв\.|вв|в\.|в))/eu', + 'replacement' => '$m[1] .$this->tag($m[2]."—".$m[4]." вв.","span", array("class"=>"nowrap"))' + ), + 'time_interval' => array( + 'description' => 'Тире и отмена переноса между диапозоном времени', + 'pattern' => '/([^\d\>]|^)([\d]{1,2}\:[\d]{2})(-|\&mdash\;|\&minus\;)([\d]{1,2}\:[\d]{2})([^\d\<]|$)/eui', + 'replacement' => '$m[1] . $this->tag($m[2]."—".$m[4],"span", array("class"=>"nowrap")).$m[5]' + ), + 'split_number_to_triads' => array( + 'description' => 'Разбиение числа на триады', + 'pattern' => '/([^a-zA-Z0-9<\)]|^)([0-9]{5,})([^a-zA-Z>\(]|$)/eu', + 'replacement' => '$m[1].str_replace(" "," ",EMT_Lib::split_number($m[2])).$m[3] ' + //'function' => 'split_number' + ), + 'expand_no_nbsp_in_nobr' => array( + 'description' => 'Удаление nbsp в nobr/nowrap тэгах', + 'function' => 'remove_nbsp' + ), + 'nobr_to_nbsp' => array( + 'description' => 'Преобразование nobr в nbsp', + 'disabled' => true, + 'function' => 'nobr_to_nbsp' + ), + ); + + protected function remove_nbsp() + { + $thetag = $this->tag("###", 'span', array('class' => "nowrap")); + $arr = explode("###", $thetag); + $b = preg_quote($arr[0], '/'); + $e = preg_quote($arr[1], '/'); + + $match = '/(^|[^a-zа-яё])([a-zа-яё]+)\ \;('.$b.')/iu'; + do { + $this->_text = preg_replace($match, '\1\3\2 ', $this->_text); + } while(preg_match($match, $this->_text)); + + $match = '/('.$e.')\ \;([a-zа-яё]+)($|[^a-zа-яё])/iu'; + do { + $this->_text = preg_replace($match, ' \2\1\3', $this->_text); + } while(preg_match($match, $this->_text)); + + $this->_text = $this->preg_replace_e('/'.$b.'.*?'.$e.'/iue', 'str_replace(" "," ",$m[0]);' , $this->_text ); + } + + protected function nobr_to_nbsp() + { + $thetag = $this->tag("###", 'span', array('class' => "nowrap")); + $arr = explode("###", $thetag); + $b = preg_quote($arr[0], '/'); + $e = preg_quote($arr[1], '/'); + $this->_text = $this->preg_replace_e('/'.$b.'(.*?)'.$e.'/iue', 'str_replace(" "," ",$m[1]);' , $this->_text ); + } + /* + protected function split_number () { + + $this->preg_replace_e("/([^a-zA-Z<]|^)([0-9]{5,})([^a-zA-Z>]|$)/u", ) + + $match = ; + while(preg_match($match, $this->_text, $m)) { + $repl = ""; + for($i = strlen($m[2]); $i >=0 ; $i-=3) + if($i-3>=0) $repl = ($i>3?" ":"").substr($m[2], $i-3, 3) . $repl; else $repl = substr($m[2], 0, $i) . $repl; + $this->_text = str_replace($m[1], $repl, $this->_text); + } + }*/ + +} + + + + +/** + * @see EMT_Tret + */ + +class EMT_Tret_Nobr extends EMT_Tret +{ + public $title = "Неразрывные конструкции"; + + public $classes = array( + 'nowrap' => 'word-spacing:nowrap;', + ); + + public $rules = array( + + 'super_nbsp' => array( + 'description' => 'Привязка союзов и предлогов к написанным после словам', + 'pattern' => '/(\s|^|\&(la|bd)quo\;|\>|\(|\&mdash\;\ \;)([a-zа-яё]{1,2}\s+)([a-zа-яё]{1,2}\s+)?([a-zа-яё0-9\-]{2,}|[0-9])/ieu', + 'replacement' => '$m[1] . trim($m[3]) . " " . ($m[4] ? trim($m[4]) . " " : "") . $m[5]' + ), + 'nbsp_in_the_end' => array( + 'description' => 'Привязка союзов и предлогов к предыдущим словам в случае конца предложения', + 'pattern' => '/([a-zа-яё0-9\-]{3,}) ([a-zа-яё]{1,2})\.( [A-ZА-ЯЁ]|$)/u', + 'replacement' => '\1 \2.\3' + ), + 'phone_builder' => array( + 'description' => 'Объединение в неразрывные конструкции номеров телефонов', + 'pattern' => + array( + '/([^\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', + '/([^\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', + ), + 'replacement' => + array( + '$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]', + '$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]', + ), + ), + 'phone_builder_v2' => array( + 'description' => 'Дополнительный формат номеров телефонов', + 'pattern' => '/([^\d]|^)\+\s?([0-9]{1})\s?\(([0-9]{3,4})\)\s?(\d{3})(\d{2})(\d{2})([^\d]|$)/ie', + 'replacement' => '$m[1].$this->tag("+".$m[2]." ".$m[3]." ".$m[4]."-".$m[5]."-".$m[6], "span", array("class" => "nowrap")).$m[7]', + ), + 'ip_address' => array( + 'description' => 'Объединение IP-адресов', + 'pattern' => '/(\s|\ \;|^)(\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3})/ie', + 'replacement' => '$m[1] . $this->nowrap_ip_address($m[2])' + ), + 'dots_for_surname_abbr' => array( + 'disabled' => true, + 'description' => 'Простановка точек к инициалам у фамилии', + 'pattern' => + array( + '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ])\.?(\s|\ \;)?([А-ЯЁ])(\s|\ \;)([А-ЯЁ][а-яё]+)(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', + '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ][а-яё]+)(\s|\ \;)([А-ЯЁ])\.?(\s|\ \;)?([А-ЯЁ])\.?(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', + ), + 'replacement' => + array( + '$m[1].$this->tag($m[2].". ".$m[4].". ".$m[6], "span", array("class" => "nowrap")).$m[7]', + '$m[1].$this->tag($m[2]." ".$m[4].". ".$m[6].".", "span", array("class" => "nowrap")).$m[7]', + ), + ), + 'spaces_nobr_in_surname_abbr' => array( + 'description' => 'Привязка инициалов к фамилиям', + 'pattern' => + array( + '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ])\.(\s|\ \;)?([А-ЯЁ])\.(\s|\ \;)?([А-ЯЁ][а-яё]+)(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', + '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ][а-яё]+)(\s|\ \;)([А-ЯЁ])\.(\s|\ \;)?([А-ЯЁ])\.(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', + '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ])(\s|\ \;)?([А-ЯЁ])(\s|\ \;)([А-ЯЁ][а-яё]+)(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', + '/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([А-ЯЁ][а-яё]+)(\s|\ \;)([А-ЯЁ])(\s|\ \;)?([А-ЯЁ])(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', + //'/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([A-Z])\.?(\s|\ \;)?([A-Z])(\.(\s|\ \;)?|(\s|\ \;))([A-Z][a-z]+)(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', + //'/(\s|^|\.|\,|\;|\:|\?|\!|\ \;)([A-Z][a-z]+)(\s|\ \;)([A-Z])\.?(\s|\ \;)?([A-Z])\.?(\s|$|\.|\,|\;|\:|\?|\!|\ \;)/ue', + ), + 'replacement' => + array( + '$m[1].$this->tag($m[2].". ".$m[4].". ".$m[6], "span", array("class" => "nowrap")).$m[7]', + '$m[1].$this->tag($m[2]." ".$m[4].". ".$m[6].".", "span", array("class" => "nowrap")).$m[7]', + '$m[1].$this->tag($m[2].(isset($m[3])? " " : "" ).$m[4].(isset($m[5])? " " : "" ).$m[6], "span", array("class" => "nowrap")).$m[7]', + '$m[1].$this->tag($m[2]." ".$m[4].(isset($m[5])? " " : "" ).$m[6], "span", array("class" => "nowrap")).$m[7]', + //'$m[1].$this->tag($m[2].". ".$m[4].". ".$m[8], "span", array("class" => "nowrap")).$m[9]', + //'$m[1].$this->tag($m[2]." ".$m[4].". ".$m[6].".", "span", array("class" => "nowrap")).$m[7]', + ), + ), + 'nbsp_before_particle' => array( + 'description' => 'Неразрывный пробел перед частицей', + 'pattern' => '/(\040|\t)+(ли|бы|б|же|ж)(\ \;|\.|\,|\:|\;|\&hellip\;|\?|\s)/iue', + 'replacement' => '" ".$m[2] . ($m[3] == " " ? " " : $m[3])' + ), + 'nbsp_v_kak_to' => array( + 'description' => 'Неразрывный пробел в как то', + 'pattern' => '/как то\:/ui', + 'replacement' => 'как то:' + ), + 'nbsp_celcius' => array( + 'description' => 'Привязка градусов к числу', + 'pattern' => '/(\s|^|\>|\ \;)(\d+)( |\ \;)?(°|\°\;)(C|С)(\s|\.|\!|\?|\,|$|\ \;|\;)/iu', + 'replacement' => '\1\2 \4C\6' + ), + 'hyphen_nowrap_in_small_words' => array( + 'description' => 'Обрамление пятисимвольных слов разделенных дефисом в неразрывные блоки', + 'disabled' => true, + 'cycled' => true, + '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', + 'replacement' => '$m[1] . $this->tag($m[2], "span", array("class"=>"nowrap")) . $m[4]', + ), + 'hyphen_nowrap' => array( + 'description' => 'Отмена переноса слова с дефисом', + 'disabled' => true, + 'cycled' => true, + 'pattern' => '/(\ \;|\s|\>|^)([a-zа-яё]+)((\-([a-zа-яё]+)){1,2})(\s|\.|\,|\!|\?|\ \;|\&hellip\;|$)/uie', + 'replacement' => '$m[1] . $this->tag($m[2].$m[3], "span", array("class"=>"nowrap")) . $m[6]' + ), + ); + + /** + * Объединение IP-адрессов в неразрывные конструкции (IPv4 only) + * + * @param unknown_type $triads + * @return unknown + */ + protected function nowrap_ip_address($triads) + { + $triad = explode('.', $triads); + $addTag = true; + + foreach ($triad as $value) { + $value = (int) $value; + if ($value > 255) { + $addTag = false; + break; + } + } + + if (true === $addTag) { + $triads = $this->tag($triads, 'span', array('class' => "nowrap")); + } + + return $triads; + } +} + + +/** + * @see EMT_Tret + */ + +class EMT_Tret_Number extends EMT_Tret +{ + public $title = "Числа, дроби, математические знаки"; + + + public $rules = array( + 'minus_between_nums' => array( + 'description' => 'Расстановка знака минус между числами', + 'pattern' => '/(\d+)\-(\d)/i', + 'replacement' => '\1−\2' + ), + 'minus_in_numbers_range' => array( + 'description' => 'Расстановка знака минус между диапозоном чисел', + 'pattern' => '/(^|\s|\ \;)(\&minus\;|\-)(\d+)(\.\.\.|\&hellip\;)(\s|\ \;)?(\+|\-|\&minus\;)?(\d+)/ie', + 'replacement' => '$m[1] ."−".$m[3] . $m[4].$m[5].($m[6]=="+"?$m[6]:"−").$m[7]' + ), + 'auto_times_x' => array( + 'description' => 'Замена x на символ × в размерных единицах', + 'cycled' => true, + 'pattern' => '/([^a-zA-Z><]|^)(\×\;)?(\d+)(\040*)(x|х)(\040*)(\d+)([^a-zA-Z><]|$)/u', + 'replacement' => '\1\2\3×\7\8' + ), + 'numeric_sub' => array( + 'description' => 'Нижний индекс', + 'pattern' => '/([a-zа-яё0-9])\_([\d]{1,3})([^@а-яёa-z0-9]|$)/ieu', + 'replacement' => '$m[1] . $this->tag($this->tag($m[2],"small"),"sub") . $m[3]' + ), + 'numeric_sup' => array( + 'description' => 'Верхний индекс', + 'pattern' => '/([a-zа-яё0-9])\^([\d]{1,3})([^а-яёa-z0-9]|$)/ieu', + 'replacement' => '$m[1] . $this->tag($this->tag($m[2],"small"),"sup") . $m[3]' + ), + 'simple_fraction' => array( + 'description' => 'Замена дробей 1/2, 1/4, 3/4 на соответствующие символы', + 'pattern' => array('/(^|\D)1\/(2|4)(\D)/', '/(^|\D)3\/4(\D)/'), + 'replacement' => array('\1&frac1\2;\3', '\1¾\2') + ), + 'math_chars' => array( + 'description' => 'Математические знаки больше/меньше/плюс минус/неравно', + 'pattern' => array('/!=/', '/\<=/', '/([^=]|^)\>=/', '/~=/', '/\+-/'), + 'replacement' => array('≠', '≤', '\1≥', '≅', '±' ) + ), + + 'thinsp_between_number_triads' => array( + 'description' => 'Объединение триад чисел полупробелом', + 'pattern' => '/([0-9]{1,3}( [0-9]{3}){1,})(.|$)/ue', + 'replacement' => '($m[3]=="-"? $m[0]:str_replace(" "," ",$m[1]).$m[3])' + ), + 'thinsp_between_no_and_number' => array( + 'description' => 'Пробел между симоволом номера и числом', + 'pattern' => '/(№|\№\;)(\s| )*(\d)/iu', + 'replacement' => '№ \3' + ), + 'thinsp_between_sect_and_number' => array( + 'description' => 'Пробел между параграфом и числом', + 'pattern' => '/(§|\§\;)(\s| )*(\d+|[IVX]+|[a-zа-яё]+)/ui', + 'replacement' => '§ \3' + ), + ); +} + + +/** + * @see EMT_Tret + */ + +class EMT_Tret_OptAlign extends EMT_Tret +{ + + public $classes = array( + 'oa_obracket_sp_s' => "margin-right:0.3em;", + "oa_obracket_sp_b" => "margin-left:-0.3em;", + "oa_obracket_nl_b" => "margin-left:-0.3em;", + "oa_comma_b" => "margin-right:-0.2em;", + "oa_comma_e" => "margin-left:0.2em;", + 'oa_oquote_nl' => "margin-left:-0.44em;", + 'oa_oqoute_sp_s' => "margin-right:0.44em;", + 'oa_oqoute_sp_q' => "margin-left:-0.44em;", + ); + + /** + * Базовые параметры тофа + * + * @var array + */ + public $title = "Оптическое выравнивание"; + public $rules = array( + 'oa_oquote' => array( + 'description' => 'Оптическое выравнивание открывающей кавычки', + //'disabled' => true, + 'pattern' => array( + '/([a-zа-яё\-]{3,})(\040|\ \;|\t)(\«\;)/uie', + '/(\n|\r|^)(\«\;)/ei' + ), + 'replacement' => array( + '$m[1] . $this->tag($m[2], "span", array("class"=>"oa_oqoute_sp_s")) . $this->tag($m[3], "span", array("class"=>"oa_oqoute_sp_q"))', + '$m[1] . $this->tag($m[2], "span", array("class"=>"oa_oquote_nl"))', + ), + ), + 'oa_oquote_extra' => array( + 'description' => 'Оптическое выравнивание кавычки', + //'disabled' => true, + 'function' => 'oaquote_extra' + ), + 'oa_obracket_coma' => array( + 'description' => 'Оптическое выравнивание для пунктуации (скобка)', + //'disabled' => true, + 'pattern' => array( + '/(\040|\ \;|\t)\(/ei', + '/(\n|\r|^)\(/ei', + //'/([а-яёa-z0-9]+)\,(\040+)/iue', + ), + 'replacement' => array( + '$this->tag($m[1], "span", array("class"=>"oa_obracket_sp_s")) . $this->tag("(", "span", array("class"=>"oa_obracket_sp_b"))', + '$m[1] . $this->tag("(", "span", array("class"=>"oa_obracket_nl_b"))', + //'$m[1] . $this->tag(",", "span", array("class"=>"oa_comma_b")) . $this->tag(" ", "span", array("class"=>"oa_comma_e"))', + ), + ), + + ); + + /** + * Если стоит открывающая кавычка после

надо делать её висячей + * + * @return void + */ + protected function oaquote_extra() + { + $this->_text = $this->preg_replace_e( + '/(<' .self::BASE64_PARAGRAPH_TAG . '>)([\040\t]+)?(\«\;)/e', + '$m[1] . $this->tag($m[3], "span", array("class"=>"oa_oquote_nl"))', + $this->_text); + } + + +} + + +/** + * @see EMT_Tret + */ + +class EMT_Tret_Punctmark extends EMT_Tret +{ + public $title = "Пунктуация и знаки препинания"; + + public $rules = array( + 'auto_comma' => array( + 'description' => 'Расстановка запятых перед а, но', + 'pattern' => '/([a-zа-яё])(\s| )(но|а)(\s| )/iu', + 'replacement' => '\1,\2\3\4' + ), + 'punctuation_marks_limit' => array( + 'description' => 'Лишние восклицательные, вопросительные знаки и точки', + 'pattern' => '/([\!\.\?]){4,}/', + 'replacement' => '\1\1\1' + ), + 'punctuation_marks_base_limit' => array( + 'description' => 'Лишние запятые, двоеточия, точки с запятой', + 'pattern' => '/([\,]|[\:]|[\;]]){2,}/', + 'replacement' => '\1' + ), + 'hellip' => array( + 'description' => 'Замена трех точек на знак многоточия', + 'simple_replace'=> true, + 'pattern' => '...', + 'replacement' => '…' + ), + 'fix_excl_quest_marks' => array( + 'description' => 'Замена восклицательного и вопросительного знаков местами', + 'pattern' => '/([a-zа-яё0-9])\!\?(\s|$|\<)/ui', + 'replacement' => '\1?!\2' + ), + 'fix_pmarks' => array( + 'description' => 'Замена сдвоенных знаков препинания на одинарные', + 'pattern' => array( + '/([^\!\?])\.\./', + '/([a-zа-яё0-9])(\!|\.)(\!|\.|\?)(\s|$|\<)/ui', + '/([a-zа-яё0-9])(\?)(\?)(\s|$|\<)/ui', + ), + 'replacement' => array( + '\1.', + '\1\2\4', + '\1\2\4' + ), + ), + 'fix_brackets' => array( + 'description' => 'Лишние пробелы после открывающей скобочки и перед закрывающей', + 'pattern' => array('/(\()(\040|\t)+/', '/(\040|\t)+(\))/'), + 'replacement' => array('\1', '\2') + ), + 'fix_brackets_space' => array( + 'description' => 'Пробел перед открывающей скобочкой', + 'pattern' => '/([a-zа-яё])(\()/iu', + 'replacement' => '\1 \2' + ), + 'dot_on_end' => array( + 'description' => 'Точка в конце текста, если её там нет', + 'disabled' => true, + 'pattern' => '/([a-zа-яё0-9])(\040|\t|\ \;)*$/ui', + //'pattern' => '/(([^\.\!\?])|(&(ra|ld)quo;))$/', + 'replacement' => '\1.' + ), + + ); +} + + +/** + * @see EMT_Tret + */ + +class EMT_Tret_Quote extends EMT_Tret +{ + /** + * Базовые параметры тофа + * + * @var array + */ + public $title = "Кавычки"; + + + public $rules = array( + 'quotes_outside_a' => array( + 'description' => 'Кавычки вне тэга ', + //'pattern' => '/(\<%%\_\_.+?\>)\"(.+?)\"(\<\/%%\_\_.+?\>)/s', + 'pattern' => '/(\<%%\_\_[^\>]+\>)\"(.+?)\"(\<\/%%\_\_[^\>]+\>)/s', + 'replacement' => '"\1\2\3"' + ), + + 'open_quote' => array( + 'description' => 'Открывающая кавычка', + 'pattern' => '/(^|\(|\s|\>|-)((\"|\\\")+)(\S+)/iue', + 'replacement' => '$m[1] . str_repeat(self::QUOTE_FIRS_OPEN, substr_count($m[2],"\"") ) . $m[4]' + ), + 'close_quote' => array( + 'description' => 'Закрывающая кавычка', + 'pattern' => '/([a-zа-яё0-9]|\.|\&hellip\;|\!|\?|\>|\)|\:|\+|\%|\@|\#|\$|\*)((\"|\\\")+)(\.|\&hellip\;|\;|\:|\?|\!|\,|\s|\)|\<\/|\<|$)/uie', + 'replacement' => '$m[1] . str_repeat(self::QUOTE_FIRS_CLOSE, substr_count($m[2],"\"") ) . $m[4]' + ), + 'close_quote_adv' => array( + 'description' => 'Закрывающая кавычка особые случаи', + //'pattern' => '/([a-zа-яё0-9]|\.|\&hellip\;|\!|\?|\>|\)|\:)((\"|\\\"|\«\;)+)(\<.+?\>)(\.|\&hellip\;|\;|\:|\?|\!|\,|\s|\)|\<\/|$)/uie', + 'pattern' => + array( + '/([a-zа-яё0-9]|\.|\&hellip\;|\!|\?|\>|\)|\:|\+|\%|\@|\#|\$|\*)((\"|\\\"|\«\;)+)(\<[^\>]+\>)(\.|\&hellip\;|\;|\:|\?|\!|\,|\)|\<\/|$| )/uie', + '/([a-zа-яё0-9]|\.|\&hellip\;|\!|\?|\>|\)|\:|\+|\%|\@|\#|\$|\*)(\s+)((\"|\\\")+)(\s+)(\.|\&hellip\;|\;|\:|\?|\!|\,|\)|\<\/|$| )/uie', + '/\>(\«\;)\.($|\s|\<)/ui', + '/\>(\«\;),($|\s|\<|\S)/ui', + '/\>(\«\;):($|\s|\<|\S)/ui', + '/\>(\«\;);($|\s|\<|\S)/ui', + '/\>(\«\;)\)($|\s|\<|\S)/ui', + '/((\"|\\\")+)$/uie', + ), + 'replacement' => + array( + '$m[1] . str_repeat(self::QUOTE_FIRS_CLOSE, substr_count($m[2],"\"")+substr_count($m[2],"«") ) . $m[4]. $m[5]', + '$m[1] .$m[2]. str_repeat(self::QUOTE_FIRS_CLOSE, substr_count($m[3],"\"")+substr_count($m[3],"«") ) . $m[5]. $m[6]', + '>».\2', + '>»,\2', + '>»:\2', + '>»;\2', + '>»)\2', + 'str_repeat(self::QUOTE_FIRS_CLOSE, substr_count($m[1],"\"") )', + ), + ), + 'open_quote_adv' => array( + 'description' => 'Открывающая кавычка особые случаи', + 'pattern' => '/(^|\(|\s|\>)(\"|\\\")(\s)(\S+)/iue', + 'replacement' => '$m[1] . self::QUOTE_FIRS_OPEN .$m[4]' + ), + 'close_quote_adv_2' => array( + 'description' => 'Закрывающая кавычка последний шанс', + 'pattern' => '/(\S)((\"|\\\")+)(\.|\&hellip\;|\;|\:|\?|\!|\,|\s|\)|\<\/|\<|$)/uie', + 'replacement' => '$m[1] . str_repeat(self::QUOTE_FIRS_CLOSE, substr_count($m[2],"\"") ) . $m[4]' + ), + 'quotation' => array( + 'description' => 'Внутренние кавычки-лапки и дюймы', + 'function' => 'build_sub_quotations' + ), + ); + + protected function inject_in($pos, $text, &$thetext) + { + for($i=0;$i_text, "")!==false ? "" : (strpos($this->_text,"\r\n")!==false ? "\r\n\r\n" :"\n\n"); + + $texts_in = explode($exp, $this->_text); + $texts_out = array(); + + foreach($texts_in as $textx) { + + $okposstack = array('0'); + $okpos = 0; + $level = 0; + $off = 0; + while(true) + { + $p = EMT_Lib::strpos_ex($textx, array("«", "»"), $off); + if($p===false) break; + if($p['str'] == "«") + { + if($level>0) if(!$this->is_on('no_bdquotes')) $this->inject_in($p['pos'], self::QUOTE_CRAWSE_OPEN, $textx); + $level++; + } + if($p['str'] == "»") + { + $level--; + if($level>0) if(!$this->is_on('no_bdquotes')) $this->inject_in($p['pos'], self::QUOTE_CRAWSE_CLOSE, $textx); + } + $off = $p['pos']+strlen($p['str']); + if($level == 0) + { + $okpos = $off; + array_push($okposstack, $okpos); + } elseif($level<0) // уровень стал меньше нуля + { + if(!$this->is_on('no_inches')) + { + do{ + $lokpos = array_pop($okposstack); + $k = substr($textx, $lokpos, $off-$lokpos); + $k = str_replace(self::QUOTE_CRAWSE_OPEN, self::QUOTE_FIRS_OPEN, $k); + $k = str_replace(self::QUOTE_CRAWSE_CLOSE, self::QUOTE_FIRS_CLOSE, $k); + //$k = preg_replace("/(^|[^0-9])([0-9]+)\»\;/ui", '\1\2″', $k, 1, $amount); + + $amount = 0; + $__ax = preg_match_all("/(^|[^0-9])([0-9]+)\»\;/ui", $k, $m); + $__ay = 0; + if($__ax) + { + $k = preg_replace_callback("/(^|[^0-9])([0-9]+)\»\;/ui", + create_function('$m','global $__ax,$__ay; $__ay++; if($__ay==$__ax){ return $m[1].$m[2]."″";} return $m[0];'), + $k); + $amount = 1; + } + + + + } while(($amount==0) && count($okposstack)); + + // успешно сделали замену + if($amount == 1) + { + // заново просмотрим содержимое + $textx = substr($textx, 0, $lokpos). $k . substr($textx, $off); + $off = $lokpos; + $level = 0; + continue; + } + + // иначе просто заменим последнюю явно на " от отчаяния + if($amount == 0) + { + // говорим, что всё в порядке + $level = 0; + $textx = substr($textx, 0, $p['pos']). '"' . substr($textx, $off); + $off = $p['pos'] + strlen('"'); + $okposstack = array($off); + continue; + } + } + } + + + } + // не совпало количество, отменяем все подкавычки + if($level != 0 ){ + + // закрывающих меньше, чем надо + if($level>0) + { + $k = substr($textx, $okpos); + $k = str_replace(self::QUOTE_CRAWSE_OPEN, self::QUOTE_FIRS_OPEN, $k); + $k = str_replace(self::QUOTE_CRAWSE_CLOSE, self::QUOTE_FIRS_CLOSE, $k); + $textx = substr($textx, 0, $okpos). $k; + } + } + $texts_out[] = $textx; + } + $this->_text = implode($exp, $texts_out); + } + +} + + + + + + + +/** + * @see EMT_Tret + */ + +class EMT_Tret_Space extends EMT_Tret +{ + public $title = "Расстановка и удаление пробелов"; + + public $domain_zones = array('ru','ру','ком','орг', 'уа', 'ua', 'uk', 'co', 'fr', + 'com', 'net', 'edu', 'gov', 'org', 'mil', 'int', 'info', 'biz', 'info', 'name', 'pro'); + + public $classes = array( + 'nowrap' => 'word-spacing:nowrap;', + ); + + public $rules = array( + 'nobr_twosym_abbr' => array( + 'description' => 'Неразрывный перед 2х символьной аббревиатурой', + 'pattern' => '/([a-zA-Zа-яёА-ЯЁ])(\040|\t)+([A-ZА-ЯЁ]{2})([\s\;\.\?\!\:\(\"]|\&(ra|ld)quo\;|$)/u', + 'replacement' => '\1 \3\4' + ), + 'remove_space_before_punctuationmarks' => array( + 'description' => 'Удаление пробела перед точкой, запятой, двоеточием, точкой с запятой', + 'pattern' => '/((\040|\t|\ \;)+)([\,\:\.\;\?])(\s+|$)/', + 'replacement' => '\3\4' + ), + 'autospace_after_comma' => array( + 'description' => 'Пробел после запятой', + 'pattern' => array( + '/(\040|\t|\ \;)\,([а-яёa-z0-9])/iu', + '/([^0-9])\,([а-яёa-z0-9])/iu', + ), + 'replacement' => array( + ', \2', + '\1, \2' + ), + ), + 'autospace_after_pmarks' => array( + 'description' => 'Пробел после знаков пунктуации, кроме точки', + 'pattern' => '/(\040|\t|\ \;|^|\n)([a-zа-яё0-9]+)(\040|\t|\ \;)?(\:|\)|\,|\&hellip\;|(?:\!|\?)+)([а-яёa-z])/iu', + 'replacement' => '\1\2\4 \5' + ), + 'autospace_after_dot' => array( + 'description' => 'Пробел после точки', + 'pattern' => array( + '/(\040|\t|\ \;|^)([a-zа-яё0-9]+)(\040|\t|\ \;)?\.([а-яёa-z]{5,})($|[^a-zа-яё])/iue', + '/(\040|\t|\ \;|^)([a-zа-яё0-9]+)\.([а-яёa-z]{1,4})($|[^a-zа-яё])/iue', + ), + 'replacement' => array( + //'\1\2. \4', + '$m[1].$m[2]."." .( $m[5] == "." ? "" : " ").$m[4].$m[5]', + '$m[1].$m[2]."." .(in_array(EMT_Lib::strtolower($m[3]), $this->domain_zones)? "":( $m[4] == "." ? "" : " ")). $m[3].$m[4]' + ), + ), + 'autospace_after_hellips' => array( + 'description' => 'Пробел после знаков троеточий с вопросительным или восклицательными знаками', + 'pattern' => '/([\?\!]\.\.)([а-яёa-z])/iu', + 'replacement' => '\1 \2' + ), + 'many_spaces_to_one' => array( + 'description' => 'Удаление лишних пробельных символов и табуляций', + 'pattern' => '/(\040|\t)+/', + 'replacement' => ' ' + ), + 'clear_percent' => array( + 'description' => 'Удаление пробела перед символом процента', + 'pattern' => '/(\d+)([\t\040]+)\%/', + 'replacement' => '\1%' + ), + 'nbsp_before_open_quote' => array( + 'description' => 'Неразрывный пробел перед открывающей скобкой', + 'pattern' => '/(^|\040|\t|>)([a-zа-яё]{1,2})\040(\«\;|\&bdquo\;)/u', + 'replacement' => '\1\2 \3' + ), + + 'nbsp_before_month' => array( + 'description' => 'Неразрывный пробел в датах перед числом и месяцем', + 'pattern' => '/(\d)(\s)+(января|февраля|марта|апреля|мая|июня|июля|августа|сентября|октября|ноября|декабря)([^\<]|$)/iu', + 'replacement' => '\1 \3\4' + ), + 'spaces_on_end' => array( + 'description' => 'Удаление пробелов в конце текста', + 'pattern' => '/ +$/', + 'replacement' => '' + ), + 'no_space_posle_hellip' => array( + 'description' => 'Отсутстввие пробела после троеточия после открывающей кавычки', + 'pattern' => '/(\«\;|\&bdquo\;)( |\ \;)?\&hellip\;( |\ \;)?([a-zа-яё])/ui', + 'replacement' => '\1…\4' + ), + 'space_posle_goda' => array( + 'description' => 'Пробел после года', + 'pattern' => '/(^|\040|\ \;)([0-9]{3,4})(год([ауе]|ом)?)([^a-zа-яё]|$)/ui', + 'replacement' => '\1\2 \3\5' + ), + ); +} + + +/** + * @see EMT_Tret + */ + +class EMT_Tret_Symbol extends EMT_Tret +{ + /** + * Базовые параметры тофа + * + * @var array + */ + public $classes = array( + 'nowrap' => 'word-spacing:nowrap;', + ); + + + public $title = "Специальные символы"; + public $rules = array( + 'tm_replace' => array( + 'description' => 'Замена (tm) на символ торговой марки', + 'pattern' => '/([\040\t])?\(tm\)/i', + 'replacement' => '™' + ), + 'r_sign_replace' => array( + 'description' => 'Замена (R) на символ зарегистрированной торговой марки', + 'pattern' => array( + '/(.|^)\(r\)(.|$)/ie', + //'/([^\>]|^)\(r\)([^\<]|$)/ie', + //'/\>\(r\)\ array( + //'$m[1].$this->tag("®", "sup").$m[2]', + '$m[1]."®".$m[2]', + //'>®<' + ), + ), + 'copy_replace' => array( + 'description' => 'Замена (c) на символ копирайт', + 'pattern' => array( + '/\((c|с)\)\s+/iu', + '/\((c|с)\)($|\.|,|!|\?)/iu', + ), + 'replacement' => array( + '© ', + '©\2', + ), + ), + 'apostrophe' => array( + 'description' => 'Расстановка правильного апострофа в текстах', + 'pattern' => '/(\s|^|\>|\&rsquo\;)([a-zа-яё]{1,})\'([a-zа-яё]+)/ui', + 'replacement' => '\1\2’\3', + 'cycled' => true + ), + /* + 'ru_apostrophe' => array( + 'description' => 'Расстановка правильного апострофа в русских текстах', + 'pattern' => '/(\s|^|\>)([а-яё]+)\'([а-яё]+)/iu', + 'replacement' => '\1\2’\3' + ), + */ + 'degree_f' => array( + 'description' => 'Градусы по Фаренгейту', + 'pattern' => '/([0-9]+)F($|\s|\.|\,|\;|\:|\ \;|\?|\!)/eu', + 'replacement' => '"".$this->tag($m[1]." °F","span", array("class"=>"nowrap")) .$m[2]' + ), + 'euro_symbol' => array( + 'description' => 'Символ евро', + 'simple_replace' => true, + 'pattern' => '€', + 'replacement' => '€' + ), + 'arrows_symbols' => array( + 'description' => 'Замена стрелок вправо-влево на html коды', + //'pattern' => array('/(\s|\>|\ \;|^)\-\>($|\s|\ \;|\<)/', '/(\s|\>|\ \;|^|;)\<\-(\s|\ \;|$|\<)/', '/→/u', '/←/u'), + //'pattern' => array('/\-\>($|\s|\ \;|\<)/', '/(\s|\>|\ \;|^|;)\<\-(\s|\ \;|$|\<)/', '/→/u', '/←/u'), + 'pattern' => array('/\-\>/', '/\<\-/', '/→/u', '/←/u'), + //'replacement' => array('\1→\2', '\1←\2', '→', '←' ), + 'replacement' => array('→', '←', '→', '←' ), + ), + ); +} + + +/** + * @see EMT_Tret + */ + +class EMT_Tret_Text extends EMT_Tret +{ + public $classes = array( + 'nowrap' => 'word-spacing:nowrap;', + ); + + /** + * Базовые параметры тофа + * + * @var array + */ + public $title = "Текст и абзацы"; + public $rules = array( + 'auto_links' => array( + 'description' => 'Выделение ссылок из текста', + 'pattern' => '/(\s|^)(http|ftp|mailto|https)(:\/\/)([^\s\,\!\<]{4,})(\s|\.|\,|\!|\?|\<|$)/ieu', + '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]' + ), + 'email' => array( + 'description' => 'Выделение эл. почты из текста', + 'pattern' => '/(\s|^|\ \;|\()([a-z0-9\-\_\.]{2,})\@([a-z0-9\-\.]{2,})\.([a-z]{2,6})(\)|\s|\.|\,|\!|\?|$|\<)/e', + 'replacement' => '$m[1] . $this->tag($m[2]."@".$m[3].".".$m[4], "a", array("href" => "mailto:".$m[2]."@".$m[3].".".$m[4])) . $m[5]' + ), + 'no_repeat_words' => array( + 'description' => 'Удаление повторяющихся слов', + 'disabled' => true, + 'pattern' => array( + '/([а-яё]{3,})( |\t|\ \;)\1/iu', + '/(\s|\ \;|^|\.|\!|\?)(([А-ЯЁ])([а-яё]{2,}))( |\t|\ \;)(([а-яё])\4)/eu', + ), + 'replacement' => array( + '\1', + '$m[1].($m[7] === EMT_Lib::strtolower($m[3]) ? $m[2] : $m[2].$m[5].$m[6] )', + ) + ), + 'paragraphs' => array( + 'description' => 'Простановка параграфов', + 'function' => 'build_paragraphs' + ), + 'breakline' => array( + 'description' => 'Простановка переносов строк', + 'function' => 'build_brs' + ), + + ); + + /** + * Расстановка защищенных тегов параграфа (

...

) и переноса строки + * + * @return void + */ + protected function do_paragraphs($text) { + $text = str_replace("\r\n","\n",$text); + $text = str_replace("\r","\n",$text); + $text = '<' . self::BASE64_PARAGRAPH_TAG . '>' . trim($text) . ''; + //$text = $this->preg_replace_e('/([\040\t]+)?(\n|\r){2,}/e', '"<" .self::BASE64_PARAGRAPH_TAG . ">"', $text); + //$text = $this->preg_replace_e('/([\040\t]+)?(\n){2,}/e', '"<" .self::BASE64_PARAGRAPH_TAG . ">"', $text); + $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); + //$text = $this->preg_replace_e('/([\040\t]+)?(\n)+([\040\t]*)(\n)+/e', '""."<" .self::BASE64_PARAGRAPH_TAG . ">"', $text); + //может от открвающего до закрывающего ?! + $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); + return $text; + } + + /** + * Расстановка защищенных тегов параграфа (

...

) и переноса строки + * + * @return void + */ + protected function build_paragraphs() + { + $r = mb_strpos($this->_text, '<' . self::BASE64_PARAGRAPH_TAG . '>' ); + $p = EMT_Lib::rstrpos($this->_text, '' ) ; + if(($r!== false) && ($p !== false)) { + + $beg = mb_substr($this->_text,0,$r); + $end = mb_substr($this->_text,$p+mb_strlen('')); + $this->_text = + (trim($beg) ? $this->do_paragraphs($beg). "\n":"") .'<' . self::BASE64_PARAGRAPH_TAG . '>'. + mb_substr($this->_text,$r + mb_strlen('<' . self::BASE64_PARAGRAPH_TAG . '>'),$p -($r + mb_strlen('<' . self::BASE64_PARAGRAPH_TAG . '>')) ).''. + (trim($end) ? "\n".$this->do_paragraphs($end) :"") ; + } else { + $this->_text = $this->do_paragraphs($this->_text); + } + } + + /** + * Расстановка защищенных тегов параграфа (

...

) и переноса строки + * + * @return void + */ + protected function build_brs() + { + $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); + + if (!preg_match('/\<' . self::BASE64_BREAKLINE_TAG . '\>/', $this->_text)) { + $this->_text = str_replace("\r\n","\n",$this->_text); + $this->_text = str_replace("\r","\n",$this->_text); + //$this->_text = $this->preg_replace_e('/(\n|\r)/e', '"<" . self::BASE64_BREAKLINE_TAG . ">"', $this->_text); + $this->_text = $this->preg_replace_e('/(\n)/e', '"<" . self::BASE64_BREAKLINE_TAG . ">\n"', $this->_text); + } + } +} + + +/** +* Evgeny Muravjev Typograph, http://mdash.ru +* Version: 3.5 Gold Master +* Release Date: July 2, 2015 +* Authors: Evgeny Muravjev & Alexander Drutsa +*/ + + + +/** + * Основной класс типографа Евгения Муравьёва + * реализует основные методы запуска и работы типографа + * + */ +class EMT_Base +{ + private $_text = ""; + private $inited = false; + + /** + * Список Трэтов, которые надо применить к типографированию + * + * @var array + */ + protected $trets = array() ; + protected $trets_index = array() ; + protected $tret_objects = array() ; + + public $ok = false; + public $debug_enabled = false; + public $logging = false; + public $logs = array(); + public $errors = array(); + public $debug_info = array(); + + private $use_layout = false; + private $class_layout_prefix = false; + private $use_layout_set = false; + public $disable_notg_replace = false; + public $remove_notg = false; + + public $settings = array(); + + protected function log($str, $data = null) + { + if(!$this->logging) return; + $this->logs[] = array('class' => '', 'info' => $str, 'data' => $data); + } + + protected function tret_log($tret, $str, $data = null) + { + $this->logs[] = array('class' => $tret, 'info' => $str, 'data' => $data); + } + + protected function error($info, $data = null) + { + $this->errors[] = array('class' => '', 'info' => $info, 'data' => $data); + $this->log("ERROR $info", $data ); + } + + protected function tret_error($tret, $info, $data = null) + { + $this->errors[] = array('class' => $tret, 'info' => $info, 'data' => $data); + } + + protected function debug($class, $place, &$after_text, $after_text_raw = "") + { + if(!$this->debug_enabled) return; + $this->debug_info[] = array( + 'tret' => $class == $this ? false: true, + 'class' => is_object($class)? get_class($class) : $class, + 'place' => $place, + 'text' => $after_text, + 'text_raw' => $after_text_raw, + ); + } + + + + protected $_safe_blocks = array(); + + + /** + * Включить режим отладки, чтобы посмотреть последовательность вызовов + * третов и правил после + * + */ + public function debug_on() + { + $this->debug_enabled = true; + } + + /** + * Включить режим отладки, чтобы посмотреть последовательность вызовов + * третов и правил после + * + */ + public function log_on() + { + $this->logging = true; + } + + /** + * Добавление защищенного блока + * + * + * Jare_Typograph_Tool::addCustomBlocks('', ''); + * Jare_Typograph_Tool::addCustomBlocks('\', '\<\/span\>', true); + * + * + * @param string $id идентификатор + * @param string $open начало блока + * @param string $close конец защищенного блока + * @param string $tag тэг + * @return void + */ + private function _add_safe_block($id, $open, $close, $tag) + { + $this->_safe_blocks[] = array( + 'id' => $id, + 'tag' => $tag, + 'open' => $open, + 'close' => $close, + ); + } + + /** + * Список защищенных блоков + * + * @return array + */ + public function get_all_safe_blocks() + { + return $this->_safe_blocks; + } + + /** + * Удаленного блока по его номеру ключа + * + * @param string $id идентифиактор защищённого блока + * @return void + */ + public function remove_safe_block($id) + { + foreach($this->_safe_blocks as $k => $block) { + if($block['id']==$id) unset($this->_safe_blocks[$k]); + } + } + + + /** + * Добавление защищенного блока + * + * @param string $tag тэг, который должен быть защищён + * @return void + */ + public function add_safe_tag($tag) + { + $open = preg_quote("<", '/'). $tag."[^>]*?" . preg_quote(">", '/'); + $close = preg_quote("", '/'); + $this->_add_safe_block($tag, $open, $close, $tag); + return true; + } + + + /** + * Добавление защищенного блока + * + * @param string $open начало блока + * @param string $close конец защищенного блока + * @param bool $quoted специальные символы в начале и конце блока экранированы + * @return void + */ + public function add_safe_block($id, $open, $close, $quoted = false) + { + $open = trim($open); + $close = trim($close); + + if (empty($open) || empty($close)) + { + return false; + } + + if (false === $quoted) + { + $open = preg_quote($open, '/'); + $close = preg_quote($close, '/'); + } + + $this->_add_safe_block($id, $open, $close, ""); + return true; + } + + + /** + * Сохранение содержимого защищенных блоков + * + * @param string $text + * @param bool $safe если true, то содержимое блоков будет сохранено, иначе - раскодировано. + * @return string + */ + public function safe_blocks($text, $way, $show = true) + { + if (count($this->_safe_blocks)) + { + $safeType = true === $way ? "EMT_Lib::encrypt_tag(\$m[2])" : "stripslashes(EMT_Lib::decrypt_tag(\$m[2]))"; + $safeblocks = true === $way ? $this->_safe_blocks : array_reverse($this->_safe_blocks); + foreach ($safeblocks as $block) + { + $text = preg_replace_callback("/({$block['open']})(.+?)({$block['close']})/s", create_function('$m','return $m[1].'.$safeType . '.$m[3];') , $text); + } + } + + return $text; + } + + + /** + * Декодирование блоков, которые были скрыты в момент типографирования + * + * @param string $text + * @return string + */ + public function decode_internal_blocks($text) + { + return EMT_Lib::decode_internal_blocks($text); + } + + + private function create_object($tret) + { + // если класса нету, попытаемся его прогрузить, например, если стандартный + if(!class_exists($tret)) + { + if(preg_match("/^EMT_Tret_([a-zA-Z0-9_]+)$/",$tret, $m)) + { + $tname = $m[1]; + $fname = str_replace("_"," ",$tname); + $fname = ucwords($fname); + $fname = str_replace(" ",".",$fname); + //if(file_exists("EMT.Tret.".$fname.".php")) + { + } + } + } + if(!class_exists($tret)) + { + $this->error("Класс $tret не найден. Пожалуйста, подргузите нужный файл."); + return null; + } + + $obj = new $tret(); + $obj->EMT = $this; + $obj->logging = $this->logging; + return $obj; + } + + private function get_short_tret($tretname) + { + if(preg_match("/^EMT_Tret_([a-zA-Z0-9_]+)$/",$tretname, $m)) + { + return $m[1]; + } + return $tretname; + } + + private function _init() + { + foreach($this->trets as $tret) + { + if(isset($this->tret_objects[$tret])) continue; + $obj = $this->create_object($tret); + if($obj == null) continue; + $this->tret_objects[$tret] = $obj; + } + + if(!$this->inited) + { + $this->add_safe_tag('pre'); + $this->add_safe_tag('script'); + $this->add_safe_tag('style'); + $this->add_safe_tag('notg'); + $this->add_safe_block('span-notg', '', ''); + } + $this->inited = true; + } + + + + + + /** + * Инициализация класса, используется чтобы задать список третов или + * список защищённых блоков, которые можно использовать. + * Также здесь можно отменить защищённые блоки по умлочнаию + * + */ + public function init() + { + + } + + /** + * Добавить Трэт, + * + * @param mixed $class - имя класса трета, или сам объект + * @param string $altname - альтернативное имя, если хотим например иметь два одинаоковых терта в обработке + * @return unknown + */ + public function add_tret($class, $altname = false) + { + if(is_object($class)) + { + if(!is_a($class, "EMT_Tret")) + { + $this->error("You are adding Tret that doesn't inherit base class EMT_Tret", get_class($class)); + return false; + } + + $class->EMT = $this; + $class->logging = $this->logging; + $this->tret_objects[($altname ? $altname : get_class($class))] = $class; + $this->trets[] = ($altname ? $altname : get_class($class)); + return true; + } + if(is_string($class)) + { + $obj = $this->create_object($class); + if($obj === null) + return false; + $this->tret_objects[($altname ? $altname : $class)] = $obj; + $this->trets[] = ($altname ? $altname : $class); + return true; + } + $this->error("Чтобы добавить трэт необходимо передать имя или объект"); + return false; + } + + /** + * Получаем ТРЕТ по идентификатору, т.е. названию класса + * + * @param unknown_type $name + */ + public function get_tret($name) + { + if(isset($this->tret_objects[$name])) return $this->tret_objects[$name]; + foreach($this->trets as $tret) + { + if($tret == $name) + { + $this->_init(); + return $this->tret_objects[$name]; + } + if($this->get_short_tret($tret) == $name) + { + $this->_init(); + return $this->tret_objects[$tret]; + } + } + $this->error("Трэт с идентификатором $name не найден"); + return false; + } + + /** + * Задаём текст для применения типографа + * + * @param string $text + */ + public function set_text($text) + { + $this->_text = $text; + } + + + + /** + * Запустить типограф на выполнение + * + */ + public function apply($trets = null) + { + $this->ok = false; + + $this->init(); + $this->_init(); + + $atrets = $this->trets; + if(is_string($trets)) $atrets = array($trets); + elseif(is_array($trets)) $atrets = $trets; + + $this->debug($this, 'init', $this->_text); + + $this->_text = $this->safe_blocks($this->_text, true); + $this->debug($this, 'safe_blocks', $this->_text); + + $this->_text = EMT_Lib::safe_tag_chars($this->_text, true); + $this->debug($this, 'safe_tag_chars', $this->_text); + + $this->_text = EMT_Lib::clear_special_chars($this->_text); + $this->debug($this, 'clear_special_chars', $this->_text); + + foreach ($atrets as $tret) + { + // если установлен режим разметки тэгов то выставим его + if($this->use_layout_set) + $this->tret_objects[$tret]->set_tag_layout_ifnotset($this->use_layout); + + if($this->class_layout_prefix) + $this->tret_objects[$tret]->set_class_layout_prefix($this->class_layout_prefix); + + // влючаем, если нужно + if($this->debug_enabled) $this->tret_objects[$tret]->debug_on(); + if($this->logging) $this->tret_objects[$tret]->logging = true; + + // применяем трэт + //$this->tret_objects[$tret]->set_text(&$this->_text); + $this->tret_objects[$tret]->set_text($this->_text); + $this->tret_objects[$tret]->apply(); + + // соберём ошибки если таковые есть + if(count($this->tret_objects[$tret]->errors)>0) + foreach($this->tret_objects[$tret]->errors as $err ) + $this->tret_error($tret, $err['info'], $err['data']); + + // логгирование + if($this->logging) + if(count($this->tret_objects[$tret]->logs)>0) + foreach($this->tret_objects[$tret]->logs as $log ) + $this->tret_log($tret, $log['info'], $log['data']); + + // отладка + if($this->debug_enabled) + foreach($this->tret_objects[$tret]->debug_info as $di) + { + $unsafetext = $di['text']; + $unsafetext = EMT_Lib::safe_tag_chars($unsafetext, false); + $unsafetext = $this->safe_blocks($unsafetext, false); + $this->debug($tret, $di['place'], $unsafetext, $di['text']); + } + + + } + + + $this->_text = $this->decode_internal_blocks($this->_text); + $this->debug($this, 'decode_internal_blocks', $this->_text); + + if($this->is_on('dounicode')) + { + EMT_Lib::convert_html_entities_to_unicode($this->_text); + } + + $this->_text = EMT_Lib::safe_tag_chars($this->_text, false); + $this->debug($this, 'unsafe_tag_chars', $this->_text); + + $this->_text = $this->safe_blocks($this->_text, false); + $this->debug($this, 'unsafe_blocks', $this->_text); + + if(!$this->disable_notg_replace) + { + $repl = array('', ''); + if($this->remove_notg) $repl = ""; + $this->_text = str_replace( array('',''), $repl , $this->_text); + } + $this->_text = trim($this->_text); + $this->ok = (count($this->errors)==0); + return $this->_text; + } + + /** + * Получить содержимое при использовании классов + * + * @param bool $list false - вернуть в виде строки для style или как массив + * @param bool $compact не выводить пустые классы + * @return string|array + */ + public function get_style($list = false, $compact = false) + { + $this->_init(); + + $res = array(); + foreach ($this->trets as $tret) + { + $arr =$this->tret_objects[$tret]->classes; + if(!is_array($arr)) continue; + foreach($arr as $classname => $str) + { + if(($compact) && (!$str)) continue; + $clsname = ($this->class_layout_prefix ? $this->class_layout_prefix : "" ).(isset($this->tret_objects[$tret]->class_names[$classname]) ? $this->tret_objects[$tret]->class_names[$classname] :$classname); + $res[$clsname] = $str; + } + } + if($list) return $res; + $str = ""; + foreach($res as $k => $v) + { + $str .= ".$k { $v }\n"; + } + return $str; + } + + + + + + /** + * Установить режим разметки, + * EMT_Lib::LAYOUT_STYLE - с помощью стилей + * EMT_Lib::LAYOUT_CLASS - с помощью классов + * EMT_Lib::LAYOUT_STYLE|EMT_Lib::LAYOUT_CLASS - оба метода + * + * @param int $layout + */ + public function set_tag_layout($layout = EMT_Lib::LAYOUT_STYLE) + { + $this->use_layout = $layout; + $this->use_layout_set = true; + } + + /** + * Установить префикс для классов + * + * @param string|bool $prefix если true то префикс 'emt_', иначе то, что передали + */ + public function set_class_layout_prefix($prefix ) + { + $this->class_layout_prefix = $prefix === true ? "emt_" : $prefix; + } + + /** + * Включить/отключить правила, согласно карте + * Формат карты: + * 'Название трэта 1' => array ( 'правило1', 'правило2' , ... ) + * 'Название трэта 2' => array ( 'правило1', 'правило2' , ... ) + * + * @param array $map + * @param boolean $disable если ложно, то $map соотвествует тем правилам, которые надо включить + * иначе это список правил, которые надо выключить + * @param boolean $strict строго, т.е. те которые не в списке будут тоже обработаны + */ + public function set_enable_map($map, $disable = false, $strict = true) + { + if(!is_array($map)) return; + $trets = array(); + foreach($map as $tret => $list) + { + $tretx = $this->get_tret($tret); + if(!$tretx) + { + $this->log("Трэт $tret не найден при применении карты включаемых правил"); + continue; + } + $trets[] = $tretx; + + if($list === true) // все + { + $tretx->activate(array(), !$disable , true); + } elseif(is_string($list)) { + $tretx->activate(array($list), $disable , $strict); + } elseif(is_array($list)) { + $tretx->activate($list, $disable , $strict); + } + } + if($strict) + { + foreach($this->trets as $tret) + { + if(in_array($this->tret_objects[$tret], $trets)) continue; + $this->tret_objects[$tret]->activate(array(), $disable , true); + } + } + + } + + + /** + * Установлена ли настройка + * + * @param string $key + */ + public function is_on($key) + { + if(!isset($this->settings[$key])) return false; + $kk = $this->settings[$key]; + return ((strtolower($kk)=="on") || ($kk === "1") || ($kk === true) || ($kk === 1)); + } + + + /** + * Установить настройку + * + * @param mixed $selector + * @param string $setting + * @param mixed $value + */ + protected function doset($selector, $key, $value) + { + $tret_pattern = false; + $rule_pattern = false; + //if(($selector === false) || ($selector === null) || ($selector === false) || ($selector === "*")) $type = 0; + if(is_string($selector)) + { + if(strpos($selector,".")===false) + { + $tret_pattern = $selector; + } else { + $pa = explode(".", $selector); + $tret_pattern = $pa[0]; + array_shift($pa); + $rule_pattern = implode(".", $pa); + } + } + EMT_Lib::_process_selector_pattern($tret_pattern); + EMT_Lib::_process_selector_pattern($rule_pattern); + if($selector == "*") $this->settings[$key] = $value; + + foreach ($this->trets as $tret) + { + $t1 = $this->get_short_tret($tret); + if(!EMT_Lib::_test_pattern($tret_pattern, $t1)) if(!EMT_Lib::_test_pattern($tret_pattern, $tret)) continue; + $tret_obj = $this->get_tret($tret); + if($key == "active") + { + foreach($tret_obj->rules as $rulename => $v) + { + if(!EMT_Lib::_test_pattern($rule_pattern, $rulename)) continue; + if((strtolower($value) === "on") || ($value===1) || ($value === true) || ($value=="1")) $tret_obj->enable_rule($rulename); + if((strtolower($value) === "off") || ($value===0) || ($value === false) || ($value=="0")) $tret_obj->disable_rule($rulename); + } + } else { + if($rule_pattern===false) + { + $tret_obj->set($key, $value); + } else { + foreach($tret_obj->rules as $rulename => $v) + { + if(!EMT_Lib::_test_pattern($rule_pattern, $rulename)) continue; + $tret_obj->set_rule($rulename, $key, $value); + } + } + } + } + } + + + /** + * Установить настройки для тертов и правил + * 1. если селектор является массивом, то тогда установка правил будет выполнена для каждого + * элемента этого массива, как отдельного селектора. + * 2. Если $key не является массивом, то эта настройка будет проставлена согласно селектору + * 3. Если $key массив - то будет задана группа настроек + * - если $value массив , то настройки определяются по ключам из массива $key, а значения из $value + * - иначе, $key содержит ключ-значение как массив + * 4. $exact_match - если true тогда array selector будет соответсвовать array $key, а не произведению массивов + * + * @param mixed $selector + * @param mixed $key + * @param mixed $value + * @param mixed $exact_match + */ + public function set($selector, $key , $value = false, $exact_match = false) + { + if($exact_match && is_array($selector) && is_array($key) && count($selector)==count($key)) { + $idx = 0; + foreach($key as $x => $y){ + if(is_array($value)) + { + $kk = $y; + $vv = $value[$x]; + } else { + $kk = ( $value ? $y : $x ); + $vv = ( $value ? $value : $y ); + } + $this->set($selector[$idx], $kk , $vv); + $idx++; + } + return ; + } + if(is_array($selector)) + { + foreach($selector as $val) $this->set($val, $key, $value); + return; + } + if(is_array($key)) + { + foreach($key as $x => $y) + { + if(is_array($value)) + { + $kk = $y; + $vv = $value[$x]; + } else { + $kk = ( $value ? $y : $x ); + $vv = ( $value ? $value : $y ); + } + $this->set($selector, $kk, $vv); + } + return ; + } + $this->doset($selector, $key, $value); + } + + + /** + * Возвращает список текущих третов, которые установлены + * + */ + public function get_trets_list() + { + return $this->trets; + } + + /** + * Установка одной метанастройки + * + * @param string $name + * @param mixed $value + */ + public function do_setup($name, $value) + { + + } + + + /** + * Установить настройки + * + * @param array $setupmap + */ + public function setup($setupmap) + { + if(!is_array($setupmap)) return; + + if(isset($setupmap['map']) || isset($setupmap['maps'])) + { + if(isset($setupmap['map'])) + { + $ret['map'] = $test['params']['map']; + $ret['disable'] = $test['params']['map_disable']; + $ret['strict'] = $test['params']['map_strict']; + $test['params']['maps'] = array($ret); + unset($setupmap['map']); + unset($setupmap['map_disable']); + unset($setupmap['map_strict']); + } + if(is_array($setupmap['maps'])) + { + foreach($setupmap['maps'] as $map) + { + $this->set_enable_map + ($map['map'], + isset($map['disable']) ? $map['disable'] : false, + isset($map['strict']) ? $map['strict'] : false + ); + } + } + unset($setupmap['maps']); + } + + + foreach($setupmap as $k => $v) $this->do_setup($k , $v); + } + + + + +} + + +class EMTypograph extends EMT_Base +{ + public $trets = array('EMT_Tret_Quote', 'EMT_Tret_Dash', 'EMT_Tret_Symbol', 'EMT_Tret_Punctmark', 'EMT_Tret_Number', 'EMT_Tret_Space', 'EMT_Tret_Abbr', 'EMT_Tret_Nobr', 'EMT_Tret_Date', 'EMT_Tret_OptAlign', 'EMT_Tret_Etc', 'EMT_Tret_Text'); + + + protected $group_list = array( + 'Quote' => true, + 'Dash' => true, + 'Nobr' => true, + 'Symbol' => true, + 'Punctmark' => true, + 'Number' => true, + 'Date' => true, + 'Space' => true, + 'Abbr' => true, + 'OptAlign' => true, + 'Text' => true, + 'Etc' => true, + ); + protected $all_options = array( + + 'Quote.quotes' => array( 'description' => 'Расстановка «кавычек-елочек» первого уровня', 'selector' => "Quote.*quote" ), + 'Quote.quotation' => array( 'description' => 'Внутренние кавычки-лапки', 'selector' => "Quote", 'setting' => 'no_bdquotes', 'reversed' => true ), + + 'Dash.to_libo_nibud' => 'direct', + 'Dash.iz_za_pod' => 'direct', + 'Dash.ka_de_kas' => 'direct', + + 'Nobr.super_nbsp' => 'direct', + 'Nobr.nbsp_in_the_end' => 'direct', + 'Nobr.phone_builder' => 'direct', + 'Nobr.phone_builder_v2' => 'direct', + 'Nobr.ip_address' => 'direct', + 'Nobr.spaces_nobr_in_surname_abbr' => 'direct', + 'Nobr.dots_for_surname_abbr' => 'direct', + 'Nobr.nbsp_celcius' => 'direct', + 'Nobr.hyphen_nowrap_in_small_words' => 'direct', + 'Nobr.hyphen_nowrap' => 'direct', + 'Nobr.nowrap' => array('description' => 'Nobr (по умолчанию) & nowrap', 'disabled' => true, 'selector' => '*', 'setting' => 'nowrap' ), + + 'Symbol.tm_replace' => 'direct', + 'Symbol.r_sign_replace' => 'direct', + 'Symbol.copy_replace' => 'direct', + 'Symbol.apostrophe' => 'direct', + 'Symbol.degree_f' => 'direct', + 'Symbol.arrows_symbols' => 'direct', + 'Symbol.no_inches' => array( 'description' => 'Расстановка дюйма после числа', 'selector' => "Quote", 'setting' => 'no_inches', 'reversed' => true ), + + 'Punctmark.auto_comma' => 'direct', + 'Punctmark.hellip' => 'direct', + 'Punctmark.fix_pmarks' => 'direct', + 'Punctmark.fix_excl_quest_marks' => 'direct', + 'Punctmark.dot_on_end' => 'direct', + + 'Number.minus_between_nums' => 'direct', + 'Number.minus_in_numbers_range' => 'direct', + 'Number.auto_times_x' => 'direct', + 'Number.simple_fraction' => 'direct', + 'Number.math_chars' => 'direct', + 'Number.thinsp_between_number_triads' => 'direct', + 'Number.thinsp_between_no_and_number' => 'direct', + 'Number.thinsp_between_sect_and_number' => 'direct', + + 'Date.years' => 'direct', + 'Date.mdash_month_interval' => 'direct', + 'Date.nbsp_and_dash_month_interval' => 'direct', + 'Date.nobr_year_in_date' => 'direct', + + 'Space.many_spaces_to_one' => 'direct', + 'Space.clear_percent' => 'direct', + 'Space.clear_before_after_punct' => array( 'description' => 'Удаление пробелов перед и после знаков препинания в предложении', 'selector' => 'Space.remove_space_before_punctuationmarks'), + 'Space.autospace_after' => array( 'description' => 'Расстановка пробелов после знаков препинания', 'selector' => 'Space.autospace_after_*'), + 'Space.bracket_fix' => array( 'description' => 'Удаление пробелов внутри скобок, а также расстановка пробела перед скобками', + 'selector' => array('Space.nbsp_before_open_quote', 'Punctmark.fix_brackets')), + + 'Abbr.nbsp_money_abbr' => array( 'description' => 'Форматирование денежных сокращений (расстановка пробелов и привязка названия валюты к числу)', + 'selector' => array('Abbr.nbsp_money_abbr', 'Abbr.nbsp_money_abbr_rev')), + 'Abbr.nobr_vtch_itd_itp' => 'direct', + 'Abbr.nobr_sm_im' => 'direct', + 'Abbr.nobr_acronym' => 'direct', + 'Abbr.nobr_locations' => 'direct', + 'Abbr.nobr_abbreviation' => 'direct', + 'Abbr.ps_pps' => 'direct', + 'Abbr.nbsp_org_abbr' => 'direct', + 'Abbr.nobr_gost' => 'direct', + 'Abbr.nobr_before_unit_volt' => 'direct', + 'Abbr.nbsp_before_unit' => 'direct', + + 'OptAlign.all' => array( 'description' => 'Все настройки оптического выравнивания', 'hide' => true, 'selector' => 'OptAlign.*'), + 'OptAlign.oa_oquote' => 'direct', + 'OptAlign.oa_obracket_coma' => 'direct', + 'OptAlign.oa_oquote_extra' => 'direct', + 'OptAlign.layout' => array( 'description' => 'Inline стили или CSS' ), + + 'Text.paragraphs' => 'direct', + 'Text.auto_links' => 'direct', + 'Text.email' => 'direct', + 'Text.breakline' => 'direct', + 'Text.no_repeat_words' => 'direct', + + + //'Etc.no_nbsp_in_nobr' => 'direct', + 'Etc.unicode_convert' => array('description' => 'Преобразовывать html-сущности в юникод', 'selector' => array('*', 'Etc.nobr_to_nbsp'), 'setting' => array('dounicode','active'), 'exact_selector' => true ,'disabled' => true), + 'Etc.nobr_to_nbsp' => 'direct', + 'Etc.split_number_to_triads' => 'direct', + + ); + + /** + * Получить список имеющихся опций + * + * @return array + * all - полный список + * group - сгруппированный по группам + */ + public function get_options_list() + { + $arr['all'] = array(); + $bygroup = array(); + foreach($this->all_options as $opt => $op) + { + $arr['all'][$opt] = $this->get_option_info($opt); + $x = explode(".",$opt); + $bygroup[$x[0]][] = $opt; + } + $arr['group'] = array(); + foreach($this->group_list as $group => $ginfo) + { + if($ginfo === true) + { + $tret = $this->get_tret($group); + if($tret) $info['title'] = $tret->title; else $info['title'] = "Не определено"; + } else { + $info = $ginfo; + } + $info['name'] = $group; + $info['options'] = array(); + if(is_array($bygroup[$group])) foreach($bygroup[$group] as $opt) $info['options'][] = $opt; + $arr['group'][] = $info; + } + return $arr; + } + + + /** + * Получить информацию о настройке + * + * @param string $key + * @return array|false + */ + protected function get_option_info($key) + { + if(!isset($this->all_options[$key])) return false; + if(is_array($this->all_options[$key])) return $this->all_options[$key]; + + if(($this->all_options[$key] == "direct") || ($this->all_options[$key] == "reverse")) + { + $pa = explode(".", $key); + $tret_pattern = $pa[0]; + $tret = $this->get_tret($tret_pattern); + if(!$tret) return false; + if(!isset($tret->rules[$pa[1]])) return false; + $array = $tret->rules[$pa[1]]; + $array['way'] = $this->all_options[$key]; + return $array; + } + return false; + } + + + /** + * Установка одной метанастройки + * + * @param string $name + * @param mixed $value + */ + public function do_setup($name, $value) + { + if(!isset($this->all_options[$name])) return; + + // эта настрока связана с правилом ядра + if(is_string($this->all_options[$name])) + { + $this->set($name, "active", $value ); + return ; + } + if(is_array($this->all_options[$name])) + { + if(isset($this->all_options[$name]['selector'])) + { + $settingname = "active"; + if(isset($this->all_options[$name]['setting'])) $settingname = $this->all_options[$name]['setting']; + $this->set($this->all_options[$name]['selector'], $settingname, $value, isset($this->all_options[$name]['exact_selector'])); + } + } + + if($name == "OptAlign.layout") + { + if($value == "style") $this->set_tag_layout(EMT_Lib::LAYOUT_STYLE); + if($value == "class") $this->set_tag_layout(EMT_Lib::LAYOUT_CLASS); + } + + } + + /** + * Запустить типограф со стандартными параметрами + * + * @param string $text + * @param array $options + * @return string + */ + public static function fast_apply($text, $options = null) + { + $obj = new self(); + if(is_array($options)) $obj->setup($options); + $obj->set_text($text); + return $obj->apply(); + } +} + + ?> \ No newline at end of file diff --git a/ddTypograph.php b/ddTypograph.php index 8e234db..65f495a 100644 --- a/ddTypograph.php +++ b/ddTypograph.php @@ -1,24 +1,25 @@ = 5.4. + * @uses MODXEvo.libraries.ddTools >= 0.21 {@link http://code.divandesign.biz/modx/ddtools }. * @uses EMT lib 3.5 (contains in archive). * * @param $text {string} — Text to correct. @required - * @param $optAlign {0 | 1} — Optical alignment (hanging punctuation). Default: 0. - * @param $text_paragraphs {0 | 1} — Section signs and line breaks insertion. Default: 0. - * @param $text_autoLinks {0 | 1} — Marking links (including email ones). Default: 0. - * @param $etc_unicodeConvert {0 | 1} — Convert html entities into Unicode (“—” instead of “—” etc.). Default: 1. - * @param $noTags {0 | 1} — Whether HTML element insertion is allowed or not. There are cases when using tags causes the text to be invalid, for example, using the snippet inside of an HTML attribute. Default: 0. + * @param $optAlign {0|1} — Optical alignment (hanging punctuation). Default: 0. + * @param $text_paragraphs {0|1} — Section signs and line breaks insertion. Default: 0. + * @param $text_autoLinks {0|1} — Marking links (including email ones). Default: 0. + * @param $etc_unicodeConvert {0|1} — Convert html entities into Unicode (“—” instead of “—” etc.). Default: 1. + * @param $noTags {0|1} — Whether HTML element insertion is allowed or not. There are cases when using tags causes the text to be invalid, for example, using the snippet inside of an HTML attribute. Default: 0. + * @param $excludeTags {string_commaSeparated} — HTML tags which content will be ignored by snippet. Default: 'notg,code'. * - * @link http://code.divandesign.biz/modx/ddtypograph/2.3 + * @link http://code.divandesign.biz/modx/ddtypograph/2.4 * - * @copyright 2015, DivanDesign - * http://www.DivanDesign.biz + * @copyright 2010–2017 DivanDesign {@link http://www.DivanDesign.biz } */ //Если есть что типографировать @@ -26,28 +27,66 @@ global $ddTypograph; //Заменим кавычки, вставленные через спец. символы на обычные (а то не обрабатываются в библиотеке) - $text = str_replace('"', '"', $text); + $text = str_replace( + '"', + '"', + $text + ); if (!isset($ddTypograph)){ //Подключаем EMT типограф - require_once $modx->config['base_path'].'assets/snippets/ddTypograph/EMT.php'; + require_once $modx->getConfig('base_path').'assets/snippets/ddTypograph/EMT.php'; $ddTypograph = new EMTypograph(); } - //Подключаем modx.ddTools - require_once $modx->config['base_path'].'assets/snippets/ddTools/modx.ddtools.class.php'; + //Include MODXEvo.libraries.ddTools + require_once $modx->getConfig('base_path').'assets/libs/ddTools/modx.ddtools.class.php'; //Для обратной совместимости - extract(ddTools::verifyRenamedParams($params, array( - 'optAlign' => 'OptAlign', - 'text_paragraphs' => 'Text_paragraphs', - 'text_autoLinks' => 'Text_autoLinks', - 'etc_unicodeConvert' => 'Etc_unicodeConvert' - ))); + extract(ddTools::verifyRenamedParams( + $params, + [ + 'optAlign' => 'OptAlign', + 'text_paragraphs' => 'Text_paragraphs', + 'text_autoLinks' => 'Text_autoLinks', + 'etc_unicodeConvert' => 'Etc_unicodeConvert' + ] + )); + + //Safe tags + $excludeTags = isset($excludeTags) ? strtolower($excludeTags) : 'notg,code'; + $excludeTags = explode(',', $excludeTags); + + foreach ($excludeTags as $excludeTags_item){ + $excludeTags_item = trim($excludeTags_item); + + //We don't need anything with default EMT tag + if ($excludeTags_item != 'notg'){ + //Wrap + $text = str_ireplace( + [ + //Tag start + '<'.$excludeTags_item, + //Tag end + '' + ], + [ + //Tag start + '<'.$excludeTags_item, + //Tag end + '' + ], + $text + ); + } + } //Если нельзя добавлять теги к тексту - if (isset($noTags) && $noTags == 1){ + if ( + isset($noTags) && + $noTags == 1 + ){ // $noTags = 'off'; $optAlign = 'off'; @@ -67,7 +106,7 @@ $etc_unicodeConvert = isset($etc_unicodeConvert) && $etc_unicodeConvert == 0 ? 'off' : 'on'; - $ddTypograph->setup(array( + $ddTypograph->setup([ //Расстановка «кавычек-елочек» первого уровня 'Quote.quotes' => 'on', //Внутренние кавычки-лапки @@ -236,7 +275,7 @@ 'Etc.nobr_to_nbsp' => $etc_nobr_to_nbsp, //Разбиение числа на триады («10000» → «10 000») 'Etc.split_number_to_triads' => 'on' - )); + ]); $ddTypograph->set_text($text);