PageRenderTime 44ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/mailpoet/lib-3rd-party/pquery/gan_formatter.php

https://gitlab.com/remyvianne/krowkaramel
PHP | 382 lines | 244 code | 43 blank | 95 comment | 88 complexity | 8f80f55aeae80780932eb417d365291e MD5 | raw file
  1. <?php
  2. /**
  3. * @author Niels A.D.
  4. * @author Todd Burry <todd@vanillaforums.com>
  5. * @copyright 2010 Niels A.D., 2014 Todd Burry
  6. * @license http://opensource.org/licenses/LGPL-2.1 LGPL-2.1
  7. * @package pQuery
  8. */
  9. namespace MailPoetVendor\pQuery;
  10. if (!defined('ABSPATH')) exit;
  11. /**
  12. * Indents text
  13. * @param string $text
  14. * @param int $indent
  15. * @param string $indent_string
  16. * @return string
  17. */
  18. function indent_text($text, $indent, $indent_string = ' ') {
  19. if ($indent && $indent_string) {
  20. return str_replace("\n", "\n".str_repeat($indent_string, $indent), $text);
  21. } else {
  22. return $text;
  23. }
  24. }
  25. /**
  26. * Class used to format/minify HTML nodes
  27. *
  28. * Used like:
  29. * <code>
  30. * <?php
  31. * $formatter = new HtmlFormatter();
  32. * $formatter->format($root);
  33. * ?>
  34. * </code>
  35. */
  36. class HtmlFormatter {
  37. /**
  38. * Determines which elements start on a new line and which function as block
  39. * @var array('element' => array('new_line' => true, 'as_block' => true, 'format_inside' => true))
  40. */
  41. var $block_elements = array(
  42. 'p' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  43. 'h1' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  44. 'h2' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  45. 'h3' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  46. 'h4' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  47. 'h5' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  48. 'h6' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  49. 'form' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  50. 'fieldset' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  51. 'legend' => array('new_line' => true, 'as_block' => false, 'format_inside' => true),
  52. 'dl' => array('new_line' => true, 'as_block' => false, 'format_inside' => true),
  53. 'dt' => array('new_line' => true, 'as_block' => false, 'format_inside' => true),
  54. 'dd' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  55. 'ol' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  56. 'ul' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  57. 'li' => array('new_line' => true, 'as_block' => false, 'format_inside' => true),
  58. 'table' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  59. 'tr' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  60. 'dir' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  61. 'menu' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  62. 'address' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  63. 'blockquote' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  64. 'center' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  65. 'del' => array('new_line' => true, 'as_block' => false, 'format_inside' => true),
  66. //'div' => array('new_line' => false, 'as_block' => true, 'format_inside' => true),
  67. 'hr' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  68. 'ins' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  69. 'noscript' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  70. 'pre' => array('new_line' => true, 'as_block' => true, 'format_inside' => false),
  71. 'script' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  72. 'style' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  73. 'html' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  74. 'head' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  75. 'body' => array('new_line' => true, 'as_block' => true, 'format_inside' => true),
  76. 'title' => array('new_line' => true, 'as_block' => false, 'format_inside' => false)
  77. );
  78. /**
  79. * Determines which characters are considered whitespace
  80. * @var array("\t" => true) True to recognize as new line
  81. */
  82. var $whitespace = array(
  83. ' ' => false,
  84. "\t" => false,
  85. "\x0B" => false,
  86. "\0" => false,
  87. "\n" => true,
  88. "\r" => true
  89. );
  90. /**
  91. * String that is used to generate correct indenting
  92. * @var string
  93. */
  94. var $indent_string = ' ';
  95. /**
  96. * String that is used to break lines
  97. * @var string
  98. */
  99. var $linebreak_string = "\n";
  100. /**
  101. * Other formatting options
  102. * @var array
  103. */
  104. public $options = array(
  105. 'img_alt' => '',
  106. 'self_close_str' => null,
  107. 'attribute_shorttag' => false,
  108. 'sort_attributes' => false,
  109. 'attributes_case' => CASE_LOWER,
  110. 'minify_script' => true
  111. );
  112. /**
  113. * Errors found during formatting
  114. * @var array
  115. */
  116. var $errors = array();
  117. /**
  118. * Class constructor
  119. * @param array $options {@link $options}
  120. */
  121. function __construct($options = array()) {
  122. $this->options = array_merge($this->options, $options);
  123. if (isset($options['indent_str']))
  124. $this->indent_string = $options['indent_str'];
  125. if (isset($options['linebreak_str']))
  126. $this->linebreak_string = $options['linebreak_str'];
  127. }
  128. #php4 PHP4 class constructor compatibility
  129. #function HtmlFormatter($options = array()) {return $this->__construct($options);}
  130. #php4e
  131. /**
  132. * Class magic invoke method, performs {@link format()}
  133. * @access private
  134. */
  135. function __invoke(&$node) {
  136. return $this->format($node);
  137. }
  138. /**
  139. * Minifies HTML / removes unneeded whitespace
  140. * @param DomNode $root
  141. * @param bool $strip_comments
  142. * @param bool $recursive
  143. */
  144. static function minify_html(&$root, $strip_comments = true, $recursive = true) {
  145. if ($strip_comments) {
  146. foreach($root->select(':comment', false, $recursive, true) as $c) {
  147. $prev = $c->getSibling(-1);
  148. $next = $c->getSibling(1);
  149. $c->delete();
  150. if ($prev && $next && ($prev->isText()) && ($next->isText())) {
  151. $prev->text .= $next->text;
  152. $next->delete();
  153. }
  154. }
  155. }
  156. foreach($root->select('(!pre + !xmp + !style + !script + !"?php" + !"~text~" + !"~comment~"):not-empty > "~text~"', false, $recursive, true) as $c) {
  157. $c->text = preg_replace('`\s+`', ' ', $c->text);
  158. }
  159. }
  160. /**
  161. * Minifies javascript using JSMin+
  162. * @param DomNode $root
  163. * @param string $indent_string
  164. * @param bool $wrap_comment Wrap javascript in HTML comments (<!-- ~text~ //-->)
  165. * @param bool $recursive
  166. * @return bool|array Array of errors on failure, true on succes
  167. */
  168. static function minify_javascript(&$root, $indent_string = ' ', $wrap_comment = true, $recursive = true) {
  169. #php4 JSMin+ doesn't support PHP4
  170. #return true;
  171. #php4e
  172. #php5
  173. include_once('third_party/jsminplus.php');
  174. $errors = array();
  175. foreach($root->select('script:not-empty > "~text~"', false, $recursive, true) as $c) {
  176. try {
  177. $text = $c->text;
  178. while ($text) {
  179. $text = trim($text);
  180. //Remove comment/CDATA tags at begin and end
  181. if (substr($text, 0, 4) === '<!--') {
  182. $text = substr($text, 5);
  183. continue;
  184. } elseif (strtolower(substr($text, 0, 9)) === '<![cdata[') {
  185. $text = substr($text, 10);
  186. continue;
  187. }
  188. if (($end = substr($text, -3)) && (($end === '-->') || ($end === ']]>'))) {
  189. $text = substr($text, 0, -3);
  190. continue;
  191. }
  192. break;
  193. }
  194. if (trim($text)) {
  195. $text = JSMinPlus::minify($text);
  196. if ($wrap_comment) {
  197. $text = "<!--\n".$text."\n//-->";
  198. }
  199. if ($indent_string && ($wrap_comment || (strpos($text, "\n") !== false))) {
  200. $text = indent_text("\n".$text, $c->indent(), $indent_string);
  201. }
  202. }
  203. $c->text = $text;
  204. } catch (\Exception $e) {
  205. $errors[] = array($e, $c->parent->dumpLocation());
  206. }
  207. }
  208. return (($errors) ? $errors : true);
  209. #php5e
  210. }
  211. /**
  212. * Formats HTML
  213. * @param DomNode $root
  214. * @param bool $recursive
  215. * @access private
  216. */
  217. function format_html(&$root, $recursive = null) {
  218. if ($recursive === null) {
  219. $recursive = true;
  220. self::minify_html($root);
  221. } elseif (is_int($recursive)) {
  222. $recursive = (($recursive > 1) ? $recursive - 1 : false);
  223. }
  224. $root_tag = strtolower($root->tag);
  225. $in_block = isset($this->block_elements[$root_tag]) && $this->block_elements[$root_tag]['as_block'];
  226. $child_count = count($root->children);
  227. if (isset($this->options['attributes_case']) && $this->options['attributes_case']) {
  228. $root->attributes = array_change_key_case($root->attributes, $this->options['attributes_case']);
  229. $root->attributes_ns = null;
  230. }
  231. if (isset($this->options['sort_attributes']) && $this->options['sort_attributes']) {
  232. if ($this->options['sort_attributes'] === 'reverse') {
  233. krsort($root->attributes);
  234. } else {
  235. ksort($root->attributes);
  236. }
  237. }
  238. if ($root->select(':element', true, false, true)) {
  239. $root->setTag(strtolower($root->tag), true);
  240. if (($this->options['img_alt'] !== null) && ($root_tag === 'img') && (!isset($root->alt))) {
  241. $root->setAttribute('alt', $this->options['img_alt']);
  242. }
  243. }
  244. if ($this->options['self_close_str'] !== null) {
  245. $root->self_close_str = $this->options['self_close_str'];
  246. }
  247. if ($this->options['attribute_shorttag'] !== null) {
  248. $root->attribute_shorttag = $this->options['attribute_shorttag'];
  249. }
  250. $prev = null;
  251. $n_tag = '';
  252. // $prev_tag = '';
  253. $as_block = false;
  254. $prev_asblock = false;
  255. for($i = 0; $i < $child_count; $i++) {
  256. $n =& $root->children[$i];
  257. $indent = $n->indent();
  258. if (!$n->isText()) {
  259. $n_tag = strtolower($n->tag);
  260. $new_line = isset($this->block_elements[$n_tag]) && $this->block_elements[$n_tag]['new_line'];
  261. $as_block = isset($this->block_elements[$n_tag]) && $this->block_elements[$n_tag]['as_block'];
  262. $format_inside = ((!isset($this->block_elements[$n_tag])) || $this->block_elements[$n_tag]['format_inside']);
  263. if ($prev && ($prev->isText()) && $prev->text && ($char = $prev->text[strlen($prev->text) - 1]) && isset($this->whitespace[$char])) {
  264. if ($this->whitespace[$char]) {
  265. $prev->text .= str_repeat($this->indent_string, $indent);
  266. } else {
  267. $prev->text = substr_replace($prev->text, $this->linebreak_string.str_repeat($this->indent_string, $indent), -1, 1);
  268. }
  269. } elseif (($new_line || $prev_asblock || ($in_block && ($i === 0)))){
  270. if ($prev && ($prev->isText())) {
  271. $prev->text .= $this->linebreak_string.str_repeat($this->indent_string, $indent);
  272. } else {
  273. $root->addText($this->linebreak_string.str_repeat($this->indent_string, $indent), $i);
  274. ++$child_count;
  275. }
  276. }
  277. if ($format_inside && count($n->children)) {
  278. //$last = end($n->children);
  279. $last = $n->children[count($n->children) - 1];
  280. $last_tag = ($last) ? strtolower($last->tag) : '';
  281. $last_asblock = ($last_tag && isset($this->block_elements[$last_tag]) && $this->block_elements[$last_tag]['as_block']);
  282. if (($n->childCount(true) > 0) || (trim($n->getPlainText()))) {
  283. if ($last && ($last->isText()) && $last->text && ($char = $last->text[strlen($last->text) - 1]) && isset($this->whitespace[$char])) {
  284. if ($as_block || ($last->index() > 0) || isset($this->whitespace[$last->text[0]])) {
  285. if ($this->whitespace[$char]) {
  286. $last->text .= str_repeat($this->indent_string, $indent);
  287. } else {
  288. $last->text = substr_replace($last->text, $this->linebreak_string.str_repeat($this->indent_string, $indent), -1, 1);
  289. }
  290. }
  291. } elseif (($as_block || $last_asblock || ($in_block && ($i === 0))) && $last) {
  292. if ($last && ($last->isText())) {
  293. $last->text .= $this->linebreak_string.str_repeat($this->indent_string, $indent);
  294. } else {
  295. $n->addText($this->linebreak_string.str_repeat($this->indent_string, $indent));
  296. }
  297. }
  298. } elseif (!trim($n->getInnerText())) {
  299. $n->clear();
  300. }
  301. if ($recursive) {
  302. $this->format_html($n, $recursive);
  303. }
  304. }
  305. } elseif (trim($n->text) && ((($i - 1 < $child_count) && ($char = $n->text[0]) && isset($this->whitespace[$char])) || ($in_block && ($i === 0)))) {
  306. if (isset($this->whitespace[$char])) {
  307. if ($this->whitespace[$char]) {
  308. $n->text = str_repeat($this->indent_string, $indent).$n->text;
  309. } else {
  310. $n->text = substr_replace($n->text, $this->linebreak_string.str_repeat($this->indent_string, $indent), 0, 1);
  311. }
  312. } else {
  313. $n->text = $this->linebreak_string.str_repeat($this->indent_string, $indent).$n->text;
  314. }
  315. }
  316. $prev = $n;
  317. // $prev_tag = $n_tag;
  318. $prev_asblock = $as_block;
  319. }
  320. return true;
  321. }
  322. /**
  323. * Formats HTML/Javascript
  324. * @param DomNode $root
  325. * @see format_html()
  326. */
  327. function format(&$node) {
  328. $this->errors = array();
  329. if ($this->options['minify_script']) {
  330. $a = self::minify_javascript($node, $this->indent_string, true, true);
  331. if (is_array($a)) {
  332. foreach($a as $error) {
  333. $this->errors[] = $error[0]->getMessage().' >>> '.$error[1];
  334. }
  335. }
  336. }
  337. return $this->format_html($node);
  338. }
  339. }