diff --git a/assets/snippets/ddTypograph/EMT.php b/assets/snippets/ddTypograph/EMT.php index 7d9b537..b14ef0e 100644 --- a/assets/snippets/ddTypograph/EMT.php +++ b/assets/snippets/ddTypograph/EMT.php @@ -2,3312 +2,3427 @@ /** * Evgeny Muravjev Typograph, http://mdash.ru -* Version: 3.3 Gold Master -* Release Date: May 4, 2014 +* Version: 3.5 Gold Master +* 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 $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 $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($text); - } - - - - 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; - } - -} - - - -/** - * Базовый класс для группы правил обработки текста - * Класс группы должен наследовать, данный класс и задавать - * в нём 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' => 'Объединение сокращений и т.д., и т.п., в т.ч.', - 'pattern' => array( - '/(\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"))', - ) - ), - '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_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]' - ), - 'expand_no_nbsp_in_nobr' => array( - 'description' => 'Удаление nbsp в nobr/nowrap тэгах', - 'function' => 'remove_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 ); - } - -} - - - - -/** - * @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]', - ), - ), - '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])' - ), - 'spaces_nobr_in_surname_abbr' => array( - 'description' => 'Привязка инициалов к фамилиям', - 'pattern' => - array( - '/(\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[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' => 'Математические знаки больше/меньше/плюс минус/неравно', - 'simple_replace' => true, - 'pattern' => array('!=', '<=', '>=', '~=', '+-'), - 'replacement' => array('≠', '≤', '≥', '≅', '±' ) - ), - /* - 'split_number_to_triads' => array( - 'description' => 'Разбиение числа на триады', - 'cycled' => true, - 'pattern' => '/([0-9])([0-9]{3})([^0-9]|$)/u', - 'replacement' => '\1 \2\3' - ), - */ - '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а-яё0-9])(\()/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] . self::QUOTE_FIRS_OPEN . $m[3]' - ), - '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', - ), - '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', - ), - ), - 'open_quote_adv' => array( - 'description' => 'Открывающая кавычка особые случаи', - 'pattern' => '/(^|\(|\s|\>)(\"|\\\")(\s)(\S+)/iue', - 'replacement' => '$m[1] . self::QUOTE_FIRS_OPEN .$m[4]' - ), - 'quotation' => array( - 'description' => 'Внутренние кавычки-лапки и дюймы', - 'function' => 'build_sub_quotations' - ), - ); - - protected function inject_in($pos, $text) - { - for($i=0;$i_text[$pos+$i] = $text[$i]; - } - - protected function build_sub_quotations() - { - global $__ax,$__ay; - $okposstack = array('0'); - $okpos = 0; - $level = 0; - $off = 0; - while(true) - { - $p = EMT_Lib::strpos_ex($this->_text, 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); - $level++; - } - if($p['str'] == "»") - { - $level--; - if($level>0) if(!$this->is_on('no_bdquotes')) $this->inject_in($p['pos'], self::QUOTE_CRAWSE_CLOSE); - } - $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($this->_text, $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) - { - // заново просмотрим содержимое - $this->_text = substr($this->_text, 0, $lokpos). $k . substr($this->_text, $off); - $off = $lokpos; - $level = 0; - continue; - } - - // иначе просто заменим последнюю явно на " от отчаяния - if($amount == 0) - { - // говорим, что всё в порядке - $level = 0; - $this->_text = substr($this->_text, 0, $p['pos']). '"' . substr($this->_text, $off); - $off = $p['pos'] + strlen('"'); - $okposstack = array($off); - continue; - } - } - } - - - } - // не совпало количество, отменяем все подкавычки - if($level != 0 ){ - - // закрывающих меньше, чем надо - if($level>0) - { - $k = substr($this->_text, $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); - $this->_text = substr($this->_text, 0, $okpos). $k; - } - } - } - -} - - - - - - - -/** - * @see EMT_Tret - */ - -class EMT_Tret_Space extends EMT_Tret -{ - public $title = "Расстановка и удаление пробелов"; - - public $domain_zones = array('ru','ру','com','ком','org','орг', 'уа', 'ua'); - - 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]{4,})/iu', - '/(\040|\t|\ \;|^)([a-zа-яё0-9]+)\.([а-яёa-z]{1,3})/iue', - ), - 'replacement' => array( - '\1\2. \4', - '$m[1].$m[2]."." .(in_array(EMT_Lib::strtolower($m[3]), $this->domain_zones)? "":" "). $m[3]' - ), - ), - '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'), - 'replacement' => array('\1→\2', '\1←\2', '→', '←' ) - ), - ); -} - - -/** - * @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.0 Gold Master -* Release Date: September 28, 2013 -* 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[$id] = 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) - { - unset($this->_safe_blocks[$id]); - } - - - /** - * Добавление защищенного блока - * - * @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]))"; - foreach ($this->_safe_blocks 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 содержит ключ-значение как массив - * - * @param mixed $selector - * @param mixed $key - * @param mixed $value - */ - public function set($selector, $key , $value = false) - { - 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 = $x; - $vv = $y; - } - $this->set($selector, $kk, $vv); - } - } - $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.ip_address' => 'direct', - 'Nobr.spaces_nobr_in_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.split_number_to_triads' => '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' => 'direct', - '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' => 'Inline стили или CSS', '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' => '*', 'setting' => 'dounicode' , 'disabled' => true), - - ); - - /** - * Получить список имеющихся опций - * - * @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); - } - } - - 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 5f11375..8e234db 100644 --- a/ddTypograph.php +++ b/ddTypograph.php @@ -1,23 +1,23 @@ 'on', - //Расстановка дефисов между из-за, из-под + //Расстановка дефисов между «из-за», «из-под» 'Dash.iz_za_pod' => 'on', - //Расстановка дефисов перед -ка, -де, -кась. + //Расстановка дефисов перед «-ка», «-де», «-кась». 'Dash.ka_de_kas' => 'on', //Привязка союзов и предлогов к написанным после словам 'Nobr.super_nbsp' => 'on', //Привязка союзов и предлогов к предыдущим словам в случае конца предложения 'Nobr.nbsp_in_the_end' => 'on', - //Объединение в неразрывные конструкции номеров телефонов TODO: работает плоховато (в +7 777 777 77 77 ставит неразнывные пробелы только в двух первых случаях), обсудить с Евгением. + //TODO: работает плоховато (в «+7 777 777 77 77» ставит неразнывные пробелы только в двух первых случаях), обсудить с Евгением + //Объединение в неразрывные конструкции номеров телефонов +// 'Nobr.phone_builder' => $noTags, 'Nobr.phone_builder' => 'on', + //Дополнительный формат номеров телефонов («+7(123)1234567» → «+7 123 123-45-67») +// 'Nobr.phone_builder_v2' => $noTags, + 'Nobr.phone_builder_v2' => 'on', //Объединение IP-адресов. 'Nobr.ip_address' => 'off', - //Привязка инициалов к фамилиям - 'Nobr.spaces_nobr_in_surname_abbr' => $noTags, - //Привязка градусов к числу TODO: Не работает (по крайней мере, не удалось увидеть работу). + //Привязка инициалов к фамилиям («Иванов И. И.» → «Иванов И. И.») +// 'Nobr.spaces_nobr_in_surname_abbr' => $noTags, + 'Nobr.spaces_nobr_in_surname_abbr' => 'on', + //Расстановка точек у инициалов («Иванов И И» | «Иванов ИИ» → «Иванов И. И.») +// 'Nobr.dots_for_surname_abbr' => $noTags, + 'Nobr.dots_for_surname_abbr' => 'on', + //TODO: Не работает (по крайней мере, не удалось увидеть работу) + //Привязка градусов к числу 'Nobr.nbsp_celcius' => 'on', - //Обрамление пятисимвольных слов разделенных дефисом в неразрывные блоки TODO: Не удалось понять, что это, как и когда работает. + //TODO: Параметр не ясен + //Обрамление пятисимвольных слов разделенных дефисом в неразрывные блоки 'Nobr.hyphen_nowrap_in_small_words' => 'off', //Отмена переноса слова с дефисом - 'Nobr.hyphen_nowrap' => $noTags, - //Использовать nowrap для неразрывных конструкций TODO: Тег «nobr» невалидный, а для «word-spacing» нет значения «nowrap», нужно использовать свойство «white-space». +// 'Nobr.hyphen_nowrap' => $noTags, + 'Nobr.hyphen_nowrap' => 'on', + //TODO: Тег «nobr» невалидный, а для «word-spacing» нет значения «nowrap», нужно использовать свойство «white-space». + //Использовать nowrap для неразрывных конструкций 'Nobr.nowrap' => 'on', - //Замена (tm) на символ торговой марки ™ + //Замена «(tm)» на символ торговой марки «™» 'Symbol.tm_replace' => 'on', - //Замена (r) на символ зарегистрированной торговой марки ® + //Замена «(r)» на символ зарегистрированной торговой марки «®» 'Symbol.r_sign_replace' => 'on', - //Замена (c) на символ копирайта © + //Замена «(c)» на символ копирайта «©» 'Symbol.copy_replace' => 'on', //Расстановка правильного апострофа в текстах 'Symbol.apostrophe' => 'on', - //Градусы по Фаренгейту TODO: Не удалось понять, что это, как и когда работает. + //TODO: Параметр не ясен + //Градусы по Фаренгейту 'Symbol.degree_f' => 'on', - //Замена стрелок <- и -> на символы ← и → TODO: Не срабатывает в конце предложения. + //TODO: Не срабатывает в конце предложения + //Замена стрелок «<-» и «->» на символы «←» и «→» 'Symbol.arrows_symbols' => 'on', - //Расстановка дюйма после числа TODO: Не удалось понять, что это, как и когда работает. + //TODO: Параметр не ясен + //Расстановка дюйма после числа 'Symbol.no_inches' => 'on', - //Расстановка запятых перед а, но + //Расстановка запятых перед «а» и «но» 'Punctmark.auto_comma' => 'on', - //Замена трех точек на знак многоточия + //Замена трех точек на знак многоточия («...» → «…») 'Punctmark.hellip' => 'on', //Замена сдвоенных знаков препинания на одинарные 'Punctmark.fix_pmarks' => 'on', @@ -123,13 +143,14 @@ //Расстановка знака минус между числами 'Number.minus_between_nums' => 'on', - //Расстановка знака минус между диапозоном чисел TODO: Не удалось понять, что это, как и когда работает. + //TODO: Параметр не ясен + //Расстановка знака минус между диапозоном чисел 'Number.minus_in_numbers_range' => 'off', - //Замена x (и по-русски и по-английски) на символ × в размерных единицах + //Замена «x» (и по-русски и по-английски) на символ «×» в размерных единицах 'Number.auto_times_x' => 'on', - //Замена дробей 1/2, 1/4, 3/4 на соответствующие символы + //Замена дробей на соответствующие символы («1/2» → «½», «1/4» → «⅓», «3/4» → «¼») 'Number.simple_fraction' => 'off', - //Математические знаки больше/меньше/плюс минус/неравно TODO: Со знаками больше и меньше не ясно. + //Математические знаки больше или равно/меньше или равно/плюс минус/неравно («>=» → «≥», «<=» → «≤», «+-» → «±», «!=» → «≠») 'Number.math_chars' => 'on', //Объединение триад чисел полупробелом (не разбивает на триады, просто заменяет обычный пробел на полупробел) 'Number.thinsp_between_number_triads' => 'on', @@ -138,13 +159,14 @@ //Пробел между символом параграфа и числом 'Number.thinsp_between_sect_and_number' => 'on', - //Установка тире и пробельных символов в периодах дат TODO: Не удалось понять, что это, как и когда работает. + //TODO: Параметр не ясен + //Установка тире и пробельных символов в периодах дат 'Date.years' => 'on', //Расстановка тире и объединение в неразрывные периоды месяцев 'Date.mdash_month_interval' => 'off', //Расстановка тире и объединение в неразрывные периоды дней 'Date.nbsp_and_dash_month_interval' => 'off', - //Привязка года к дате TODO: Не удалось понять, что это, как и когда работает. + //Привязка года к дате (« 01.01.2015г.» → « 01.01.2015 г.») 'Date.nobr_year_in_date' => 'on', //Удаление лишних пробельных символов и табуляций @@ -160,41 +182,48 @@ //Форматирование денежных сокращений (расстановка пробелов и привязка названия валюты к числу) 'Abbr.nbsp_money_abbr' => 'on', - //Объединение сокращений: и т. д., и т. п., в т. ч. - 'Abbr.nobr_vtch_itd_itp' => $noTags, - //Расстановка пробелов перед сокращениями: см., им. + //Объединение сокращений «и т. д.», «и т. п.», «в т. ч.» +// 'Abbr.nobr_vtch_itd_itp' => $noTags, + 'Abbr.nobr_vtch_itd_itp' => 'on', + //Расстановка пробелов перед сокращениями «см.», «им.» 'Abbr.nobr_sm_im' => 'on', - //Расстановка пробелов перед сокращениями гл., стр., рис., илл., ст., п. + //Расстановка пробелов перед сокращениями «гл.», «стр.», «рис.», «илл.», «ст.», «п.» 'Abbr.nobr_acronym' => 'on', - //Расстановка пробелов в сокращениях г., ул., пер., д. + //Расстановка пробелов в сокращениях «г.», «ул.», «пер.», «д.» 'Abbr.nobr_locations' => 'on', - //Расстановка пробелов перед сокращениями dpi, lpi + //Расстановка пробелов перед сокращениями «dpi», «lpi» 'Abbr.nobr_abbreviation' => 'on', - //Объединение сокращений P.S., P.P.S. - 'Abbr.ps_pps' => $noTags, + //Объединение сокращений «P.S.», «P.P.S.» +// 'Abbr.ps_pps' => $noTags, + 'Abbr.ps_pps' => 'on', //Привязка сокращений форм собственности к названиям организаций 'Abbr.nbsp_org_abbr' => 'on', - //Привязка аббревиатуры ГОСТ к номеру - 'Abbr.nobr_gost' => $noTags, + //Привязка аббревиатуры «ГОСТ» к номеру +// 'Abbr.nobr_gost' => $noTags, + 'Abbr.nobr_gost' => 'on', //Установка пробельных символов в сокращении вольт 'Abbr.nobr_before_unit_volt' => 'on', - //Замена символов и привязка сокращений в размерных величинах: м, см, м2, … + //Замена символов и привязка сокращений в размерных величинах («м», «см», «м2», …) 'Abbr.nbsp_before_unit' => 'on', - //Inline стили или CSS TODO: Разобраться, что это за параметр и какие значения он может принимать + //TODO: Разобраться, что это за параметр и какие значения он может принимать + //Все настройки оптического выравнивания // 'OptAlign.all' => 'off', //Оптическое выравнивание открывающей кавычки 'OptAlign.oa_oquote' => $optAlign, //Оптическое выравнивание для пунктуации (скобка и запятая) 'OptAlign.oa_obracket_coma' => $optAlign, - //Inline стили или CSS + //TODO: Параметр не ясен + //Оптическое выравнивание кавычки + 'OptAlign.oa_oquote_extra' => $optAlign, + //Inline стили или CSS-классы 'OptAlign.layout' => 'style', //Простановка параграфов 'Text.paragraphs' => $text_paragraphs, - //Выделение ссылок из текста. + //Выделение ссылок из текста 'Text.auto_links' => $text_autoLinks, - //Выделение эл. почты из текста. + //Выделение электронной почты из текста 'Text.email' => $text_autoLinks, //Простановка переносов строк 'Text.breakline' => $text_paragraphs, @@ -202,7 +231,11 @@ 'Text.no_repeat_words' => 'off', //Преобразовывать html-сущности в юникод - 'Etc.unicode_convert' => $etc_unicodeConvert + 'Etc.unicode_convert' => $etc_unicodeConvert, + //Использовать символ « » вместо тегов для связывания + 'Etc.nobr_to_nbsp' => $etc_nobr_to_nbsp, + //Разбиение числа на триады («10000» → «10 000») + 'Etc.split_number_to_triads' => 'on' )); $ddTypograph->set_text($text);