We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 4113
    • 2 Posts
    Thanks SO much for providing this plugin. I am now using this on all my sites!

    I have enhanced the plugin code to do the following:

    - Don’t indent after opening <html> tag, just like it doesn’t indent after the <!DOCTYPE ...> tag
    - Don’t strip out blank lines - often I include blank lines to improve code readability
    - Incorporated BobRay’s simplification

    My updated version is:

    function fix_newlines_for_clean_html($fixthistext)
    {
        $fixthistext_array = explode("\n", $fixthistext);
        foreach ($fixthistext_array as $unfixedtextkey => $unfixedtextvalue)
        {
            //Makes sure empty lines are ignored
            if (!preg_match("/^(\s)*$/", $unfixedtextvalue))
            {
                $fixedtextvalue = preg_replace("/>(\s|\t)*</U", ">\n<", $unfixedtextvalue);
                $fixedtext_array[$unfixedtextkey] = $fixedtextvalue;
            } else {
                $fixedtext_array[$unfixedtextkey] = $unfixedtextvalue;
            }
        }
        return implode("\n", $fixedtext_array);
    }
    
    function clean_html_code($uncleanhtml)
    {
        //Set wanted indentation
        $indent = "    ";
    
    
        //Uses previous function to seperate tags
        $fixed_uncleanhtml = fix_newlines_for_clean_html($uncleanhtml);
        $uncleanhtml_array = explode("\n", $fixed_uncleanhtml);
        //Sets no indentation
        $indentlevel = 0;
        foreach ($uncleanhtml_array as $uncleanhtml_key => $currentuncleanhtml)
        {
            //Removes all indentation
            $currentuncleanhtml = preg_replace("/\t+/", "", $currentuncleanhtml);
            $currentuncleanhtml = preg_replace("/^\s+/", "", $currentuncleanhtml);
            
            $replaceindent = "";
            
            //Sets the indentation from current indentlevel
            for ($o = 0; $o < $indentlevel; $o++)
            {
                $replaceindent .= $indent;
            }
            
            //If self-closing tag, simply apply indent
            if (preg_match("/<(.+)\/>/", $currentuncleanhtml))
            { 
                $cleanhtml_array[$uncleanhtml_key] = $replaceindent.$currentuncleanhtml;
            }
            //If doctype declaration, or <html> tag, simply apply indent
            else if (preg_match("/<!(.*)>/", $currentuncleanhtml) || preg_match("/<html(.*)>/", $currentuncleanhtml))
            { 
                $cleanhtml_array[$uncleanhtml_key] = $replaceindent.$currentuncleanhtml;
            }
            //If opening AND closing tag on same line, simply apply indent
            else if (preg_match("/<[^\/](.*)>/", $currentuncleanhtml) && preg_match("/<\/(.*)>/", $currentuncleanhtml))
            { 
                $cleanhtml_array[$uncleanhtml_key] = $replaceindent.$currentuncleanhtml;
            }
            //If closing HTML tag or closing JavaScript clams, decrease indentation and then apply the new level
            else if (preg_match("/<\/(.*)>/", $currentuncleanhtml) || preg_match("/^(\s|\t)*\}{1}(\s|\t)*$/", $currentuncleanhtml))
            {
                $indentlevel--;
                $replaceindent = "";
                for ($o = 0; $o < $indentlevel; $o++)
                {
                    $replaceindent .= $indent;
                }
                
                $cleanhtml_array[$uncleanhtml_key] = $replaceindent.$currentuncleanhtml;
            }
            //If opening HTML tag AND not a stand-alone tag, or opening JavaScript clams, increase indentation and then apply new level
            else if ((preg_match("/<[^\/](.*)>/", $currentuncleanhtml) && !preg_match("/<(link|meta|base|br|img|hr)(.*)>/", $currentuncleanhtml)) || preg_match("/^(\s|\t)*\{{1}(\s|\t)*$/", $currentuncleanhtml))
            {
                $cleanhtml_array[$uncleanhtml_key] = $replaceindent.$currentuncleanhtml;
                
                $indentlevel++;
                $replaceindent = "";
                for ($o = 0; $o < $indentlevel; $o++)
                {
                    $replaceindent .= $indent;
                }
            }
            else
            //Else, only apply indentation
            {$cleanhtml_array[$uncleanhtml_key] = $replaceindent.$currentuncleanhtml;}
        }
        //Return single string seperated by newline
        return implode("\n", $cleanhtml_array); 
    }
    
    $modx->documentOutput = clean_html_code($modx->documentOutput);
    return;
    

      • 4113
      • 2 Posts
      While this plugin is working well for my 95% of the time, there are a few problems I’m running into:

      1. It often splits lines e.g.

      <div id="some-selector"></div>


      is changed into:

      <div id="some-selector>
      </div>
      


      Can that be easily avoided?


      2. It sometimes get confused. For example:

                  <a id="logo" href="./">
                      <img src="assets/images/logo.png" alt="Logo" />Site Name
                   </a>
                   <p>hello</p>
      


      Gets changed into:

                  <a id="logo" href="./">
                      <img src="assets/images/logo.png" alt="Logo" />Site Name</a>
                      <p>hello</p>
      


      As a result, all the code after this point in the document is indented one level too far.


      3. This is more like a feature wish than a problem, but it would improve the legibility of code if long lines of text content would be word-wrapped and indented, rather than being wrapped to the beginning of the line by the view source window.

      e.g.

                         <div>
                             <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris nec sapien lacus. Sed interdum suscipit dapibus. Integer dictum ornare velit et posuere. Cras in metus risus. Mauris at vulputate lorem. Morbi gravida, nisl id viverra interdum, eros quam fringilla massa, vel rutrum justo sapien sed lectus. Etiam rutrum blandit commodo. Nullam ut sem a orci ullamcorper pretium. Aliquam et diam vitae risus varius imperdiet vel et tellus. Curabitur adipiscing eleifend ultrices. Vestibulum ultricies odio vestibulum massa malesuada id adipiscing magna lobortis. Proin bibendum ornare quam, molestie porta tellus molestie in. Nullam faucibus tortor vitae purus commodo ac posuere nisi posuere. Praesent mauris nulla, mattis nec viverra sed, dictum tempor augue.</p>
                         </div>
      


      would be much better indented if it became the following instead:
                         <div>
                             <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris nec sapien lacus. 
                             Sed interdum suscipit dapibus. Integer dictum ornare velit et posuere. Cras in metus risus. 
                             Mauris at vulputate lorem. Morbi gravida, nisl id viverra interdum, eros quam fringilla 
                             massa, vel rutrum justo sapien sed lectus. Etiam rutrum blandit commodo. Nullam ut sem 
                             a orci ullamcorper pretium. Aliquam et diam vitae risus varius imperdiet vel et tellus. 
                             Curabitur adipiscing eleifend ultrices. Vestibulum ultricies odio vestibulum massa 
                             malesuada id adipiscing magna lobortis. Proin bibendum ornare quam, molestie porta
                             tellus molestie in. Nullam faucibus tortor vitae purus commodo ac posuere nisi posuere.
                             Praesent mauris nulla, mattis nec viverra sed, dictum tempor augue.</p>
                         </div>
      



      I’m not sure how to accomplish these things, though. Is someone here smarter than me and know how to make these changes?
        • 6890
        • 21 Posts
        I’ve noticed the exact same issue, and with long enough code the indentation actually gets messed up somewhere along the way. Since initially posting the indentation code 6+ months ago, I’ve re-written it. This works flawlessly for me and would like some ’beta’ testers to provide feedback. I’ll be looking at converting this to an actual Revolution Plugin with options to remove comments, indentations settings, etc. It should also work for evolution.

        Install Instructions

        • Create a new Plugin - Call it anything you want. (I use xTidy)
        • Paste the entire code from below into the pulgin.
        • On the system events tab, ensure ’OnWebpagePreRender’ is enabled.
        • Save the plugin, load a page and view the source.

        <?php
        function clean_xhtml($string, $keep_tags = null) { 
        
            if (!$keep_tags) { 
                $keep_regexp = '~<script[^>]*>.*?<\/script>|<pre[^>]*>.*?<\/pre>|<textarea[^>]*>.*?<\/textarea>~s'; 
            } 
        
            // replace \r\n with \n 
            $string = preg_replace('~\r\n~ms', "\n", $string); 
        
            // replace \r with \n 
            $string = preg_replace('~\r~ms', "\n", $string); 
        
            // remove whitespace from the beginnig 
            $string = preg_replace('~^\s+~s', '', $string); 
        
            // remove whitespace from the end 
            $string = preg_replace('~\s+$~s', '', $string); 
        
            // store all tag which should remain the same 
            preg_match_all($keep_regexp, $string, $original_tags); 
        
            // remove whitespace from the beginning of each line 
            $string = preg_replace('~^\s+~m', '', $string); 
        
            // remove whitespace from the end of each line 
            $string = preg_replace('~\s+$~m', '', $string); 
        
            // removes empty lines 
            $string = preg_replace('~\n\s*(?=\n)~ms', '', $string); 
        
            // removes line breaks inside normal text 
            $string = preg_replace('~([^>\s])(\s\s+|\n)([^<\s])~m', '$1 $3', $string); 
        
            // correct indention 
            $indent = 0; 
            $string = explode("\n", $string); 
            foreach ($string as &$line) { 
                $correction = intval(substr($line, 0, 2) == '</'); // correct indention, if line starts with closing tag
                $line = str_repeat("\t", $indent - $correction).$line; 
                $indent += substr_count($line, '<'); // indent every tag 
                $indent -= substr_count($line, '<!'); // subtract doctype declaration 
                $indent -= substr_count($line, '<?'); // subtract processing instructions 
                $indent -= substr_count($line, '/>'); // subtract self closing tags 
                $indent -= substr_count($line, '</') * 2; // subtract closing tags 
            } 
            $string = implode("\n", $string); 
        
            // fetch all tag which could been changed 
            preg_match_all($keep_regexp, $string, $current_tags); 
        
            // restore all stored tags 
            foreach ($current_tags[0] as $key => $match) { 
                $string = str_replace($match, $original_tags[0][$key], $string); 
            } 
        
            return $string; 
        
        }
        
        $modx->documentOutput = clean_xhtml($modx->documentOutput);
        return;

          • 3749
          • 24,544 Posts
          Nice. cool

          You might consider using a property of the plugin to specify the indent string. That way users could choose to use spaces rather than a tab and choose how many spaces to indent.
            Did I help you? Buy me a beer
            Get my Book: MODX:The Official Guide
            MODX info for everyone: http://bobsguides.com/modx.html
            My MODX Extras
            Bob's Guides is now hosted at A2 MODX Hosting
            • 6890
            • 21 Posts
            Quote from: BobRay at Dec 15, 2010, 08:39 PM

            Nice. cool

            You might consider using a property of the plugin to specify the indent string. That way users could choose to use spaces rather than a tab and choose how many spaces to indent.

            Thanks for your suggestion BobRay. While it’s been a while, I’ve finally made it into a plugin titled ’xTidy’! Thanks to all who helped.
              • 36565
              • 73 Posts
              Using Revo, I have trouble getting xTidy to work. I pasted the plugin code into a new plugin "xTidy" and checked OnWebpagePrerender in System Events - but nothing happens. Source code looks as ugly as always.

              Is there something obvious I am missing?
                PHP illiterate MODx enthusiast
                • 36565
                • 73 Posts
                I just discovered something in my MODx error log that might be related. However, I can't make anything of it:
                [2011-09-27 16:10:24] (ERROR @ /home/neo/public_html/modx/core/cache/includes/elements/modplugin/5.include.cache.php : 46) PHP warning: preg_match_all(): Empty regular expression
                [2011-09-27 16:10:24] (ERROR @ /home/neo/public_html/modx/core/cache/includes/elements/modplugin/5.include.cache.php : 79) PHP warning: preg_match_all(): Empty regular expression
                [2011-09-27 16:10:24] (ERROR @ /home/neo/public_html/modx/core/cache/includes/elements/modplugin/5.include.cache.php : 80) PHP warning: Invalid argument supplied for foreach()
                [2011-09-27 16:13:46] (ERROR @ /home/neo/public_html/modx/core/cache/includes/elements/modplugin/5.include.cache.php : 46) PHP warning: preg_match_all(): Empty regular expression
                [2011-09-27 16:13:46] (ERROR @ /home/neo/public_html/modx/core/cache/includes/elements/modplugin/5.include.cache.php : 79) PHP warning: preg_match_all(): Empty regular expression
                [2011-09-27 16:13:46] (ERROR @ /home/neo/public_html/modx/core/cache/includes/elements/modplugin/5.include.cache.php : 80) PHP warning: Invalid argument supplied for foreach()
                [2011-09-27 16:17:01] (ERROR @ /home/neo/public_html/modx/core/cache/includes/elements/modplugin/5.include.cache.php : 46) PHP warning: preg_match_all(): Empty regular expression
                [2011-09-27 16:17:01] (ERROR @ /home/neo/public_html/modx/core/cache/includes/elements/modplugin/5.include.cache.php : 79) PHP warning: preg_match_all(): Empty regular expression
                [2011-09-27 16:17:01] (ERROR @ /home/neo/public_html/modx/core/cache/includes/elements/modplugin/5.include.cache.php : 80) PHP warning: Invalid argument supplied for foreach()

                Note: I get these even after using the downloaded plugin version xTidy 0.1.0-beta1.
                Thank you for having a look at it! [ed. note: neo2.0 last edited this post 15 years ago.]
                  PHP illiterate MODx enthusiast
                  • 11055 ☆ A M B ☆
                  • 3,112 Posts
                  what if you clear cache, and set the snippet uncached? [ed. note: goldsky last edited this post 14 years, 11 months ago.]
                    Rico
                    Genius is one percent inspiration and ninety-nine percent perspiration. Thomas A. Edison
                    MODx is great, but knowing how to use it well makes it perfect!

                    www.virtudraft.com

                    Security, security, security! | Indonesian MODx Forum | MODx Revo's cheatsheets | MODx Evo's cheatsheets

                    Author of Easy 2 Gallery 1.4.x, PHPTidy, spieFeed, FileDownload R, Upload To Users CMP, Inherit Template TV, LexRating, ExerPlan, Lingua, virtuNewsletter, Grid Class Key, SmartTag, prevNext

                    Maintainter/contributor of Babel

                    Because it's hard to follow all topics on the forum, PING ME ON TWITTER @_goldsky if you need my help.
                    • 36565
                    • 73 Posts
                    I take it that by 'set the snippet uncached' you mean unchecking the option 'clear cache when saving' (ad hoc translation from german: Cache beim Speichern leeren).

                    Well, that doesn't help. I don't know where to look for additional information on why that is so, I'm afraid.
                      PHP illiterate MODx enthusiast
                      • 11055 ☆ A M B ☆
                      • 3,112 Posts
                      sorry, I forgot that this is a plugin, not a snippet.

                      Use this:
                      <?php
                      
                      function clean_xhtml($string, $keep_tags = null) {
                      
                          if (!$keep_tags) {
                              $keep_regexp = '~<script[^>]*>.*?<\/script>|<pre[^>]*>.*?<\/pre>|<textarea[^>]*>.*?<\/textarea>~s';
                          }
                      
                          // replace \r\n with \n
                          $string = preg_replace('~\r\n~ms', "\n", $string);
                      
                          // replace \r with \n
                          $string = preg_replace('~\r~ms', "\n", $string);
                      
                          // remove whitespace from the beginnig
                          $string = preg_replace('~^\s+~s', '', $string);
                      
                          // remove whitespace from the end
                          $string = preg_replace('~\s+$~s', '', $string);
                      
                          // store all tag which should remain the same
                          preg_match_all($keep_regexp, $string, $original_tags);
                      
                          // remove whitespace from the beginning of each line
                          $string = preg_replace('~^\s+~m', '', $string);
                      
                          // remove whitespace from the end of each line
                          $string = preg_replace('~\s+$~m', '', $string);
                      
                          // removes empty lines
                          $string = preg_replace('~\n\s*(?=\n)~ms', '', $string);
                      
                          // removes line breaks inside normal text
                          $string = preg_replace('~([^>\s])(\s\s+|\n)([^<\s])~m', '$1 $3', $string);
                      
                          // correct indention
                          $indent = 0;
                          $string = explode("\n", $string);
                          foreach ($string as &$line) {
                              $correction = intval(substr($line, 0, 2) == '</'); // correct indention, if line starts with closing tag
                              $line = str_repeat("\t", $indent - $correction) . $line;
                              $indent += substr_count($line, '<'); // indent every tag
                              $indent -= substr_count($line, '<!'); // subtract doctype declaration
                              $indent -= substr_count($line, '<?'); // subtract processing instructions
                              $indent -= substr_count($line, '/>'); // subtract self closing tags
                              $indent -= substr_count($line, '</') * 2; // subtract closing tags
                          }
                          $string = implode("\n", $string);
                      
                          // fetch all tag which could been changed
                          preg_match_all($keep_regexp, $string, $current_tags);
                      
                          // restore all stored tags
                          foreach ($current_tags[0] as $key => $match) {
                              $string = str_replace($match, $original_tags[0][$key], $string);
                          }
                      
                          return $string;
                      }
                      
                      $eventName = $modx->event->name;
                      switch ($eventName) {
                          case 'OnWebPageInit':
                              /* do something */
                              break;
                          case 'OnWebPagePrerender':
                              $content = $modx->resource->_output;
                              $content =  clean_xhtml($content);
                              $modx->resource->_output = $content;
                              break;
                      }
                      
                      return;
                      
                        Rico
                        Genius is one percent inspiration and ninety-nine percent perspiration. Thomas A. Edison
                        MODx is great, but knowing how to use it well makes it perfect!

                        www.virtudraft.com

                        Security, security, security! | Indonesian MODx Forum | MODx Revo's cheatsheets | MODx Evo's cheatsheets

                        Author of Easy 2 Gallery 1.4.x, PHPTidy, spieFeed, FileDownload R, Upload To Users CMP, Inherit Template TV, LexRating, ExerPlan, Lingua, virtuNewsletter, Grid Class Key, SmartTag, prevNext

                        Maintainter/contributor of Babel

                        Because it's hard to follow all topics on the forum, PING ME ON TWITTER @_goldsky if you need my help.