Completed
Push — master ( c0d296...ad9476 )
by Lars
02:27
created

CssToInlineStyles::cleanupHTML()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 1
crap 2
1
<?php
2
3
namespace voku\CssToInlineStyles;
4
5
use Symfony\Component\CssSelector\CssSelectorConverter;
6
use Symfony\Component\CssSelector\Exception\ExceptionInterface;
7
use voku\helper\HtmlDomParser;
8
9
/**
10
 * CSS to Inline Styles class
11
 *
12
 * @author     Tijs Verkoyen <[email protected]>
13
 */
14
class CssToInlineStyles
15
{
16
17
  /**
18
   * @var CssSelectorConverter
19
   */
20
  private $cssConverter;
21
22
  /**
23
   * regular expression: css media queries
24
   *
25
   * @var string
26
   */
27
  private static $cssMediaQueriesRegEx = '#@media\\s+(?:only\\s)?(?:[\\s{\\(]|screen|all)\\s?[^{]+{.*}\\s*}\\s*#misU';
28
29
  /**
30
   * regular expression: css charset
31
   *
32
   * @var string
33
   */
34
  private static $cssCharsetRegEx = '/@charset [\'"][^\'"]+[\'"];/i';
35
36
  /**
37
   * regular expression: conditional inline style tags
38
   *
39
   * @var string
40
   */
41
  private static $excludeConditionalInlineStylesBlockRegEx = '/<!--\[if.*<style.*-->/isU';
42
43
  /**
44
   * regular expression: inline style tags
45
   *
46
   * @var string
47
   */
48
  private static $styleTagRegEx = '|<style(?:\s.*)?>(.*)</style>|isU';
49
50
  /**
51
   * regular expression: html-comments without conditional comments
52
   *
53
   * @var string
54
   */
55
  private static $htmlCommentWithoutConditionalCommentRegEx = '|<!--(?!\[if).*?-->|isU';
56
57
  /**
58
   * regular expression: style-tag with 'cleanup'-css-class
59
   *
60
   * @var string
61
   */
62
  private static $styleTagWithCleanupClassRegEx = '|<style[^>]+class="cleanup"[^>]*>.*</style>|isU';
63
64
  /**
65
   * regular expression: css-comments
66
   *
67
   * @var string
68
   */
69
  private static $styleCommentRegEx = '/\\/\\*.*\\*\\//sU';
70
71
  /**
72
   * The CSS to use.
73
   *
74
   * @var string
75
   */
76
  private $css;
77
78
  /**
79
   * The CSS-Media-Queries to use.
80
   *
81
   * @var string
82
   */
83
  private $css_media_queries;
84
85
  /**
86
   * Should the generated HTML be cleaned.
87
   *
88
   * @var bool
89
   */
90
  private $cleanup = false;
91
92
  /**
93
   * The encoding to use.
94
   *
95
   * @var string
96
   */
97
  private $encoding = 'UTF-8';
98
99
  /**
100
   * The HTML to process.
101
   *
102
   * @var string
103
   */
104
  private $html;
105
106
  /**
107
   * Use inline-styles block as CSS.
108
   *
109
   * @var bool
110
   */
111
  private $useInlineStylesBlock = false;
112
113
  /**
114
   * Use link block reference as CSS.
115
   *
116
   * @var bool
117
   */
118
  private $loadCSSFromHTML = false;
119
120
  /**
121
   * Strip original style tags.
122
   *
123
   * @var bool
124
   */
125
  private $stripOriginalStyleTags = false;
126
127
  /**
128
   * Exclude conditional inline-style blocks.
129
   *
130
   * @var bool
131
   */
132
  private $excludeConditionalInlineStylesBlock = true;
133
134
  /**
135
   * Exclude media queries from "$this->css" and keep media queries for inline-styles blocks.
136
   *
137
   * @var bool
138
   */
139
  private $excludeMediaQueries = true;
140
141
  /**
142
   * Exclude media queries from "$this->css" and keep media queries for inline-styles blocks.
143
   *
144
   * @var bool
145
   */
146
  private $excludeCssCharset = true;
147
148
  /**
149
   * Creates an instance, you could set the HTML and CSS here, or load it later.
150
   *
151
   * @param  null|string $html The HTML to process.
152
   * @param  null|string $css  The CSS to use.
153
   */
154 56
  public function __construct($html = null, $css = null)
155
  {
156 56
    if (null !== $html) {
157 2
      $this->setHTML($html);
158
    }
159
160 56
    if (null !== $css) {
161 2
      $this->setCSS($css);
162
    }
163
164 56
    if (class_exists('Symfony\Component\CssSelector\CssSelectorConverter')) {
165 56
      $this->cssConverter = new CssSelectorConverter();
166
    }
167 56
  }
168
169
  /**
170
   * Set HTML to process.
171
   *
172
   * @param  string $html The HTML to process.
173
   *
174
   * @return $this
175
   */
176 54
  public function setHTML($html)
177
  {
178
    // strip style definitions, if we use css-class "cleanup" on a style-element
179 54
    $this->html = (string)preg_replace(self::$styleTagWithCleanupClassRegEx, ' ', $html);
180
181 54
    return $this;
182
  }
183
184
  /**
185
   * Set CSS to use.
186
   *
187
   * @param  string $css The CSS to use.
188
   *
189
   * @return $this
190
   */
191 49
  public function setCSS($css)
192
  {
193 49
    $this->css = (string)$css;
194
195 49
    $this->css_media_queries = $this->getMediaQueries($css);
196
197 49
    return $this;
198
  }
199
200
  /**
201
   * Sort an array on the specificity element in an ascending way.
202
   *
203
   * INFO: Lower specificity will be sorted to the beginning of the array.
204
   *
205
   * @param Specificity[] $e1 The first element.
206
   * @param Specificity[] $e2 The second element.
207
   *
208
   * @return int
209
   */
210 23
  private static function sortOnSpecificity($e1, $e2)
211
  {
212
    // Compare the specificity
213 23
    $value = $e1['specificity']->compareTo($e2['specificity']);
214
215
    // if the specificity is the same, use the order in which the element appeared
216 23
    if (0 === $value) {
217 18
      $value = $e1['order'] - $e2['order'];
218
    }
219
220 23
    return $value;
221
  }
222
223
  /**
224
   * Converts the loaded HTML into an HTML-string with inline styles based on the loaded CSS.
225
   *
226
   * @param bool     $outputXHTML        [optional] Should we output valid XHTML?
227
   * @param int|null $libXMLExtraOptions [optional] $libXMLExtraOptions Since PHP 5.4.0 and Libxml 2.6.0,
228
   *                                     you may also use the options parameter to specify additional
229
   *                                     Libxml parameters.
230
   * @param bool     $path               [optional] Set the path to your external css-files.
231
   *
232
   * @return string
233
   *
234
   * @throws Exception
235
   */
236 54
  public function convert($outputXHTML = false, $libXMLExtraOptions = null, $path = false)
237
  {
238
    // init
239 54
    $outputXHTML = (bool)$outputXHTML;
240
241
    // validate
242 54
    if (!$this->html) {
243 1
      throw new Exception('No HTML provided.');
244
    }
245
246
    // use local variables
247 53
    $css = $this->css;
248
249
    // create new HtmlDomParser
250 53
    $dom = HtmlDomParser::str_get_html($this->html, $libXMLExtraOptions);
0 ignored issues
show
Unused Code introduced by
The call to HtmlDomParser::str_get_html() has too many arguments starting with $this->html.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
251
252
    // check if there is some link css reference
253 53
    if ($this->loadCSSFromHTML) {
254 1
      foreach ($dom->find('link') as $node) {
255
256 1
        $file = ($path ?: __DIR__) . '/' . $node->getAttribute('href');
257
258 1
        if (file_exists($file)) {
259 1
          $css .= file_get_contents($file);
260
261
          // converting to inline css because we don't need/want to load css files, so remove the link
262 1
          $node->outertext = '';
263
        }
264
      }
265
    }
266
267
    // should we use inline style-block
268 53
    if ($this->useInlineStylesBlock) {
269
270 30
      if (true === $this->excludeConditionalInlineStylesBlock) {
271 26
        $this->html = preg_replace(self::$excludeConditionalInlineStylesBlockRegEx, '', $this->html);
272
      }
273
274 30
      $css .= $this->getCssFromInlineHtmlStyleBlock($this->html);
275
    }
276
277
    // process css
278 53
    $cssRules = $this->processCSS($css);
279
280
    // create new XPath
281 53
    $xPath = $this->createXPath($dom->getDocument(), $cssRules);
282
283
    // strip original style tags if we need to
284 53
    if ($this->stripOriginalStyleTags === true) {
285 13
      $this->stripOriginalStyleTags($xPath);
286
    }
287
288
    // cleanup the HTML if we need to
289 53
    if (true === $this->cleanup) {
290 3
      $this->cleanupHTML($xPath);
291
    }
292
293
    // should we output XHTML?
294 53
    if (true === $outputXHTML) {
295 6
      return $dom->xml();
296
    }
297
298
    // just regular HTML 4.01 as it should be used in newsletters
299 48
    $html = $dom->html();
300
301
    // add css media queries from "$this->setCSS()"
302
    if (
303 48
        $this->stripOriginalStyleTags === false
304
        &&
305 48
        $this->css_media_queries
306
    ) {
307 3
      $html = str_ireplace('</head>', "\n" . '<style type="text/css">' . "\n" . $this->css_media_queries . "\n" . '</style>' . "\n" . '</head>', $html);
308
    }
309
310 48
    return $html;
311
  }
312
313
  /**
314
   * get css from inline-html style-block
315
   *
316
   * @param string $html
317
   *
318
   * @return string
319
   */
320 32
  public function getCssFromInlineHtmlStyleBlock($html)
321
  {
322
    // init var
323 32
    $css = '';
324 32
    $matches = array();
325
326 32
    $htmlNoComments = preg_replace(self::$htmlCommentWithoutConditionalCommentRegEx, '', $html);
327
328
    // match the style blocks
329 32
    preg_match_all(self::$styleTagRegEx, $htmlNoComments, $matches);
330
331
    // any style-blocks found?
332 32
    if (!empty($matches[1])) {
333
      // add
334 31
      foreach ($matches[1] as $match) {
335 31
        $css .= trim($match) . "\n";
336
      }
337
    }
338
339 32
    return $css;
340
  }
341
342
  /**
343
   * Process the loaded CSS
344
   *
345
   * @param string $css
346
   *
347
   * @return array
348
   */
349 53
  private function processCSS($css)
350
  {
351
    //reset current set of rules
352 53
    $cssRules = array();
353
354
    // init vars
355 53
    $css = (string)$css;
356
357 53
    $css = $this->doCleanup($css);
358
359
    // rules are splitted by }
360 53
    $rules = (array)explode('}', $css);
361
362
    // init var
363 53
    $i = 1;
364
365
    // loop rules
366 53
    foreach ($rules as $rule) {
367
      // split into chunks
368 53
      $chunks = explode('{', $rule);
369
370
      // invalid rule?
371 53
      if (!isset($chunks[1])) {
372 53
        continue;
373
      }
374
375
      // set the selectors
376 40
      $selectors = trim($chunks[0]);
377
378
      // get css-properties
379 40
      $cssProperties = trim($chunks[1]);
380
381
      // split multiple selectors
382 40
      $selectors = (array)explode(',', $selectors);
383
384
      // loop selectors
385 40
      foreach ($selectors as $selector) {
386
        // cleanup
387 40
        $selector = trim($selector);
388
389
        // build an array for each selector
390 40
        $ruleSet = array();
391
392
        // store selector
393 40
        $ruleSet['selector'] = $selector;
394
395
        // process the properties
396 40
        $ruleSet['properties'] = $this->processCSSProperties($cssProperties);
397
398
        // calculate specificity
399 40
        $ruleSet['specificity'] = Specificity::fromSelector($selector);
400
401
        // remember the order in which the rules appear
402 40
        $ruleSet['order'] = $i;
403
404
        // add into rules
405 40
        $cssRules[] = $ruleSet;
406
407
        // increment
408 40
        $i++;
409
      }
410
    }
411
412
    // sort based on specificity
413 53
    if (0 !== count($cssRules)) {
414 40
      usort($cssRules, array(__CLASS__, 'sortOnSpecificity'));
415
    }
416
417 53
    return $cssRules;
418
  }
419
420
  /**
421
   * @param string $css
422
   *
423
   * @return string
424
   */
425 53
  private function doCleanup($css)
426
  {
427
    // remove newlines & replace double quotes by single quotes
428 53
    $css = str_replace(
429 53
        array("\r", "\n", '"'),
430 53
        array('', '', '\''),
431
        $css
432
    );
433
434
    // remove comments
435 53
    $css = preg_replace(self::$styleCommentRegEx, '', $css);
436
437
    // remove spaces
438 53
    $css = preg_replace('/\s\s+/', ' ', $css);
439
440
    // remove css charset
441 53
    if (true === $this->excludeCssCharset) {
442 53
      $css = $this->stripeCharsetInCss($css);
443
    }
444
445
    // remove css media queries
446 53
    if (true === $this->excludeMediaQueries) {
447 51
      $css = $this->stripeMediaQueries($css);
448
    }
449
450 53
    return (string)$css;
451
  }
452
453
  /**
454
   * remove css media queries from the string
455
   *
456
   * @param string $css
457
   *
458
   * @return string
459
   */
460 51
  private function stripeMediaQueries($css)
461
  {
462
    // remove comments previously to matching media queries
463 51
    $css = preg_replace(self::$styleCommentRegEx, '', $css);
464
465 51
    return (string)preg_replace(self::$cssMediaQueriesRegEx, '', $css);
466
  }
467
468
  /**
469
   * get css media queries from the string
470
   *
471
   * @param string $css
472
   *
473
   * @return string
474
   */
475 49
  private function getMediaQueries($css)
476
  {
477
    // remove comments previously to matching media queries
478 49
    $css = preg_replace(self::$styleCommentRegEx, '', $css);
479
480 49
    preg_match_all(self::$cssMediaQueriesRegEx, $css, $matches);
481
482 49
    return implode("\n", $matches[0]);
483
  }
484
485
  /**
486
   * remove charset from the string
487
   *
488
   * @param string $css
489
   *
490
   * @return string
491
   */
492 53
  private function stripeCharsetInCss($css)
493
  {
494 53
    return (string)preg_replace(self::$cssCharsetRegEx, '', $css);
495
  }
496
497
  /**
498
   * Process the CSS-properties
499
   *
500
   * @return array
501
   *
502
   * @param  string $propertyString The CSS-properties.
503
   */
504 40
  private function processCSSProperties($propertyString)
505
  {
506
    // split into chunks
507 40
    $properties = $this->splitIntoProperties($propertyString);
508
509
    // init var
510 40
    $pairs = array();
511
512
    // loop properties
513 40
    foreach ($properties as $property) {
514
      // split into chunks
515 40
      $chunks = (array)explode(':', $property, 2);
516
517
      // validate
518 40
      if (!isset($chunks[1])) {
519 34
        continue;
520
      }
521
522
      // cleanup
523 39
      $chunks[0] = trim($chunks[0]);
524 39
      $chunks[1] = trim($chunks[1]);
525
526
      // add to pairs array
527
      if (
528 39
          !isset($pairs[$chunks[0]])
529
          ||
530 39
          !in_array($chunks[1], $pairs[$chunks[0]], true)
531
      ) {
532 39
        $pairs[$chunks[0]][] = $chunks[1];
533
      }
534
    }
535
536
    // sort the pairs
537 40
    ksort($pairs);
538
539
    // return
540 40
    return $pairs;
541
  }
542
543
  /**
544
   * Split a style string into an array of properties.
545
   * The returned array can contain empty strings.
546
   *
547
   * @param string $styles ex: 'color:blue;font-size:12px;'
548
   *
549
   * @return array an array of strings containing css property ex: array('color:blue','font-size:12px')
550
   */
551 40
  private function splitIntoProperties($styles)
552
  {
553 40
    $properties = (array)explode(';', $styles);
554 40
    $propertiesCount = count($properties);
555
556
    /** @noinspection ForeachInvariantsInspection */
557 40
    for ($i = 0; $i < $propertiesCount; $i++) {
558
      // If next property begins with base64,
559
      // Then the ';' was part of this property (and we should not have split on it).
560
      if (
561 40
          isset($properties[$i + 1])
562
          &&
563 40
          strpos($properties[$i + 1], 'base64,') !== false
564
      ) {
565 1
        $properties[$i] .= ';' . $properties[$i + 1];
566 1
        $properties[$i + 1] = '';
567 1
        ++$i;
568
      }
569
    }
570
571 40
    return $properties;
572
  }
573
574
  /**
575
   * create XPath
576
   *
577
   * @param \DOMDocument $document
578
   * @param array        $cssRules
579
   *
580
   * @return \DOMXPath
581
   */
582 53
  private function createXPath(\DOMDocument $document, array $cssRules)
583
  {
584 53
    $propertyStorage = new \SplObjectStorage();
585 53
    $xPath = new \DOMXPath($document);
586
587
    // any rules?
588 53
    if (0 !== count($cssRules)) {
589
      // loop rules
590 40
      foreach ($cssRules as $rule) {
591
592 40
        $ruleSelector = $rule['selector'];
593 40
        $ruleProperties = $rule['properties'];
594
595 40
        if (!$ruleSelector || !$ruleProperties) {
596 4
          continue;
597
        }
598
599
        try {
600 39
          $query = $this->cssConverter->toXPath($ruleSelector);
601 5
        } catch (ExceptionInterface $e) {
602 5
          $query = null;
603
        }
604
605
        // validate query
606 39
        if (null === $query) {
607 5
          continue;
608
        }
609
610
        // search elements
611 38
        $elements = $xPath->query($query);
612
613
        // validate elements
614 38
        if (false === $elements) {
615
          continue;
616
        }
617
618
        // loop found elements
619 38
        foreach ($elements as $element) {
620
621
          /**
622
           * @var $element \DOMElement
623
           */
624
625
          if (
626 38
              $ruleSelector == '*'
627
              &&
628
              (
629 2
                  $element->tagName == 'html'
630 2
                  || $element->tagName === 'title'
631 2
                  || $element->tagName == 'meta'
632 2
                  || $element->tagName == 'head'
633 2
                  || $element->tagName == 'style'
634 2
                  || $element->tagName == 'script'
635 38
                  || $element->tagName == 'link'
636
              )
637
          ) {
638 2
            continue;
639
          }
640
641
          // no styles stored?
642 38
          if (!isset($propertyStorage[$element])) {
643
644
            // init var
645 38
            $originalStyle = $element->attributes->getNamedItem('style');
646
647 38
            if ($originalStyle) {
648 12
              $originalStyle = (string)$originalStyle->value;
0 ignored issues
show
Bug introduced by
The property value does not seem to exist in DOMNode.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
649
            } else {
650 36
              $originalStyle = '';
651
            }
652
653
            // store original styles
654 38
            $propertyStorage->attach($element, $originalStyle);
655
656
            // clear the styles
657 38
            $element->setAttribute('style', '');
658
          }
659
660
          // set attribute
661 38
          $propertiesString = $this->createPropertyChunks($element, $ruleProperties);
662 38
          if ($propertiesString) {
663 38
            $element->setAttribute('style', $propertiesString);
664
          }
665
        }
666
      }
667
668 40
      foreach ($propertyStorage as $element) {
669 38
        $originalStyle = $propertyStorage->getInfo();
670 38
        if ($originalStyle) {
671 12
          $originalStyles = $this->splitIntoProperties($originalStyle);
672 12
          $originalProperties = $this->splitStyleIntoChunks($originalStyles);
673
674
          // set attribute
675 12
          $propertiesString = $this->createPropertyChunks($element, $originalProperties);
676 12
          if ($propertiesString) {
677 38
            $element->setAttribute('style', $propertiesString);
678
          }
679
        }
680
      }
681
    }
682
683 53
    return $xPath;
684
  }
685
686
  /**
687
   * @param \DOMElement $element
688
   * @param array       $ruleProperties
689
   *
690
   * @return string
691
   */
692 38
  private function createPropertyChunks(\DOMElement $element, array $ruleProperties)
693
  {
694
    // init var
695 38
    $properties = array();
696
697
    // get current styles
698 38
    $stylesAttribute = $element->attributes->getNamedItem('style');
699
700
    // any styles defined before?
701 38
    if (null !== $stylesAttribute) {
702
      // get value for the styles attribute
703 38
      $definedStyles = (string)$stylesAttribute->value;
0 ignored issues
show
Bug introduced by
The property value does not seem to exist in DOMNode.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
704
705
      // split into properties
706 38
      $definedProperties = $this->splitIntoProperties($definedStyles);
707
708 38
      $properties = $this->splitStyleIntoChunks($definedProperties);
709
    }
710
711
    // add new properties into the list
712 38
    foreach ($ruleProperties as $key => $value) {
713
      // If one of the rules is already set and is !important, don't apply it,
714
      // except if the new rule is also important.
715
      if (
716 38
          !isset($properties[$key])
717
          ||
718 12
          false === stripos($properties[$key], '!important')
719
          ||
720 38
          false !== stripos(implode('', (array)$value), '!important')
721
      ) {
722 38
        unset($properties[$key]);
723 38
        $properties[$key] = $value;
724
      }
725
    }
726
727
    // build string
728 38
    $propertyChunks = array();
729
730
    // build chunks
731 38
    foreach ($properties as $key => $values) {
732 38
      foreach ((array)$values as $value) {
733 38
        $propertyChunks[] = $key . ': ' . $value . ';';
734
      }
735
    }
736
737 38
    return implode(' ', $propertyChunks);
738
  }
739
740
  /**
741
   * @param array $definedProperties
742
   *
743
   * @return array
744
   */
745 38
  private function splitStyleIntoChunks(array $definedProperties)
746
  {
747
    // init var
748 38
    $properties = array();
749
750
    // loop properties
751 38
    foreach ($definedProperties as $property) {
752
      // validate property
753
      if (
754 38
          !$property
755
          ||
756 38
          strpos($property, ':') === false
757
      ) {
758 38
        continue;
759
      }
760
761
      // split into chunks
762 20
      $chunks = (array)explode(':', trim($property), 2);
763
764
      // validate
765 20
      if (!isset($chunks[1])) {
766
        continue;
767
      }
768
769
      // loop chunks
770 20
      $properties[$chunks[0]] = trim($chunks[1]);
771
    }
772
773 38
    return $properties;
774
  }
775
776
  /**
777
   * Strip style tags into the generated HTML.
778
   *
779
   * @param  \DOMXPath $xPath The DOMXPath for the entire document.
780
   */
781 13
  private function stripOriginalStyleTags(\DOMXPath $xPath)
782
  {
783
    // get all style tags
784 13
    $nodes = $xPath->query('descendant-or-self::style');
785 13
    foreach ($nodes as $node) {
786 12
      if ($this->excludeMediaQueries === true) {
787
788
        // remove comments previously to matching media queries
789 11
        $node->nodeValue = preg_replace(self::$styleCommentRegEx, '', $node->nodeValue);
790
791
        // search for Media Queries
792 11
        preg_match_all(self::$cssMediaQueriesRegEx, $node->nodeValue, $mqs);
793
794
        // replace the nodeValue with just the Media Queries
795 11
        $node->nodeValue = implode("\n", $mqs[0]);
796
797
      } else {
798
        // remove the entire style tag
799 12
        $node->parentNode->removeChild($node);
800
      }
801
    }
802 13
  }
803
804
  /**
805
   * Remove id and class attributes.
806
   *
807
   * @param  \DOMXPath $xPath The DOMXPath for the entire document.
808
   */
809 3
  private function cleanupHTML(\DOMXPath $xPath)
810
  {
811 3
    $nodes = $xPath->query('//@class | //@id');
812 3
    foreach ($nodes as $node) {
813 3
      $node->ownerElement->removeAttributeNode($node);
814
    }
815 3
  }
816
817
  /**
818
   * Should the IDs and classes be removed?
819
   *
820
   * @param  bool $on Should we enable cleanup?
821
   *
822
   * @return $this
823
   */
824 3
  public function setCleanup($on = true)
825
  {
826 3
    $this->cleanup = (bool)$on;
827
828 3
    return $this;
829
  }
830
831
  /**
832
   * Set the encoding to use with the DOMDocument.
833
   *
834
   * @param  string $encoding The encoding to use.
835
   *
836
   * @return $this
837
   *
838
   * @deprecated Doesn't have any effect
839
   */
840
  public function setEncoding($encoding)
841
  {
842
    $this->encoding = (string)$encoding;
843
844
    return $this;
845
  }
846
847
  /**
848
   * Set use of inline styles block.
849
   *
850
   * Info: If this is enabled the class will use the style-block in the HTML.
851
   *
852
   * @param  bool $on Should we process inline styles?
853
   *
854
   * @return $this
855
   */
856 30
  public function setUseInlineStylesBlock($on = true)
857
  {
858 30
    $this->useInlineStylesBlock = (bool)$on;
859
860 30
    return $this;
861
  }
862
863
  /**
864
   * Set use of inline link block.
865
   *
866
   * Info: If this is enabled the class will use the links reference in the HTML.
867
   *
868
   * @param  bool [optional] $on Should we process link styles?
869
   *
870
   * @return $this
871
   */
872 2
  public function setLoadCSSFromHTML($on = true)
873
  {
874 2
    $this->loadCSSFromHTML = (bool)$on;
875
876 2
    return $this;
877
  }
878
879
  /**
880
   * Set strip original style tags.
881
   *
882
   * Info: If this is enabled the class will remove all style tags in the HTML.
883
   *
884
   * @param  bool $on Should we process inline styles?
885
   *
886
   * @return $this
887
   */
888 17
  public function setStripOriginalStyleTags($on = true)
889
  {
890 17
    $this->stripOriginalStyleTags = (bool)$on;
891
892 17
    return $this;
893
  }
894
895
  /**
896
   * Set exclude media queries.
897
   *
898
   * Info: If this is enabled the media queries will be removed before inlining the rules.
899
   *
900
   * WARNING: If you use inline styles block "<style>" the this option will keep the media queries.
901
   *
902
   * @param bool $on
903
   *
904
   * @return $this
905
   */
906 15
  public function setExcludeMediaQueries($on = true)
907
  {
908 15
    $this->excludeMediaQueries = (bool)$on;
909
910 15
    return $this;
911
  }
912
913
  /**
914
   * Set exclude charset.
915
   *
916
   * @param bool $on
917
   *
918
   * @return $this
919
   */
920 1
  public function setExcludeCssCharset($on = true)
921
  {
922 1
    $this->excludeCssCharset = (bool)$on;
923
924 1
    return $this;
925
  }
926
927
  /**
928
   * Set exclude conditional inline-style blocks.
929
   *
930
   * e.g.: <!--[if gte mso 9]><style>.foo { bar } </style><![endif]-->
931
   *
932
   * @param bool $on
933
   *
934
   * @return $this
935
   */
936 6
  public function setExcludeConditionalInlineStylesBlock($on = true)
937
  {
938 6
    $this->excludeConditionalInlineStylesBlock = (bool)$on;
939
940 6
    return $this;
941
  }
942
}
943