[
  {
    "path": ".gitignore",
    "content": ".DS_Store\n*.gem\nbin/*.html\nhtml/\nvendor/\ndoc/\n.yardoc/\n*.sw?\npkg/\n.bundle/\n*.sublime-*\n"
  },
  {
    "path": ".jrubyrc",
    "content": "cext.enabled=true\n"
  },
  {
    "path": ".travis.yml",
    "content": "cache: bundler\nsudo: false\nbranches:\n  only: master\nmatrix:\n  fast_finish: true\nbefore_install: rm Gemfile.lock\nrvm:\n  - 2.0.0\n  - 2.1.0\n  - 2.2.0\n  - 2.3.0\n"
  },
  {
    "path": ".yardopts",
    "content": "--markup markdown\n--markup-provider redcarpet\n--charset utf-8\n--no-private\n--readme README.md\n--title \"Premailer Documentation\"\n-\nREADME.md\nLICENSE.md\n"
  },
  {
    "path": "Gemfile",
    "content": "source \"https://rubygems.org\"\n\ngem 'css_parser', :git => 'git://github.com/premailer/css_parser.git'\n\nplatforms :jruby do\n  gem 'jruby-openssl'\nend\n\ngemspec\n"
  },
  {
    "path": "LICENSE.md",
    "content": "# Premailer License\n\nCopyright (c) 2007-2012, Alex Dunae.  All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n* Neither the name of Premailer, Alex Dunae nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "# Premailer README [![Build Status](https://travis-ci.org/premailer/premailer.png?branch=master)](https://travis-ci.org/premailer/premailer)\n\n## What is this?\n\nFor the best HTML e-mail delivery results, CSS should be inline. This is a \nhuge pain and a simple newsletter becomes un-managable very quickly. This \nscript is my solution.\n\n* CSS styles are converted to inline style attributes\n  - Checks `style` and `link[rel=stylesheet]` tags and preserves existing inline attributes\n* Relative paths are converted to absolute paths\n  - Checks links in `href`, `src` and CSS `url('')`\n* CSS properties are checked against e-mail client capabilities\n  - Based on the Email Standards Project's guides\n* A plain text version is created (optional)\n\n## Premailer 2.0 is coming\n\nI'm looking for input on a version 2.0 update to Premailer. Please visit the [Premailer 2.0 Planning Page](https://github.com/premailer/premailer/wiki/New-Premailer-2.0-Planning) and give me your feedback.\n\n## Installation\n\nInstall the Premailer gem from RubyGems.\n\n```bash\ngem install premailer\n```\n\nor add it to your `Gemfile` and run `bundle`.\n\n## Example\n\n```ruby\nrequire 'rubygems' # optional for Ruby 1.9 or above.\nrequire 'premailer'\n\npremailer = Premailer.new('http://example.com/myfile.html', :warn_level => Premailer::Warnings::SAFE)\n\n# Write the HTML output\nFile.open(\"output.html\", \"w\") do |fout|\n  fout.puts premailer.to_inline_css\nend\n\n# Write the plain-text output\nFile.open(\"output.txt\", \"w\") do |fout|\n  fout.puts premailer.to_plain_text\nend\n\n# Output any CSS warnings\npremailer.warnings.each do |w|\n  puts \"#{w[:message]} (#{w[:level]}) may not render properly in #{w[:clients]}\"\nend\n```\n\n## Ruby Compatibility\n\nPremailer is tested on Ruby 1.8.7, Ruby 1.9.2, Ruby 1.9.3, and Ruby 2.x.0 . It also works on REE. JRuby support is close; contributors are welcome.  Checkout the latest build status on the [Travis CI dashboard](https://travis-ci.org/#!/premailer/premailer).\n\n## Premailer-specific CSS\n\nPremailer looks for a few CSS attributes that make working with tables a bit easier.\n\n| CSS Attribute | Availability |\n| ------------- | ------------ |\n| -premailer-width | Available on `table`, `th` and `td` elements |\n| -premailer-height | Available on `table`, `tr`, `th` and `td` elements |\n| -premailer-cellpadding | Available on `table` elements |\n| -premailer-cellspacing | Available on `table` elements |\n| data-premailer=\"ignore\" | Available on `link` and `style` elements. Premailer will ignore these elements entirely. |\n\nEach of these CSS declarations will be copied to appropriate element's attribute.\n\nFor example\n\n```css\ntable { -premailer-cellspacing: 5; -premailer-width: 500; }\n```\n\nwill result in \n\n```html\n<table cellspacing='5' width='500'>\n```\n\n## Contributions\n\nContributions are most welcome.  Premailer was rotting away in a private SVN repository for too long and could use some TLC.  Fork and patch to your heart's content.  Please don't increment the version numbers, though.\n\nA few areas that are particularly in need of love:\n\n* Improved test coverage\n* Move un-repeated background images defined in CSS for Outlook\n\n## Credits and code\n\nThanks to [all the wonderful contributors](https://github.com/premailer/premailer/contributors) for their updates.\n\nThanks to [Greenhood + Company](http://www.greenhood.com/) for sponsoring some of the 1.5.6 updates,\nand to [Campaign Monitor](https://www.campaignmonitor.com/) for supporting the web interface.\n\nThe web interface can be found at [premailer.dialect.ca](http://premailer.dialect.ca).\n\nThe source code can be found on [GitHub](https://github.com/alexdunae/premailer).\n\nCopyright by Alex Dunae (dunae.ca, e-mail 'code' at the same domain), 2007-2012.  See [LICENSE.md](https://github.com/alexdunae/premailer/blob/master/LICENSE.md) for license details.\n\n"
  },
  {
    "path": "Rakefile",
    "content": "require 'bundler/setup'\nrequire 'rake/testtask'\nrequire \"bundler/gem_tasks\"\nrequire 'yard'\n\nGEM_ROOT = File.dirname(__FILE__).freeze  unless defined?(GEM_ROOT)\n\nlib_path = File.expand_path('lib', GEM_ROOT)\n$LOAD_PATH.unshift(lib_path)  unless $LOAD_PATH.include? lib_path\n\nrequire 'premailer/version'\n\ndesc 'Parse a URL and write out the output.'\ntask :inline do\n  require 'premailer'\n\n  url = ENV['url']\n  output = ENV['output']\n\n  if !url or url.empty? or !output or output.empty?\n    puts 'Usage: rake inline url=http://example.com/ output=output.html'\n    exit\n  end\n\n  premailer = Premailer.new(url, :warn_level => Premailer::Warnings::SAFE, :verbose => true, :adapter => :nokogiri)\n  File.open(output, \"w\") do |fout|\n    fout.puts premailer.to_inline_css\n  end\n\n  puts \"Succesfully parsed '#{url}' into '#{output}'\"\n  puts premailer.warnings.length.to_s + ' CSS warnings were found'\nend\n\ntask :text do\n  require 'premailer'\n\n  url = ENV['url']\n  output = ENV['output']\n\n  if !url or url.empty? or !output or output.empty?\n    puts 'Usage: rake text url=http://example.com/ output=output.txt'\n    exit\n  end\n\n  premailer = Premailer.new(url, :warn_level => Premailer::Warnings::SAFE)\n  File.open(output, \"w\") do |fout|\n    fout.puts premailer.to_plain_text\n  end\n\n  puts \"Succesfully parsed '#{url}' into '#{output}'\"\nend\n\nRake::TestTask.new do |t|\n  t.test_files = FileList['test/test_*.rb']\n  t.verbose = false\n  t.warning = false\nend\n\nYARD::Rake::YardocTask.new do |yard|\n  yard.options << \"--title='Premailer #{Premailer::VERSION} Documentation'\"\nend\n\ntask :default => [:test]\n"
  },
  {
    "path": "bin/premailer",
    "content": "#!/usr/bin/env ruby\n\n# This binary used in rubygems environment only as part of installed gem\n\nrequire 'premailer/executor'\n\n"
  },
  {
    "path": "lib/premailer/adapter/hpricot.rb",
    "content": "require 'hpricot'\n\nclass Premailer\n  module Adapter\n    # Hpricot adapter\n    module Hpricot\n\n      # Merge CSS into the HTML document.\n      # @return [String] HTML.\n      def to_inline_css\n        doc = @processed_doc\n        @unmergable_rules = CssParser::Parser.new\n\n        # Give all styles already in style attributes a specificity of 1000\n        # per http://www.w3.org/TR/CSS21/cascade.html#specificity\n        doc.search(\"*[@style]\").each do |el|\n          el['style'] = '[SPEC=1000[' + el.attributes['style'] + ']]'\n        end\n\n        # Iterate through the rules and merge them into the HTML\n        @css_parser.each_selector(:all) do |selector, declaration, specificity, media_types|\n          # Save un-mergable rules separately\n          selector.gsub!(/:link([\\s]*)+/i) {|m| $1 }\n\n          # Convert element names to lower case\n          selector.gsub!(/([\\s]|^)([\\w]+)/) {|m| $1.to_s + $2.to_s.downcase }\n\n          if Premailer.is_media_query?(media_types) || selector =~ Premailer::RE_UNMERGABLE_SELECTORS\n            @unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration), media_types) unless @options[:preserve_styles]\n          else\n            begin\n              if selector =~ Premailer::RE_RESET_SELECTORS\n                # this is in place to preserve the MailChimp CSS reset: http://github.com/mailchimp/Email-Blueprints/\n                # however, this doesn't mean for testing pur\n                @unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration))  unless !@options[:preserve_reset]\n              end\n\n              # Change single ID CSS selectors into xpath so that we can match more\n              # than one element.  Added to work around dodgy generated code.\n              selector.gsub!(/\\A\\#([\\w_\\-]+)\\Z/, '*[@id=\\1]')\n\n              # convert attribute selectors to hpricot's format\n              selector.gsub!(/\\[([\\w]+)\\]/, '[@\\1]')\n              selector.gsub!(/\\[([\\w]+)([\\=\\~\\^\\$\\*]+)([\\w\\s]+)\\]/, '[@\\1\\2\\'\\3\\']')\n\n              doc.search(selector).each do |el|\n                if el.elem? and (el.name != 'head' and el.parent.name != 'head')\n                  # Add a style attribute or append to the existing one\n                  block = \"[SPEC=#{specificity}[#{declaration}]]\"\n                  el['style'] = (el.attributes['style'].to_s ||= '') + ' ' + block\n                end\n              end\n            rescue ::Hpricot::Error, RuntimeError, ArgumentError\n              $stderr.puts \"CSS syntax error with selector: #{selector}\" if @options[:verbose]\n              next\n            end\n          end\n        end\n\n        # Remove script tags\n        if @options[:remove_scripts]\n          doc.search(\"script\").remove\n        end\n\n        # Read STYLE attributes and perform folding\n        doc.search(\"*[@style]\").each do |el|\n          style = el.attributes['style'].to_s\n\n          declarations = []\n\n          style.scan(/\\[SPEC\\=([\\d]+)\\[(.[^\\]\\]]*)\\]\\]/).each do |declaration|\n            rs = CssParser::RuleSet.new(nil, declaration[1].to_s, declaration[0].to_i)\n            declarations << rs\n          end\n          # Perform style folding\n          merged = CssParser.merge(declarations)\n          merged.expand_shorthand!\n          merged.create_shorthand! if @options[:create_shorthands]\n\n          # Duplicate CSS attributes as HTML attributes\n          if Premailer::RELATED_ATTRIBUTES.has_key?(el.name) && @options[:css_to_attributes]\n            Premailer::RELATED_ATTRIBUTES[el.name].each do |css_att, html_att|\n              el[html_att] = merged[css_att].gsub(/url\\('(.*)'\\)/,'\\1').gsub(/;$|\\s*!important/, '').strip if el[html_att].nil? and not merged[css_att].empty?\n              merged.instance_variable_get(\"@declarations\").tap do |declarations|\n                declarations.delete(css_att)\n              end\n            end\n          end\n\n          # write the inline STYLE attribute\n          el['style'] = Premailer.escape_string(merged.declarations_to_s)\n        end\n\n        doc = write_unmergable_css_rules(doc, @unmergable_rules)\n\n        if @options[:remove_classes] or @options[:remove_comments]\n          doc.search('*').each do |el|\n            if el.comment? and @options[:remove_comments]\n              lst = el.parent.children\n              el.parent = nil\n              lst.delete(el)\n            elsif el.elem?\n              el.remove_attribute('class') if @options[:remove_classes]\n            end\n          end\n        end\n\n        if @options[:reset_contenteditable]\n          doc.search('*[@contenteditable]').each do |el|\n            el.remove_attribute('contenteditable')\n          end\n        end\n\n        if @options[:remove_ids]\n          # find all anchor's targets and hash them\n          targets = []\n          doc.search(\"a[@href^='#']\").each do |el|\n            target = el.get_attribute('href')[1..-1]\n            targets << target\n            el.set_attribute('href', \"#\" + Digest::MD5.hexdigest(target))\n          end\n          # hash ids that are links target, delete others\n          doc.search(\"*[@id]\").each do |el|\n            id = el.get_attribute('id')\n            if targets.include?(id)\n              el.set_attribute('id', Digest::MD5.hexdigest(id))\n            else\n              el.remove_attribute('id')\n            end\n          end\n        end\n\n        @processed_doc = doc\n\n        @processed_doc.to_original_html\n      end\n\n      # Create a <tt>style</tt> element with un-mergable rules (e.g. <tt>:hover</tt>)\n      # and write it into the <tt>body</tt>.\n      #\n      # <tt>doc</tt> is an Hpricot document and <tt>unmergable_css_rules</tt> is a Css::RuleSet.\n      #\n      # @return [::Hpricot] a document.\n      def write_unmergable_css_rules(doc, unmergable_rules) # :nodoc:\n        styles = unmergable_rules.to_s\n\n        unless styles.empty?\n          style_tag = \"\\n<style type=\\\"text/css\\\">\\n#{styles}</style>\\n\"\n          if head = doc.search('head')\n            head.append(style_tag)\n          elsif body = doc.search('body')\n            body.append(style_tag)\n          else\n            doc.inner_html= doc.inner_html << style_tag\n          end\n        end\n        doc\n      end\n\n\n      # Converts the HTML document to a format suitable for plain-text e-mail.\n      #\n      # If present, uses the <body> element as its base; otherwise uses the whole document.\n      #\n      # @return [String] Plain text.\n      def to_plain_text\n        html_src = ''\n        begin\n          html_src = @doc.search(\"body\").inner_html\n        rescue; end\n\n        html_src = @doc.to_html unless html_src and not html_src.empty?\n        convert_to_text(html_src, @options[:line_length], @html_encoding)\n      end\n\n\n      # Gets the original HTML as a string.\n      # @return [String] HTML.\n      def to_s\n        @doc.to_original_html\n      end\n\n      # Load the HTML file and convert it into an Hpricot document.\n      #\n      # @return [::Hpricot] a document.\n      def load_html(input) # :nodoc:\n        thing = nil\n\n        # TODO: duplicate options\n        if @options[:with_html_string] or @options[:inline] or input.respond_to?(:read)\n          thing = input\n        elsif @is_local_file\n          @base_dir = File.dirname(input)\n          thing = File.open(input, 'r')\n        else\n          thing = open(input)\n        end\n\n        # TODO: deal with Hpricot seg faults on empty input\n        thing ? Hpricot(thing) : nil\n      end\n\n    end\n  end\nend\n\n"
  },
  {
    "path": "lib/premailer/adapter/nokogiri.rb",
    "content": "require 'nokogiri'\n\nclass Premailer\n  module Adapter\n    # Nokogiri adapter\n    module Nokogiri\n\n      # Merge CSS into the HTML document.\n      #\n      # @return [String] an HTML.\n      def to_inline_css\n        doc = @processed_doc\n        @unmergable_rules = CssParser::Parser.new\n\n        # Give all styles already in style attributes a specificity of 1000\n        # per http://www.w3.org/TR/CSS21/cascade.html#specificity\n        doc.search(\"*[@style]\").each do |el|\n          el['style'] = '[SPEC=1000[' + el.attributes['style'] + ']]'\n        end\n        # Iterate through the rules and merge them into the HTML\n        @css_parser.each_selector(:all) do |selector, declaration, specificity, media_types|\n          # Save un-mergable rules separately\n          selector.gsub!(/:link([\\s]*)+/i) { |m| $1 }\n\n          # Convert element names to lower case\n          selector.gsub!(/([\\s]|^)([\\w]+)/) { |m| $1.to_s + $2.to_s.downcase }\n\n          if Premailer.is_media_query?(media_types) || selector =~ Premailer::RE_UNMERGABLE_SELECTORS\n            @unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration), media_types) unless @options[:preserve_styles]\n          else\n            begin\n              if selector =~ Premailer::RE_RESET_SELECTORS\n                # this is in place to preserve the MailChimp CSS reset: http://github.com/mailchimp/Email-Blueprints/\n                # however, this doesn't mean for testing pur\n                @unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration)) unless !@options[:preserve_reset]\n              end\n\n              # Change single ID CSS selectors into xpath so that we can match more\n              # than one element.  Added to work around dodgy generated code.\n              selector.gsub!(/\\A\\#([\\w_\\-]+)\\Z/, '*[@id=\\1]')\n\n              doc.search(selector).each do |el|\n                if el.elem? and (el.name != 'head' and el.parent.name != 'head')\n                  # Add a style attribute or append to the existing one\n                  block = \"[SPEC=#{specificity}[#{declaration}]]\"\n                  el['style'] = (el.attributes['style'].to_s ||= '') + ' ' + block\n                end\n              end\n            rescue ::Nokogiri::SyntaxError, RuntimeError, ArgumentError\n              $stderr.puts \"CSS syntax error with selector: #{selector}\" if @options[:verbose]\n              next\n            end\n          end\n        end\n\n        # Remove script tags\n        if @options[:remove_scripts]\n          doc.search(\"script\").remove\n        end\n\n        # Read STYLE attributes and perform folding\n        doc.search(\"*[@style]\").each do |el|\n          style = el.attributes['style'].to_s\n\n          declarations = []\n          style.scan(/\\[SPEC\\=([\\d]+)\\[(.[^\\]\\]]*)\\]\\]/).each do |declaration|\n            rs = CssParser::RuleSet.new(nil, declaration[1].to_s, declaration[0].to_i)\n            declarations << rs\n          end\n\n          # Perform style folding\n          merged = CssParser.merge(declarations)\n          merged.expand_shorthand!\n\n          # Duplicate CSS attributes as HTML attributes\n          if Premailer::RELATED_ATTRIBUTES.has_key?(el.name) && @options[:css_to_attributes]\n            Premailer::RELATED_ATTRIBUTES[el.name].each do |css_att, html_att|\n              el[html_att] = merged[css_att].gsub(/url\\(['|\"](.*)['|\"]\\)/, '\\1').gsub(/;$|\\s*!important/, '').strip if el[html_att].nil? and not merged[css_att].empty?\n              merged.instance_variable_get(\"@declarations\").tap do |declarations|\n                declarations.delete(css_att)\n              end\n            end\n          end\n          # Collapse multiple rules into one as much as possible.\n          merged.create_shorthand! if @options[:create_shorthands]\n\n          # write the inline STYLE attribute\n          # split by ';' but ignore those in brackets\n          attributes = Premailer.escape_string(merged.declarations_to_s).split(/;(?![^(]*\\))/).map(&:strip)\n          attributes = attributes.map { |attr| [attr.split(':').first, attr] }.sort_by { |pair| pair.first }.map { |pair| pair[1] }\n          el['style'] = attributes.join('; ') + \";\"\n        end\n\n        doc = write_unmergable_css_rules(doc, @unmergable_rules)\n\n        if @options[:remove_classes] or @options[:remove_comments]\n          doc.traverse do |el|\n            if el.comment? and @options[:remove_comments]\n              el.remove\n            elsif el.element?\n              el.remove_attribute('class') if @options[:remove_classes]\n            end\n          end\n        end\n\n        if @options[:remove_ids]\n          # find all anchor's targets and hash them\n          targets = []\n          doc.search(\"a[@href^='#']\").each do |el|\n            target = el.get_attribute('href')[1..-1]\n            targets << target\n            el.set_attribute('href', \"#\" + Digest::MD5.hexdigest(target))\n          end\n          # hash ids that are links target, delete others\n          doc.search(\"*[@id]\").each do |el|\n            id = el.get_attribute('id')\n            if targets.include?(id)\n              el.set_attribute('id', Digest::MD5.hexdigest(id))\n            else\n              el.remove_attribute('id')\n            end\n          end\n        end\n\n        if @options[:reset_contenteditable]\n          doc.search('*[@contenteditable]').each do |el|\n            el.remove_attribute('contenteditable')\n          end\n        end\n\n        @processed_doc = doc\n        if is_xhtml?\n          # we don't want to encode carriage returns\n          @processed_doc.to_xhtml(:encoding => @options[:output_encoding]).gsub(/&\\#(xD|13);/i, \"\\r\")\n        else\n          @processed_doc.to_html(:encoding => @options[:output_encoding])\n        end\n      end\n\n      # Create a <tt>style</tt> element with un-mergable rules (e.g. <tt>:hover</tt>)\n      # and write it into the <tt>body</tt>.\n      #\n      # <tt>doc</tt> is an Nokogiri document and <tt>unmergable_css_rules</tt> is a Css::RuleSet.\n      #\n      # @return [::Nokogiri::XML] a document.\n      def write_unmergable_css_rules(doc, unmergable_rules) # :nodoc:\n        styles = unmergable_rules.to_s\n\n        unless styles.empty?\n          style_tag = \"<style type=\\\"text/css\\\">\\n#{styles}</style>\"\n          unless (body = doc.search('body')).empty?\n            if doc.at_css('body').children && !doc.at_css('body').children.empty?\n              doc.at_css('body').children.before(::Nokogiri::XML.fragment(style_tag))\n            else\n              doc.at_css('body').add_child(::Nokogiri::XML.fragment(style_tag))\n            end\n          else\n            doc.inner_html = style_tag += doc.inner_html\n          end\n        end\n        doc\n      end\n\n\n      # Converts the HTML document to a format suitable for plain-text e-mail.\n      #\n      # If present, uses the <body> element as its base; otherwise uses the whole document.\n      #\n      # @return [String] a plain text.\n      def to_plain_text\n        html_src = ''\n        begin\n          html_src = @doc.at(\"body\").inner_html\n        rescue;\n        end\n\n        html_src = @doc.to_html unless html_src and not html_src.empty?\n        convert_to_text(html_src, @options[:line_length], @html_encoding)\n      end\n\n      # Gets the original HTML as a string.\n      # @return [String] HTML.\n      def to_s\n        if is_xhtml?\n          @doc.to_xhtml(:encoding => nil)\n        else\n          @doc.to_html(:encoding => nil)\n        end\n      end\n\n      # Load the HTML file and convert it into an Nokogiri document.\n      #\n      # @return [::Nokogiri::XML] a document.\n      def load_html(input) # :nodoc:\n        thing = nil\n\n        # TODO: duplicate options\n        if @options[:with_html_string] or @options[:inline] or input.respond_to?(:read)\n          thing = input\n        elsif @is_local_file\n          @base_dir = File.dirname(input)\n          thing = File.open(input, 'r')\n        else\n          thing = open(input)\n        end\n\n        if thing.respond_to?(:read)\n          thing = thing.read\n        end\n\n        return nil unless thing\n        doc = nil\n\n        # Handle HTML entities\n        if @options[:replace_html_entities] == true and thing.is_a?(String)\n          HTML_ENTITIES.map do |entity, replacement|\n            thing.gsub! entity, replacement\n          end\n        end\n        # Default encoding is ASCII-8BIT (binary) per http://groups.google.com/group/nokogiri-talk/msg/0b81ef0dc180dc74\n        # However, we really don't want to hardcode this. ASCII-8BIT should be the default, but not the only option.\n        if thing.is_a?(String) and RUBY_VERSION =~ /1.9/\n          thing = thing.force_encoding(@options[:input_encoding]).encode!\n          doc = ::Nokogiri::HTML(thing, nil, @options[:input_encoding]) { |c| c.recover }\n        else\n          default_encoding = RUBY_PLATFORM == 'java' ? nil : 'BINARY'\n          doc = ::Nokogiri::HTML(thing, nil, @options[:input_encoding] || default_encoding) { |c| c.recover }\n        end\n\n        # Fix for removing any CDATA tags from both style and script tags inserted per\n        # https://github.com/sparklemotion/nokogiri/issues/311 and\n        # https://github.com/premailer/premailer/issues/199\n        %w(style script).each do |tag|\n          doc.search(tag).children.each do |child|\n            child.swap(child.text()) if child.cdata?\n          end\n        end\n\n        doc\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "lib/premailer/adapter/nokogumbo.rb",
    "content": "require 'nokogumbo'\n\nclass Premailer\n  module Adapter\n    # Nokogiri adapter\n    module Nokogumbo\n\n      # Merge CSS into the HTML document.\n      #\n      # @return [String] an HTML.\n      def to_inline_css\n        doc = @processed_doc\n        @unmergable_rules = CssParser::Parser.new\n\n        # Give all styles already in style attributes a specificity of 1000\n        # per http://www.w3.org/TR/CSS21/cascade.html#specificity\n        doc.search(\"*[@style]\").each do |el|\n          el['style'] = '[SPEC=1000[' + el.attributes['style'] + ']]'\n        end\n        # Iterate through the rules and merge them into the HTML\n        @css_parser.each_selector(:all) do |selector, declaration, specificity, media_types|\n          # Save un-mergable rules separately\n          selector.gsub!(/:link([\\s]*)+/i) { |m| $1 }\n\n          # Convert element names to lower case\n          selector.gsub!(/([\\s]|^)([\\w]+)/) { |m| $1.to_s + $2.to_s.downcase }\n\n          if Premailer.is_media_query?(media_types) || selector =~ Premailer::RE_UNMERGABLE_SELECTORS\n            @unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration), media_types) unless @options[:preserve_styles]\n          else\n            begin\n              if selector =~ Premailer::RE_RESET_SELECTORS\n                # this is in place to preserve the MailChimp CSS reset: http://github.com/mailchimp/Email-Blueprints/\n                # however, this doesn't mean for testing pur\n                @unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration)) unless !@options[:preserve_reset]\n              end\n\n              # Change single ID CSS selectors into xpath so that we can match more\n              # than one element.  Added to work around dodgy generated code.\n              selector.gsub!(/\\A\\#([\\w_\\-]+)\\Z/, '*[@id=\\1]')\n\n              doc.search(selector).each do |el|\n                if el.elem? and (el.name != 'head' and el.parent.name != 'head')\n                  # Add a style attribute or append to the existing one\n                  block = \"[SPEC=#{specificity}[#{declaration}]]\"\n                  el['style'] = (el.attributes['style'].to_s ||= '') + ' ' + block\n                end\n              end\n            rescue ::Nokogiri::SyntaxError, RuntimeError, ArgumentError\n              $stderr.puts \"CSS syntax error with selector: #{selector}\" if @options[:verbose]\n              next\n            end\n          end\n        end\n\n        # Remove script tags\n        if @options[:remove_scripts]\n          doc.search(\"script\").remove\n        end\n\n        # Read STYLE attributes and perform folding\n        doc.search(\"*[@style]\").each do |el|\n          style = el.attributes['style'].to_s\n\n          declarations = []\n          style.scan(/\\[SPEC\\=([\\d]+)\\[(.[^\\]\\]]*)\\]\\]/).each do |declaration|\n            rs = CssParser::RuleSet.new(nil, declaration[1].to_s, declaration[0].to_i)\n            declarations << rs\n          end\n\n          # Perform style folding\n          merged = CssParser.merge(declarations)\n          merged.expand_shorthand!\n\n          # Duplicate CSS attributes as HTML attributes\n          if Premailer::RELATED_ATTRIBUTES.has_key?(el.name) && @options[:css_to_attributes]\n            Premailer::RELATED_ATTRIBUTES[el.name].each do |css_att, html_att|\n              el[html_att] = merged[css_att].gsub(/url\\(['|\"](.*)['|\"]\\)/, '\\1').gsub(/;$|\\s*!important/, '').strip if el[html_att].nil? and not merged[css_att].empty?\n              merged.instance_variable_get(\"@declarations\").tap do |declarations|\n                declarations.delete(css_att)\n              end\n            end\n          end\n          # Collapse multiple rules into one as much as possible.\n          merged.create_shorthand! if @options[:create_shorthands]\n\n          # write the inline STYLE attribute\n          attributes = Premailer.escape_string(merged.declarations_to_s).split(';').map(&:strip)\n          attributes = attributes.map { |attr| [attr.split(':').first, attr] }.sort_by { |pair| pair.first }.map { |pair| pair[1] }\n          el['style'] = attributes.join('; ') + \";\"\n        end\n\n        doc = write_unmergable_css_rules(doc, @unmergable_rules)\n\n        if @options[:remove_classes] or @options[:remove_comments]\n          doc.traverse do |el|\n            if el.comment? and @options[:remove_comments]\n              el.remove\n            elsif el.element?\n              el.remove_attribute('class') if @options[:remove_classes]\n            end\n          end\n        end\n\n        if @options[:remove_ids]\n          # find all anchor's targets and hash them\n          targets = []\n          doc.search(\"a[@href^='#']\").each do |el|\n            target = el.get_attribute('href')[1..-1]\n            targets << target\n            el.set_attribute('href', \"#\" + Digest::MD5.hexdigest(target))\n          end\n          # hash ids that are links target, delete others\n          doc.search(\"*[@id]\").each do |el|\n            id = el.get_attribute('id')\n            if targets.include?(id)\n              el.set_attribute('id', Digest::MD5.hexdigest(id))\n            else\n              el.remove_attribute('id')\n            end\n          end\n        end\n\n        if @options[:reset_contenteditable]\n          doc.search('*[@contenteditable]').each do |el|\n            el.remove_attribute('contenteditable')\n          end\n        end\n\n        @processed_doc = doc\n        if is_xhtml?\n          # we don't want to encode carriage returns\n          @processed_doc.to_xhtml(:encoding => @options[:output_encoding]).gsub(/&\\#(xD|13);/i, \"\\r\")\n        else\n          @processed_doc.to_html(:encoding => @options[:output_encoding])\n        end\n      end\n\n      # Create a <tt>style</tt> element with un-mergable rules (e.g. <tt>:hover</tt>)\n      # and write it into the <tt>body</tt>.\n      #\n      # <tt>doc</tt> is an Nokogiri document and <tt>unmergable_css_rules</tt> is a Css::RuleSet.\n      #\n      # @return [::Nokogiri::XML] a document.\n      def write_unmergable_css_rules(doc, unmergable_rules) # :nodoc:\n        styles = unmergable_rules.to_s\n\n        unless styles.empty?\n          style_tag = \"<style type=\\\"text/css\\\">\\n#{styles}</style>\"\n          unless (body = doc.search('body')).empty?\n            if doc.at_css('body').children && !doc.at_css('body').children.empty?\n              doc.at_css('body').children.before(::Nokogiri::XML.fragment(style_tag))\n            else\n              doc.at_css('body').add_child(::Nokogiri::XML.fragment(style_tag))\n            end\n          else\n            doc.inner_html = style_tag += doc.inner_html\n          end\n        end\n        doc\n      end\n\n\n      # Converts the HTML document to a format suitable for plain-text e-mail.\n      #\n      # If present, uses the <body> element as its base; otherwise uses the whole document.\n      #\n      # @return [String] a plain text.\n      def to_plain_text\n        html_src = ''\n        begin\n          html_src = @doc.at(\"body\").inner_html\n        rescue;\n        end\n\n        html_src = @doc.to_html unless html_src and not html_src.empty?\n        convert_to_text(html_src, @options[:line_length], @html_encoding)\n      end\n\n      # Gets the original HTML as a string.\n      # @return [String] HTML.\n      def to_s\n        if is_xhtml?\n          @doc.to_xhtml(:encoding => nil)\n        else\n          @doc.to_html(:encoding => nil)\n        end\n      end\n\n      # Load the HTML file and convert it into an Nokogiri document.\n      #\n      # @return [::Nokogiri::XML] a document.\n      def load_html(input) # :nodoc:\n        thing = nil\n\n        # TODO: duplicate options\n        if @options[:with_html_string] or @options[:inline] or input.respond_to?(:read)\n          thing = input\n        elsif @is_local_file\n          @base_dir = File.dirname(input)\n          thing = File.open(input, 'r')\n        else\n          thing = open(input)\n        end\n\n        if thing.respond_to?(:read)\n          thing = thing.read\n        end\n\n        return nil unless thing\n        doc = nil\n\n        # Handle HTML entities\n        if @options[:replace_html_entities] == true and thing.is_a?(String)\n          HTML_ENTITIES.map do |entity, replacement|\n            thing.gsub! entity, replacement\n          end\n        end\n        # Default encoding is ASCII-8BIT (binary) per http://groups.google.com/group/nokogiri-talk/msg/0b81ef0dc180dc74\n        # However, we really don't want to hardcode this. ASCII-8BIT should be the default, but not the only option.\n        if thing.is_a?(String) and RUBY_VERSION =~ /1.9/\n          thing = thing.force_encoding(@options[:input_encoding]).encode!\n          doc = ::Nokogiri::HTML5(thing)\n        else\n          default_encoding = RUBY_PLATFORM == 'java' ? nil : 'BINARY'\n          doc = ::Nokogiri::HTML5(thing)\n        end\n\n        # Fix for removing any CDATA tags from both style and script tags inserted per\n        # https://github.com/sparklemotion/nokogiri/issues/311 and\n        # https://github.com/premailer/premailer/issues/199\n        %w(style script).each do |tag|\n          doc.search(tag).children.each do |child|\n            child.swap(child.text()) if child.cdata?\n          end\n        end\n\n        doc\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "lib/premailer/adapter.rb",
    "content": "\n\nclass Premailer\n  # Manages the adapter classes. Currently supports:\n  #\n  # * nokogiri\n  # * hpricot\n  module Adapter\n\n    autoload :Hpricot, 'premailer/adapter/hpricot'\n    autoload :Nokogiri, 'premailer/adapter/nokogiri'\n    autoload :Nokogumbo, 'premailer/adapter/nokogumbo'\n\n    # adapter to required file mapping.\n    REQUIREMENT_MAP = [\n      [\"hpricot\",  :hpricot],\n      [\"nokogiri\", :nokogiri],\n      [\"nokogumbi\", :nokogumbo],\n    ]\n\n    # Returns the adapter to use.\n    def self.use\n      return @use if @use\n      self.use = self.default\n      @use\n    end\n\n    # The default adapter based on what you currently have loaded and\n    # installed. First checks to see if any adapters are already loaded,\n    # then checks to see which are installed if none are loaded.\n    # @raise [RuntimeError] unless suitable adapter found.\n    def self.default\n      return :hpricot  if defined?(::Hpricot)\n      return :nokogiri if defined?(::Nokogiri)\n      return :nokogumbo if defined?(::Nokogumbo)\n\n      REQUIREMENT_MAP.each do |(library, adapter)|\n        begin\n          require library\n          return adapter\n        rescue LoadError\n          next\n        end\n      end\n\n      raise RuntimeError.new(\"No suitable adapter for Premailer was found, please install hpricot or nokogiri\")\n    end\n\n    # Sets the adapter to use.\n    # @raise [ArgumentError] unless the adapter exists.\n    def self.use=(new_adapter)\n      @use = find(new_adapter)\n    end\n\n    # Returns an adapter.\n    # @raise [ArgumentError] unless the adapter exists.\n    def self.find(adapter)\n      return adapter if adapter.is_a?(Module)\n\n      Premailer::Adapter.const_get(\"#{adapter.to_s.split('_').map{|s| s.capitalize}.join('')}\")\n    rescue NameError\n      raise ArgumentError, \"Invalid adapter: #{adapter}\"\n    end\n\n  end\nend\n"
  },
  {
    "path": "lib/premailer/executor.rb",
    "content": "require 'optparse'\nrequire 'premailer'\n\n# defaults\noptions = {\n  :base_url => nil,\n  :link_query_string => nil,\n  :remove_classes => false,\n  :verbose => false,\n  :line_length => 65\n}\n\nmode = :html\n\nopts = OptionParser.new do |opts|\n  opts.banner = \"Improve the rendering of HTML emails by making CSS inline among other things. Takes a path to a local file, a URL or a pipe as input.\\n\\n\"\n  opts.define_head \"Usage: premailer <optional uri|optional path> [options]\"\n  opts.separator \"\"\n  opts.separator \"Examples:\"\n  opts.separator \"  premailer http://example.com/ > out.html\"\n  opts.separator \"  premailer http://example.com/ --mode txt > out.txt\"\n  opts.separator \"  cat input.html | premailer -q src=email > out.html\"\n  opts.separator \"  premailer ./public/index.html\"\n  opts.separator \"\"\n  opts.separator \"Options:\"\n\n  opts.on(\"--mode MODE\", [:html, :txt], \"Output: html or txt\") do |v|\n    mode = v\n  end\n\n  opts.on(\"-b\", \"--base-url STRING\", String, \"Base URL, useful for local files\") do |v|\n    options[:base_url] = v\n  end\n\n  opts.on(\"-q\", \"--query-string STRING\", String, \"Query string to append to links\") do |v|\n    options[:link_query_string] = v\n  end\n\n  opts.on(\"--css FILE,FILE\", Array, \"Additional CSS stylesheets\") do |v|\n    options[:css] = v\n  end\n\n  opts.on(\"-r\", \"--remove-classes\", \"Remove HTML classes\") do |v|\n    options[:remove_classes] = v\n  end\n\n  opts.on(\"-j\", \"--remove-scripts\", \"Remove <script> elements\") do |v|\n    options[:remove_classes] = v\n  end\n\n  opts.on(\"-l\", \"--line-length N\", Integer, \"Line length for plaintext (default: #{options[:line_length].to_s})\") do |v|\n    options[:line_length] = v\n  end\n\n  opts.on(\"-e\", \"--entities\", \"Output HTML entities instead of UTF-8 when using Nokogiri\") do |v|\n    options[:output_encoding] = \"US-ASCII\"\n  end\n\n  opts.on(\"-d\", \"--io-exceptions\", \"Abort on I/O errors\") do |v|\n    options[:io_exceptions] = v\n  end\n\n  opts.on(\"-v\", \"--verbose\", \"Print additional information at runtime\") do |v|\n    options[:verbose] = v\n  end\n\n  opts.on_tail(\"-?\", \"--help\", \"Show this message\") do\n    puts opts\n    exit\n  end\n\n  opts.on_tail(\"-V\", \"--version\", \"Show version\") do\n    puts \"Premailer #{Premailer::VERSION} (c) 2008-2010 Alex Dunae\"\n    exit\n  end\nend\nopts.parse!\n\n$stderr.puts \"Processing in #{mode} mode with options #{options.inspect}\" if options[:verbose]\n\npremailer = nil\ninput = nil\n\nif ARGV.size > 0\n  # Executed via command line or shell script\n  input = ARGV.shift\nelse\n  # Called in piped command\n  input = $stdin.read\n  options[:with_html_string] = true\nend\n\nif input\n  premailer = Premailer.new(input, options)\nelse\n  puts opts\n  exit 1\nend\n\nif mode == :txt\n  print premailer.to_plain_text\nelse\n  print premailer.to_inline_css\nend\n\nexit\n"
  },
  {
    "path": "lib/premailer/html_to_plain_text.rb",
    "content": "# coding: utf-8\nrequire 'htmlentities'\n\n# Support functions for Premailer\nmodule HtmlToPlainText\n\n  # Returns the text in UTF-8 format with all HTML tags removed\n  #\n  # TODO: add support for DL, OL\n  def convert_to_text(html, line_length = 65, from_charset = 'UTF-8')\n    txt = html\n\n    # strip text ignored html. Useful for removing\n    # headers and footers that aren't needed in the\n    # text version\n    txt.gsub!(/<!-- start text\\/html -->.*?<!-- end text\\/html -->/m, '')\n\n    # replace images with their alt attributes\n    # for img tags with \"\" for attribute quotes\n    # with or without closing tag\n    # eg. the following formats:\n    # <img alt=\"\" />\n    # <img alt=\"\">\n    txt.gsub!(/<img.+?alt=\\\"([^\\\"]*)\\\"[^>]*\\>/i, '\\1')\n\n    # for img tags with '' for attribute quotes\n    # with or without closing tag\n    # eg. the following formats:\n    # <img alt='' />\n    # <img alt=''>\n    txt.gsub!(/<img.+?alt=\\'([^\\']*)\\'[^>]*\\>/i, '\\1')\n\n    # links\n    txt.gsub!(/<a\\s.*?href=[\"'](mailto:)?([^\"']*)[\"'][^>]*>((.|\\s)*?)<\\/a>/i) do |s|\n      if $3.empty?\n        ''\n      else\n        $3.strip + ' ( ' + $2.strip + ' )'\n      end\n    end\n\n    # handle headings (H1-H6)\n    txt.gsub!(/(<\\/h[1-6]>)/i, \"\\n\\\\1\") # move closing tags to new lines\n    txt.gsub!(/[\\s]*<h([1-6]+)[^>]*>[\\s]*(.*)[\\s]*<\\/h[1-6]+>/i) do |s|\n      hlevel = $1.to_i\n\n      htext = $2\n      htext.gsub!(/<br[\\s]*\\/?>/i, \"\\n\") # handle <br>s\n      htext.gsub!(/<\\/?[^>]*>/i, '') # strip tags\n\n      # determine maximum line length\n      hlength = 0\n      htext.each_line { |l| llength = l.strip.length; hlength = llength if llength > hlength }\n      hlength = line_length if hlength > line_length\n\n      case hlevel\n        when 1   # H1, asterisks above and below\n          htext = ('*' * hlength) + \"\\n\" + htext + \"\\n\" + ('*' * hlength)\n        when 2   # H1, dashes above and below\n          htext = ('-' * hlength) + \"\\n\" + htext + \"\\n\" + ('-' * hlength)\n        else     # H3-H6, dashes below\n          htext = htext + \"\\n\" + ('-' * hlength)\n      end\n\n      \"\\n\\n\" + htext + \"\\n\\n\"\n    end\n\n    # wrap spans\n    txt.gsub!(/(<\\/span>)[\\s]+(<span)/mi, '\\1 \\2')\n\n    # lists -- TODO: should handle ordered lists\n    txt.gsub!(/[\\s]*(<li[^>]*>)[\\s]*/i, '* ')\n    # list not followed by a newline\n    txt.gsub!(/<\\/li>[\\s]*(?![\\n])/i, \"\\n\")\n\n    # paragraphs and line breaks\n    txt.gsub!(/<\\/p>/i, \"\\n\\n\")\n    txt.gsub!(/<br[\\/ ]*>/i, \"\\n\")\n\n    # strip remaining tags\n    txt.gsub!(/<\\/?[^>]*>/, '')\n\n    # decode HTML entities\n    he = HTMLEntities.new\n    txt = he.decode(txt)\n\n    # no more than two consecutive spaces\n    txt.gsub!(/ {2,}/, \" \")\n\n    txt = word_wrap(txt, line_length)\n\n    # remove linefeeds (\\r\\n and \\r -> \\n)\n    txt.gsub!(/\\r\\n?/, \"\\n\")\n\n    # strip extra spaces\n    txt.gsub!(/[ \\t]*\\302\\240+[ \\t]*/, \" \") # non-breaking spaces -> spaces\n    txt.gsub!(/\\n[ \\t]+/, \"\\n\") # space at start of lines\n    txt.gsub!(/[ \\t]+\\n/, \"\\n\") # space at end of lines\n\n    # no more than two consecutive newlines\n    txt.gsub!(/[\\n]{3,}/, \"\\n\\n\")\n\n    # the word messes up the parens\n    txt.gsub!(/\\(([ \\n])(http[^)]+)([\\n ])\\)/) do |s|\n      ($1 == \"\\n\" ? $1 : '' ) + '( ' + $2 + ' )' + ($3 == \"\\n\" ? $1 : '' )\n    end\n\n    txt.strip\n  end\n\n  # Taken from Rails' word_wrap helper (http://api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html#method-i-word_wrap)\n  def word_wrap(txt, line_length)\n    txt.split(\"\\n\").collect do |line|\n      line.length > line_length ? line.gsub(/(.{1,#{line_length}})(\\s+|$)/, \"\\\\1\\n\").strip : line\n    end * \"\\n\"\n  end\nend\n"
  },
  {
    "path": "lib/premailer/premailer.rb",
    "content": "# Premailer processes HTML and CSS to improve e-mail deliverability.\n#\n# Premailer's main function is to render all CSS as inline <tt>style</tt>\n# attributes. It also converts relative links to absolute links and checks\n# the 'safety' of CSS properties against a CSS support chart.\n#\n# ## Example of use\n#\n# ```ruby\n# premailer = Premailer.new('http://example.com/myfile.html', :warn_level => Premailer::Warnings::SAFE)\n#\n# # Write the HTML output\n# fout = File.open(\"output.html\", \"w\")\n# fout.puts premailer.to_inline_css\n# fout.close\n#\n# # Write the plain-text output\n# fout = File.open(\"ouput.txt\", \"w\")\n# fout.puts premailer.to_plain_text\n# fout.close\n#\n# # List any CSS warnings\n# puts premailer.warnings.length.to_s + ' warnings found'\n# premailer.warnings.each do |w|\n#   puts \"#{w[:message]} (#{w[:level]}) may not render properly in #{w[:clients]}\"\n# end\n#\n# premailer = Premailer.new(html_file, :warn_level => Premailer::Warnings::SAFE)\n# puts premailer.to_inline_css\n# ```\n#\nrequire 'premailer/version'\n\nclass Premailer\n  include HtmlToPlainText\n  include CssParser\n\n  CLIENT_SUPPORT_FILE = File.dirname(__FILE__) + '/../../misc/client_support.yaml'\n\n  # Unmergable selectors regexp.\n  RE_UNMERGABLE_SELECTORS = /(\\:(visited|active|hover|focus|after|before|selection|target|first\\-(line|letter))|^\\@)/i\n  # Reset selectors regexp.\n  RE_RESET_SELECTORS = /^(\\:\\#outlook|body.*|\\.ReadMsgBody|\\.ExternalClass|img|\\#backgroundTable)$/\n\n  # list of HTMLEntities to fix\n  # source: http://stackoverflow.com/questions/2812781/how-to-convert-webpage-apostrophe-8217-to-ascii-39-in-ruby-1-\n  HTML_ENTITIES = {\n    \"&#8217;\" => \"'\",\n    \"&#8230;\" => \"...\",\n    \"&#8216;\" => \"'\",\n    \"&#8218;\" => ',',\n    \"&#8219;\" => \"'\",\n    \"&#8220;\" => '\"',\n    \"&#8221;\" => '\"',\n    \"&#8208;\" => '-',\n    \"&#8211;\" => '-',\n    \"&#8212;\" => '--',\n    \"&#8213;\" => '--'\n  }\n\n  # list of CSS attributes that can be rendered as HTML attributes\n  #\n  # @todo too much repetition\n  # @todo background=\"\"\n  RELATED_ATTRIBUTES = {\n    'h1' => {'text-align' => 'align'},\n    'h2' => {'text-align' => 'align'},\n    'h3' => {'text-align' => 'align'},\n    'h4' => {'text-align' => 'align'},\n    'h5' => {'text-align' => 'align'},\n    'h6' => {'text-align' => 'align'},\n    'p' => {'text-align' => 'align'},\n    'div' => {'text-align' => 'align'},\n    'blockquote' => {'text-align' => 'align'},\n    'body' => {'background-color' => 'bgcolor'},\n    'table' => {\n      '-premailer-align' => 'align',\n      'background-color' => 'bgcolor',\n      'background-image' => 'background',\n      '-premailer-width' => 'width',\n      '-premailer-height' => 'height',\n      '-premailer-cellpadding' => 'cellpadding',\n      '-premailer-cellspacing' => 'cellspacing'\n    },\n    'tr' => {\n      'text-align' => 'align',\n      'background-color' => 'bgcolor',\n      '-premailer-height' => 'height'\n    },\n    'th' => {\n      'text-align' => 'align',\n      'background-color' => 'bgcolor',\n      'vertical-align' => 'valign',\n      '-premailer-width' => 'width',\n      '-premailer-height' => 'height'\n    },\n    'td' => {\n      'text-align' => 'align',\n      'background-color' => 'bgcolor',\n      'vertical-align' => 'valign',\n      '-premailer-width' => 'width',\n      '-premailer-height' => 'height'\n    },\n    'img' => {\n      'float' => 'align',\n      '-premailer-width' => 'width',\n      '-premailer-height' => 'height'\n    }\n  }\n\n  # URI of the HTML file used\n  attr_reader   :html_file\n\n  # base URL used to resolve links\n  attr_reader   :base_url\n\n  # base directory used to resolve links for local files\n  # @return [String] base directory\n  attr_reader   :base_dir\n\n  # unmergeable CSS rules to be preserved in the head (CssParser)\n  attr_reader   :unmergable_rules\n\n  # processed HTML document (Hpricot/Nokogiri)\n  attr_reader   :processed_doc\n\n  # source HTML document (Hpricot/Nokogiri)\n  attr_reader   :doc\n\n  # Warning levels\n  module Warnings\n    # No warnings\n    NONE = 0\n    # Safe\n    SAFE = 1\n    # Poor\n    POOR = 2\n    # Risky\n    RISKY = 3\n  end\n  include Warnings\n\n  # Waning level names\n  WARN_LABEL = %w(NONE SAFE POOR RISKY)\n\n  # Create a new Premailer object.\n  #\n  # @param html is the HTML data to process. It can be either an IO object, the URL of a\n  #   remote file, a local path or a raw HTML string.  If passing an HTML string you\n  #   must set the with_html_string option to true.\n  #\n  # @param [Hash] options the options to handle html with.\n  # @option options [Fixnum] :line_length Line length used by to_plain_text. Default is 65.\n  # @option options [Fixnum] :warn_level What level of CSS compatibility warnings to show (see {Premailer::Warnings}).\n  # @option options [String] :link_query_string A string to append to every <tt>a href=\"\"</tt> link. Do not include the initial <tt>?</tt>.\n  # @option options [String] :base_url Used to calculate absolute URLs for local files.\n  # @option options [Array(String)] :css Manually specify CSS stylesheets.\n  # @option options [Boolean] :css_to_attributes Copy related CSS attributes into HTML attributes (e.g. background-color to bgcolor)\n  # @option options [String] :css_string Pass CSS as a string\n  # @option options [Boolean] :remove_ids Remove ID attributes whenever possible and convert IDs used as anchors to hashed to avoid collisions in webmail programs.  Default is false.\n  # @option options [Boolean] :remove_classes Remove class attributes. Default is false.\n  # @option options [Boolean] :remove_comments Remove html comments. Default is false.\n  # @option options [Boolean] :remove_scripts Remove <tt>script</tt> elements. Default is true.\n  # @option options [Boolean] :reset_contenteditable Remove <tt>contenteditable</tt> attributes. Default is true.\n  # @option options [Boolean] :preserve_styles Whether to preserve any <tt>link rel=stylesheet</tt> and <tt>style</tt> elements.  Default is false.\n  # @option options [Boolean] :preserve_reset Whether to preserve styles associated with the MailChimp reset code. Default is true.\n  # @option options [Boolean] :with_html_string Whether the html param should be treated as a raw string. Default is false.\n  # @option options [Boolean] :verbose Whether to print errors and warnings to <tt>$stderr</tt>.  Default is false.\n  # @option options [Boolean] :include_link_tags Whether to include css from <tt>link rel=stylesheet</tt> tags.  Default is true.\n  # @option options [Boolean] :include_style_tags Whether to include css from <tt>style</tt> tags.  Default is true.\n  # @option options [String] :input_encoding Manually specify the source documents encoding. This is a good idea. Default is ASCII-8BIT.\n  # @option options [Boolean] :replace_html_entities Convert HTML entities to actual characters. Default is false.\n  # @option options [Boolean] :escape_url_attributes URL Escapes href, src, and background attributes on elements. Default is true.\n  # @option options [Symbol] :adapter Which HTML parser to use, either <tt>:nokogiri</tt> or <tt>:hpricot</tt>.  Default is <tt>:hpricot</tt>.\n  # @option options [String] :output_encoding Output encoding option for Nokogiri adapter. Should be set to \"US-ASCII\" to output HTML entities instead of Unicode characters.\n  # @option options [Boolean] :create_shorthands Combine several properties into a shorthand one, e.g. font: style weight size. Default is true.\n  def initialize(html, options = {})\n    @options = {:warn_level => Warnings::SAFE,\n                :line_length => 65,\n                :link_query_string => nil,\n                :base_url => nil,\n                :remove_classes => false,\n                :remove_ids => false,\n                :remove_comments => false,\n                :remove_scripts => true,\n                :reset_contenteditable => true,\n                :css => [],\n                :css_to_attributes => true,\n                :with_html_string => false,\n                :css_string => nil,\n                :preserve_styles => false,\n                :preserve_reset => true,\n                :verbose => false,\n                :debug => false,\n                :io_exceptions => false,\n                :include_link_tags => true,\n                :include_style_tags => true,\n                :input_encoding => 'ASCII-8BIT',\n                :output_encoding => nil,\n                :replace_html_entities => false,\n                :escape_url_attributes => true,\n                :unescaped_ampersand => false,\n                :create_shorthands => true,\n                :adapter => Adapter.use,\n                }.merge(options)\n\n    @html_file = html\n    @is_local_file = @options[:with_html_string] || Premailer.local_data?(html)\n\n    @css_files = [@options[:css]].flatten\n\n    @css_warnings = []\n\n    @base_url = nil\n    @base_dir = nil\n    @unmergable_rules = nil\n\n    if @options[:base_url]\n      @base_url = URI.parse(@options.delete(:base_url))\n    elsif not @is_local_file\n      @base_url = URI.parse(@html_file)\n    end\n\n    @css_parser = CssParser::Parser.new({\n      :absolute_paths => true,\n      :import => true,\n      :io_exceptions => @options[:io_exceptions]\n    })\n\n    @adapter_class = Adapter.find @options[:adapter]\n\n    self.class.send(:include, @adapter_class)\n\n    @doc = load_html(@html_file)\n\n    @processed_doc = @doc\n    @processed_doc = convert_inline_links(@processed_doc, @base_url) if @base_url\n    if options[:link_query_string]\n      @processed_doc = append_query_string(@processed_doc, options[:link_query_string])\n    end\n    load_css_from_options!\n    load_css_from_html!\n  end\n\n  # CSS warnings.\n  # @return [Array(Hash)] Array of warnings.\n  def warnings\n    return [] if @options[:warn_level] == Warnings::NONE\n    @css_warnings = check_client_support if @css_warnings.empty?\n    @css_warnings\n  end\n\nprotected\n  def load_css_from_local_file!(path)\n    css_block = ''\n    path.gsub!(/\\Afile:/, '')\n    begin\n      File.open(path, \"r\") do |file|\n        while line = file.gets\n          css_block << line\n        end\n      end\n\n      load_css_from_string(css_block)\n    rescue; end\n  end\n\n  def load_css_from_string(css_string)\n    @css_parser.add_block!(css_string, {:base_uri => @base_url, :base_dir => @base_dir, :only_media_types => [:screen, :handheld]})\n  end\n\n  # @private\n  def load_css_from_options! # :nodoc:\n    load_css_from_string(@options[:css_string]) if @options[:css_string]\n\n    @css_files.each do |css_file|\n      if Premailer.local_data?(css_file)\n        load_css_from_local_file!(css_file)\n      else\n        @css_parser.load_uri!(css_file)\n      end\n    end\n  end\n\n    # Load CSS included in <tt>style</tt> and <tt>link</tt> tags from an HTML document.\n  def load_css_from_html! # :nodoc:\n    tags = @doc.search(\"link[@rel='stylesheet']:not([@data-premailer='ignore']), style:not([@data-premailer='ignore'])\")\n    if tags\n      tags.each do |tag|\n        if tag.to_s.strip =~ /^\\<link/i && tag.attributes['href'] && media_type_ok?(tag.attributes['media']) && @options[:include_link_tags]\n          # A user might want to <link /> to a local css file that is also mirrored on the site\n          # but the local one is different (e.g. newer) than the live file, premailer will now choose the local file\n\n          if tag.attributes['href'].to_s.include? @base_url.to_s and @html_file.kind_of?(String)\n            if @options[:with_html_string]\n              link_uri = tag.attributes['href'].to_s.sub(@base_url.to_s, '')\n            else\n              link_uri = File.join(File.dirname(@html_file), tag.attributes['href'].to_s.sub!(@base_url.to_s, ''))\n              # if the file does not exist locally, try to grab the remote reference\n              unless File.exists?(link_uri)\n                link_uri = Premailer.resolve_link(tag.attributes['href'].to_s, @html_file)\n              end\n            end\n          else\n            link_uri = tag.attributes['href'].to_s\n          end\n\n          if Premailer.local_data?(link_uri)\n            $stderr.puts \"Loading css from local file: \" + link_uri if @options[:verbose]\n            load_css_from_local_file!(link_uri)\n          else\n            $stderr.puts \"Loading css from uri: \" + link_uri if @options[:verbose]\n            @css_parser.load_uri!(link_uri, {:only_media_types => [:screen, :handheld]})\n          end\n\n        elsif tag.to_s.strip =~ /^\\<style/i && @options[:include_style_tags]\n          @css_parser.add_block!(tag.inner_html, :base_uri => @base_url, :base_dir => @base_dir, :only_media_types => [:screen, :handheld])\n        end\n      end\n      tags.remove unless @options[:preserve_styles]\n    end\n  end\n\n\n\n# here be deprecated methods\npublic\n  # @private\n  # @deprecated\n  def local_uri?(uri) # :nodoc:\n    warn \"[DEPRECATION] `local_uri?` is deprecated.  Please use `Premailer.local_data?` instead.\"\n    Premailer.local_data?(uri)\n  end\n\n# here be instance methods\n\n  # @private\n  def media_type_ok?(media_types)\n    media_types = media_types.to_s\n    return true if media_types.nil? or media_types.empty?\n    media_types.split(/[\\s]+|,/).any? { |media_type| media_type.strip =~ /screen|handheld|all/i }\n  end\n\n  def append_query_string(doc, qs)\n    return doc if qs.nil?\n\n    qs.to_s.gsub!(/^[\\?]*/, '').strip!\n    return doc if qs.empty?\n\n    begin\n      current_host = @base_url.host\n    rescue\n      current_host = nil\n    end\n\n    $stderr.puts \"Attempting to append_query_string: #{qs}\" if @options[:verbose]\n\n    doc.search('a').each do|el|\n      href = el.attributes['href'].to_s.strip\n      next if href.nil? or href.empty?\n\n      next if href[0,1] =~ /[\\#\\{\\[\\<\\%]/ # don't bother with anchors or special-looking links\n\n      begin\n        href = URI.parse(href)\n\n        if current_host and href.host != nil and href.host != current_host\n          $stderr.puts \"Skipping append_query_string for: #{href.to_s} because host is no good\" if @options[:verbose]\n          next\n        end\n\n        if href.scheme and href.scheme != 'http' and href.scheme != 'https'\n          puts \"Skipping append_query_string for: #{href.to_s} because scheme is no good\" if @options[:verbose]\n          next\n        end\n\n        if href.query and not href.query.empty?\n          amp = @options[:unescaped_ampersand] ? '&' : '&amp;'\n          href.query = href.query + amp + qs\n        else\n          href.query = qs\n        end\n\n        el['href'] = href.to_s\n      rescue URI::Error => e\n        $stderr.puts \"Skipping append_query_string for: #{href.to_s} (#{e.message})\" if @options[:verbose]\n        next\n      end\n\n    end\n    doc\n  end\n\n  # Check for an XHTML doctype\n  def is_xhtml?\n    intro = @doc.to_html.strip.split(\"\\n\")[0..2].join(' ')\n    is_xhtml = !!(intro =~ /w3c\\/\\/[\\s]*dtd[\\s]+xhtml/i)\n    $stderr.puts \"Is XHTML? #{is_xhtml.inspect}\\nChecked:\\n#{intro}\" if @options[:debug]\n    is_xhtml\n  end\n\n  # Convert relative links to absolute links.\n  #\n  # Processes <tt>href</tt> <tt>src</tt> and <tt>background</tt> attributes\n  # as well as CSS <tt>url()</tt> declarations found in inline <tt>style</tt> attributes.\n  #\n  # <tt>doc</tt> is an Hpricot document and <tt>base_uri</tt> is either a string or a URI.\n  #\n  # Returns an Hpricot document.\n  def convert_inline_links(doc, base_uri) # :nodoc:\n    base_uri = URI.parse(base_uri) unless base_uri.kind_of?(URI)\n\n    append_qs = @options[:link_query_string] || ''\n    escape_attrs = @options[:escape_url_attributes]\n\n    ['href', 'src', 'background'].each do |attribute|\n      tags = doc.search(\"*[@#{attribute}]\")\n\n      next if tags.empty?\n\n      tags.each do |tag|\n        # skip links that look like they have merge tags\n        # and mailto, ftp, etc...\n        if tag.attributes[attribute].to_s =~ /^([\\%\\<\\{\\#\\[]|data:|tel:|file:|sms:|callto:|facetime:|mailto:|ftp:|gopher:|cid:)/i\n          next\n        end\n\n        if tag.attributes[attribute].to_s =~ /^http/i\n          begin\n            merged = URI.parse(tag.attributes[attribute])\n          rescue; next; end\n        else\n          begin\n            merged = Premailer.resolve_link(tag.attributes[attribute].to_s, base_uri)\n          rescue\n            begin\n              next unless escape_attrs\n              merged = Premailer.resolve_link(URI.escape(tag.attributes[attribute].to_s), base_uri)\n            rescue; end\n          end\n        end\n\n        # make sure 'merged' is a URI\n        merged = URI.parse(merged.to_s) unless merged.kind_of?(URI)\n        tag[attribute] = merged.to_s\n      end # end of each tag\n    end # end of each attrs\n\n    doc.search(\"*[@style]\").each do |el|\n      el['style'] = CssParser.convert_uris(el.attributes['style'].to_s, base_uri)\n    end\n    doc\n  end\n\n  # @private\n  def self.is_media_query?(media_types)\n    media_types && media_types.any?{|mt| mt.to_s.count('()') >= 2 }\n  end\n\n  # @private\n  def self.escape_string(str) # :nodoc:\n    str.gsub(/\"/ , \"'\")\n  end\n\n  # @private\n  def self.resolve_link(path, base_path) # :nodoc:\n    path.strip!\n    resolved = nil\n    if path =~ /\\A(?:(https?|ftp|file):)\\/\\//i\n      resolved = path\n      Premailer.canonicalize(resolved)\n    elsif base_path.kind_of?(URI)\n      resolved = base_path.merge(path)\n      Premailer.canonicalize(resolved)\n    elsif base_path.kind_of?(String) and base_path =~ /\\A(?:(?:https?|ftp|file):)\\/\\//i\n      resolved = URI.parse(base_path)\n      resolved = resolved.merge(path)\n      Premailer.canonicalize(resolved)\n    else\n      File.expand_path(path, File.dirname(base_path))\n    end\n  end\n\n  # Test the passed variable to see if we are in local or remote mode.\n  #\n  # IO objects return true, as do strings that look like URLs.\n  def self.local_data?(data)\n    return true   if data.is_a?(IO) || data.is_a?(StringIO)\n    return true   if data =~ /\\Afile:\\/\\//i\n    return false  if data =~ /\\A(?:(https?|ftp):)\\/\\//i\n    true\n  end\n\n  # from http://www.ruby-forum.com/topic/140101\n  def self.canonicalize(uri) # :nodoc:\n    u = uri.kind_of?(URI) ? uri : URI.parse(uri.to_s)\n    u.normalize!\n    newpath = u.path\n    while newpath.gsub!(%r{([^/]+)/\\.\\./?}) { |match|\n        $1 == '..' ? match : ''\n      } do end\n      newpath = newpath.gsub(%r{/\\./}, '/').sub(%r{/\\.\\z}, '/')\n      u.path = newpath\n      u.to_s\n    end\n\n  # Check <tt>CLIENT_SUPPORT_FILE</tt> for any CSS warnings\n  def check_client_support # :nodoc:\n    @client_support ||= YAML::load(File.open(CLIENT_SUPPORT_FILE))\n\n    warnings = []\n    properties = []\n\n    # Get a list off CSS properties\n    @processed_doc.search(\"*[@style]\").each do |el|\n      style_url = el.attributes['style'].to_s.gsub(/([\\w\\-]+)[\\s]*\\:/i) do |s|\n        properties.push($1)\n      end\n    end\n\n    properties.uniq!\n\n    property_support = @client_support['css_properties']\n    properties.each do |prop|\n      if property_support.include?(prop) and\n          property_support[prop].include?('support') and\n          property_support[prop]['support'] >= @options[:warn_level]\n        warnings.push({:message => \"#{prop} CSS property\",\n            :level => WARN_LABEL[property_support[prop]['support']],\n            :clients => property_support[prop]['unsupported_in'].join(', ')})\n      end\n    end\n\n    @client_support['attributes'].each do |attribute, data|\n      next unless data['support'] >= @options[:warn_level]\n      if @doc.search(\"*[@#{attribute}]\").length > 0\n        warnings.push({:message => \"#{attribute} HTML attribute\",\n            :level => WARN_LABEL[data['support']],\n            :clients => data['unsupported_in'].join(', ')})\n      end\n    end\n\n    @client_support['elements'].each do |element, data|\n      next unless data['support'] >= @options[:warn_level]\n      if @doc.search(element).length > 0\n        warnings.push({:message => \"#{element} HTML element\",\n            :level => WARN_LABEL[data['support']],\n            :clients => data['unsupported_in'].join(', ')})\n      end\n    end\n\n    warnings\n  end\nend\n"
  },
  {
    "path": "lib/premailer/version.rb",
    "content": "class Premailer\n  # Premailer version.\n  VERSION = '1.8.6'.freeze\nend\n"
  },
  {
    "path": "lib/premailer.rb",
    "content": "require 'yaml'\nrequire 'open-uri'\nrequire 'digest/md5'\nrequire 'cgi'\nrequire 'css_parser'\n\nrequire 'premailer/adapter'\nrequire 'premailer/html_to_plain_text'\nrequire 'premailer/premailer'\n"
  },
  {
    "path": "misc/client_support.yaml",
    "content": "# Capabilities of e-mail clients\n#\n# Sources\n# * http://campaignmonitor.com/css/\n# * http://www.campaignmonitor.com/blog/archives/2007/04/a_guide_to_css_support_in_emai_2.html\n# * http://www.campaignmonitor.com/blog/archives/2007/11/do_image_maps_work_in_html_ema.html\n# * http://www.campaignmonitor.com/blog/archives/2007/11/how_forms_perform_in_html_emai.html\n# * http://www.xavierfrenette.com/articles/css-support-in-webmail/\n# * http://www.email-standards.org/\n# Updated 2008-08-26\n#\n# Support: 1 = SAFE,  2 = POOR,  3 = RISKY\nelements:\n  map:\n    support: 2\n    unsupported_in: [GMail]\n  area:\n    support: 2\n    unsupported_in: [GMail]\n  form:\n    support: 3\n    unsupported_in: [Mobile Me, Old Yahoo, AOL, Live Mail, Outlook 07, Outlook 03]\n  link:\n    support: 2\n    unsupported_in: [GMail, Hotmail, Old Yahoo]\nattributes:\n  ismap:\n    support: 2\n    unsupported_in: [GMail]\ncss_properties:\n  color:\n    unsupported_in: [Eudora]\n    support_level: 92%\n    support: 1\n  font-size:\n    unsupported_in: [Eudora]\n    support_level: 92%\n    support: 1\n  font-style:\n    unsupported_in: [Eudora]\n    support_level: 92%\n    support: 1\n  font-weight:\n    unsupported_in: [Eudora]\n    support_level: 92%\n    support: 1\n  text-align:\n    unsupported_in: [Eudora]\n    support_level: 92%\n    support: 1\n  text-decoration:\n    unsupported_in: [Eudora]\n    support_level: 92%\n    support: 1\n  background-color:\n    unsupported_in: [Notes 6, Eudora]\n    support_level: 85%\n    support: 2\n  border: &border_shorthand\n    unsupported_in: [Notes 6, Eudora]\n    support_level: 85%\n    support: 2\n  border-bottom: *border_shorthand\n  border-left: *border_shorthand\n  border-right: *border_shorthand\n  border-top: *border_shorthand\n  display:\n    unsupported_in: [Outlook 07, Eudora]\n    support_level: 85%\n    support: 2\n  font-family:\n    unsupported_in: [Eudora, Old GMail, New GMail]\n    support_level: 92%\n    support: 2\n  font-variant:\n    unsupported_in: [Notes 6, Eudora]\n    support_level: 85%\n    support: 2\n  letter-spacing:\n    unsupported_in: [Notes 6, Eudora]\n    support_level: 85%\n    support: 2\n  line-height:\n    unsupported_in: [Notes 6, Eudora]\n    support_level: 85%\n    support: 2\n  padding: &padding_shorthand\n    unsupported_in: [Notes 6, Eudora]\n    support_level: 85%\n    support: 2\n  padding-bottom: *padding_shorthand\n  padding-left: *padding_shorthand\n  padding-right: *padding_shorthand\n  padding-top: *padding_shorthand\n  table-layout:\n    unsupported_in: [Notes 6, Eudora]\n    support_level: 85%\n    support: 2\n  text-indent:\n    unsupported_in: [Notes 6, Eudora]\n    support_level: 85%\n    support: 2\n  text-transform:\n    unsupported_in: [Notes 6, Eudora]\n    support_level: 85%\n    support: 2\n  border-collapse:\n    unsupported_in: [Entourage 2004, Notes 6, Eudora]\n    support_level: 77%\n    support: 3\n  clear:\n    unsupported_in: [Outlook 07, Notes 6, Eudora]\n    support_level: 77%\n    support: 3\n  direction:\n    unsupported_in: [Outlook 07, Entourage 2004, Eudora, New GMail]\n    support_level: 77%\n    support: 3\n  float:\n    unsupported_in: [Outlook 07, Eudora, Old GMail]\n    support_level: 85%\n    support: 3\n  vertical-align:\n    unsupported_in: [Outlook 07, Notes 6, Eudora]\n    support_level: 77%\n    support: 3\n  width:\n    unsupported_in: [Outlook 07, Notes 6, Eudora]\n    support_level: 77%\n    support: 3\n  word-spacing:\n    unsupported_in: [Outlook 07, Notes 6, Eudora]\n    support_level: 77%\n    support: 3\n  height:\n    unsupported_in: [Outlook 07, Notes 6, Eudora, Old GMail]\n    support_level: 77%\n    support: 3\n  list-style-type:\n    unsupported_in: [Outlook 07, Eudora, Hotmail]\n    support_level: 85%\n    support: 3\n  overflow:\n    unsupported_in: [Outlook 07, Entourage 2004, Notes 6, Eudora]\n    support_level: 69%\n    support: 3\n  visibility:\n    unsupported_in: [Outlook 07, Notes 6, Eudora, Old GMail, New GMail, aolWeb]\n    support_level: 77%\n    support: 3\n  white-space:\n    unsupported_in: [Outlook 03, Windows Mail, AOL 9, AOL 10, Notes 6, Eudora, Mobile Me]\n    support_level: 54%\n    support: 3\n  background-image:\n    unsupported_in: [Outlook 07, Notes 6, Eudora, Old GMail, New GMail, Live Mail]\n    support_level: 77%\n    support: 3\n  background-repeat:\n    unsupported_in: [Outlook 07, Notes 6, Eudora, Old GMail, New GMail, Live Mail]\n    support_level: 77%\n    support: 3\n  clip:\n    unsupported_in: [Outlook 07, Notes 6, Eudora, New Yahoo, New GMail, Live Mail, Mobile Me]\n    support_level: 77%\n    support: 3\n  cursor:\n    unsupported_in: [Outlook 07, Entourage 2004, Notes 6, Eudora, Old GMail, New GMail]\n    support_level: 69%\n    support: 3\n  list-style-image:\n    unsupported_in: [Outlook 07, Notes 6, Eudora, Old GMail, New GMail, Live Mail]\n    support_level: 77%\n    support: 3\n  list-style-position:\n    unsupported_in: [Outlook 07, Notes 6, Eudora, Old Yahoo, Hotmail]\n    support_level: 77%\n    support: 3\n  margin: &margin_shorthand\n    unsupported_in: [AOL 9, Notes 6, Eudora, Live Mail, Hotmail]\n    support_level: 77%\n    support: 3\n  margin-bottom: *margin_shorthand\n  margin-left: *margin_shorthand\n  margin-right: *margin_shorthand\n  margin-top: *margin_shorthand\n  z-index:\n    unsupported_in: [Notes 6, Eudora, New Yahoo, Old GMail, New GMail, Live Mail]\n    support_level: 85%\n    support: 3\n  left:\n    unsupported_in: [Outlook 07, Notes 6, Eudora, New Yahoo, Old GMail, New GMail, Live Mail]\n    support_level: 77%\n    support: 3\n  right:\n    unsupported_in: [Outlook 07, Notes 6, Eudora, New Yahoo, Old GMail, New GMail, Live Mail]\n    support_level: 77%\n    support: 3\n  top:\n    unsupported_in: [Outlook 07, Notes 6, Eudora, New Yahoo, Old GMail, New GMail, Live Mail]\n    support_level: 77%\n    support: 3\n  background-position:\n    unsupported_in: [Outlook 07, Notes 6, Eudora, Old Yahoo, Old GMail, New GMail, Live Mail, Hotmail]\n    support_level: 77%\n    support: 3\n  border-spacing:\n    unsupported_in: [Outlook 03, Outlook 07, Windows Mail, Entourage 2004, AOL 10, Notes 6, Eudora, Live Mail, Hotmail]\n    support_level: 46%\n    support: 3\n  bottom:\n    unsupported_in: [Outlook 07, AOL 9, Notes 6, Eudora, New Yahoo, Old GMail, New GMail, Live Mail]\n    support_level: 69%\n    support: 3\n  empty-cells:\n    unsupported_in: [Outlook 03, Outlook 07, Windows Mail, Entourage 2004, AOL 9, AOL 10, Notes 6, Eudora, Hotmail]\n    support_level: 38%\n    support: 3\n  position:\n    unsupported_in: [Outlook 07, Notes 6, Eudora, Old Yahoo, New Yahoo, Old GMail, New GMail, Live Mail, Hotmail, Mobile Me]\n    support_level: 77%\n    support: 3\n  caption-side:\n    unsupported_in: [Outlook 03, Outlook 07, Windows Mail, Mac Mail, Entourage 2004, Entourage 2008, AOL 9, AOL 10, AOL Desktop for Mac, Notes 6, Eudora, New Yahoo, Hotmail]\n    support_level: 15%\n    support: 3\n  opacity:\n    unsupported_in: [Outlook 03, Outlook 07, Windows Mail, Entourage 2004, Notes 6, Eudora, New Yahoo, Old GMail, New GMail, Live Mail, Hotmail]\n    support_level: 54%\n    support: 3\n"
  },
  {
    "path": "premailer.gemspec",
    "content": "require './lib/premailer/version'\n\nGem::Specification.new \"premailer\", Premailer::VERSION do |s|\n  s.summary  = \"Preflight for HTML e-mail.\"\n  s.email    = \"code@dunae.ca\"\n  s.homepage = \"http://premailer.dialect.ca/\"\n  s.description = \"Improve the rendering of HTML emails by making CSS inline, converting links and warning about unsupported code.\"\n  s.has_rdoc = true\n  s.author  = \"Alex Dunae\"\n  s.files            = `git ls-files lib misc LICENSE.md README.md`.split(\"\\n\")\n  s.executables      = ['premailer']\n  s.required_ruby_version = '>= 2.0.0'\n\n  s.add_dependency('css_parser', '>= 1.3.7')\n  s.add_dependency('htmlentities', ['>= 4.0.0'])\n  s.add_development_dependency \"bundler\", \"~> 1.3\"\n  s.add_development_dependency('rake', ['~> 0.8',  '!= 0.9.0'])\n  s.add_development_dependency('hpricot', '>= 0.8.3')\n  s.add_development_dependency('nokogiri', '>= 1.4.4')\n  s.add_development_dependency('yard', '~> 0.8.7.6')\n  s.add_development_dependency('redcarpet', '~> 3.0')\n  s.add_development_dependency('maxitest')\n  s.add_development_dependency('coveralls')\n  s.add_development_dependency('webmock')\n  s.add_development_dependency('nokogumbo')\nend\n\n"
  },
  {
    "path": "test/files/base.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<!--\n\n  You can read this newsletter online at\n  [webversion]\n\n-->\n<html>\n<head>\n\t<meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\">\n\t<title>Premailer Test</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\">\n\t<style type=\"text/css\">\n\t\t@import \"import.css\" screen, handheld;\n\t</style>\n\t<style type=\"text/css\">\n\t\t@import \"noimport.css\" print;\n\t</style>\n\t<style type=\"text/css\">\n\t\t#iphone { display: block; }\n\t</style>\n</head>\n<body>\n<div id=\"wrapper\">\n<p class=\"hide\" id=\"hide01\">This line should be hidden.</p>\n<p class=\"hide\" id=\"iphone\">This is an iPhone style.</p>\n<table width=\"646\" class=\"container\" cellspacing=\"0\" cellpadding=\"0\">\n<tr><td id=\"webversion\" colspan=\"6\">Having trouble reading this newsletter? <webversion>Click here to see it in your browser</webversion></td></tr>\n\n<tr><td height=\"13\" colspan=\"6\" class=\"frame\">&#x00a0;</td></tr>\n\n\n\n<table width=\"646\" class=\"container\" cellspacing=\"0\" cellpadding=\"0\">\n\n<tr>\n\t<td class=\"frame\" width=\"13\">&#x00a0;</td>\n\t<td class=\"gutter\" width=\"60\">&#x00a0;</td>\n\n\t<td class=\"content\" colspan=\"2\" width=\"500\">\n\n<h1><span>Premailer Test</span></h1>\n\n<table width=\"500\" cellpadding=\"0\" cellspacing=\"0\">\n<tr>\n\t<td width=\"20\">&#x00a0;</td>\n\t<td colspan=\"2\" width=\"460\">\n\t\t<h2>Lorem ipsum dolor</h2>\n\t\t<h3>Suspendisse id velit vitae ligula volutpat condimentum</h3>\n\t\t<p class=\"dt\">Morbi commodo, ipsum sed</p>\n\n\n\t\t<p class=\"unaligned\"><img src=\"2009-placeholder.png\" alt=\"Image\" align=\"right\" class=\"right\">Lorem&nbsp;ipsum&#x00a0;dolor sit amet, consectetuer adipiscing elit. Morbi commodo, ipsum sed pharetra gravida, orci magna rhoncus neque, id pulvinar odio lorem non turpis. Nullam sit amet enim. Suspendisse id velit vitae ligula volutpat condimentum. Aliquam erat volutpat. Sed quis velit. <a href=\"http://premailer.dialect.ca/\">Nulla facilisi</a>. Nulla libero.</p>\n\n\t\t<p attr=\"another quote\">Here&rsquo;s a quote.  Here’s a quote.  &#x201c;Here’s a quote in quotes”.</p>\n\n\t\t<p>Nullam sit amet enim. Suspendisse id velit vitae ligula volutpat condimentum. Aliquam erat volutpat. Sed quis velit. Nulla facilisi.</p>\n\t\t<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi commodo, ipsum sed pharetra gravida, orci magna rhoncus neque, id pulvinar odio lorem non turpis. Nullam sit amet enim. Suspendisse id velit vitae ligula volutpat condimentum. Aliquam erat volutpat. Sed quis velit. Nulla facilisi. Nulla libero.</p>\n\t\t<blockquote><p>“Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi commodo, ipsum sed pharetra gravida, orci magna rhoncus neque, id pulvinar odio lorem non turpis.”</p></blockquote>\n\t\t<p>Aliquam erat volutpat. Sed quis velit. Nulla facilisi. Nulla libero.</p>\n\n\t\t<h3>Link tests</h3>\n\t\t<ul>\n\t\t\t<li><a id=\"l01\" href=\"/\">Relative path to root</a></li>\n\t\t\t<li><a id=\"l02\" href=\"http://premailer.dialect.ca/\">Absolute path to root</a></li>\n\t\t\t<li><a id=\"l03\" href=\"http://example.com/\">Different domain</a></li>\n\t\t\t<li><a id=\"l04\" href=\"images/\">Relative path to sub-directory</a></li>\n\t\t\t<li><a id=\"l05\" href=\"#relative\">Link is not converted</a></li>\n\t\t\t<li><a id=\"l06\" href=\"http://example.com/test.html?cn=tf&amp;c=20&amp;ord=%%RANDOM%%\">Funky ASP URL</a></li>\n\t\t\t<li><a id=\"l07\" href=\"?query=string\">Appends tracking query string</a></li>\n\t\t\t<li><a id=\"l08\" href=\"{DONOTCONVERT}\">Link is not converted</a></li>\n\t\t\t<li><a id=\"l09\" href=\"[DONOTCONVERT]\">Link is not converted</a></li>\n\t\t\t<li><a id=\"l10\" href=\"<DONOTCONVERT>\">Link is not converted</a></li>\n\t\t\t<li><a id=\"l11\" href=\"mailto:premailer@example.com\">mailto link</a></li>\n\t\t\t<li><a id=\"l12\" href=\"ftp://example.com\">FTP link</a></li>\n\t\t\t<li><a id=\"l13\" href=\"gopher://gopher.floodgap.com/1/fun/twitpher\">Gopher link</a></li>\n\t\t</ul>\n\n\n\n\t\t<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi commodo, ipsum sed pharetra gravida, orci magna rhoncus neque, id pulvinar odio lorem non turpis.</p>\n\n\t\t<p>&nbsp;</p>\n\n\n\t</td>\n\t<td width=\"20\">&#x00a0;</td>\n</tr>\n</table>\n\n<p class=\"section\"><img src=\"dots_end.png\" alt=\"---\" width=\"499\" height=\"75\"></p>\n\t</td><!-- /#content -->\n\t<td class=\"gutter\" width=\"60\">&#x00a0;</td>\n\n\t<td class=\"frame\" width=\"13\">&#x00a0;</td>\n</tr>\n\n<tr>\n\t<td class=\"frame\" width=\"13\">&#x00a0;</td>\n\t<td colspan=\"4\" width=\"620\">\n\t\t<table summary=\"Contact information\" cellspacing=\"0\" cellpadding=\"0\" width=\"620\">\n\t\t\t<tr><td height=\"4\" class=\"hairline\">&#x00a0;</td></tr>\n\t\t\t<tr><td height=\"34\"class=\"contact\">&#x00a0;</td></tr>\n\t\t\t<tr>\n\n\t\t\t\t<td align=\"center\" class=\"contact\" id=\"contact_info\">\n\t\t\t\t\t<p id=\"address\">Premailer Test<br>\n\t\t\t\t\t<a href=\"http://dialect.ca/?utm_source=Premailer&utm_medium=Test+Suite&utm_campaign=Premailer\">by Dialect</a><br>\n\t\t\t\t\tVancouver Island, British Columbia<br>\n\t\t\t\t\t250 555.2222</p>\n\t\t\t\t</td>\n\n\t\t\t</tr>\n\t\t\t<tr><td height=\"34\"class=\"contact\">&#x00a0;</td></tr>\n\t\t\t<tr><td height=\"4\" class=\"hairline\">&#x00a0;</td></tr>\n\t\t</table>\n\t</td>\n\t<td class=\"frame\" width=\"13\">&#x00a0;</td>\n</tr>\n\n<tr>\n\t<td class=\"frame\" width=\"13\">&#x00a0;</td>\n\n\t<td colspan=\"4\" class=\"content\" height=\"60\">&#x00a0;</td>\n\t<td class=\"frame\" width=\"13\">&#x00a0;</td>\n</tr>\n\n\n\n\n\n<tr><td height=\"13\" colspan=\"6\" class=\"frame\">&#x00a0;</td></tr>\n<tr><td height=\"22\" colspan=\"6\" >&#x00a0;</td></tr>\n<tr><td id=\"credit\" colspan=\"6\">Newsletter communications by<br><a href=\"http://dialect.ca/dialogue/?utm_source=Dialogue&utm_medium=Credit&utm_campaign=South+Hollow\"><img src=\"inc/dialect.png\" alt=\"Dialect\" width=\"60\" height=\"35\" border=\"0\"></a><br><unsubscribe>Click here to unsubscribe</unsubscribe></td></tr>\n\n</table>\n\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "test/files/chars.html",
    "content": "<!DOCTYPE html>\n<html>\n<body>\n<p>cédille c&eacute; & garçon gar&#231;on à &agrave; &nbsp; &amp; &copy;</p>\n</body>\n</html>"
  },
  {
    "path": "test/files/html4.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n   \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n  <title>Title</title>\n</head>\n<body>\n<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n  <br>\n  Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>\n</body>\n</html>\n"
  },
  {
    "path": "test/files/html_with_uri.html",
    "content": "<html>\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\">\n  <body>\n    <p>\n      The following line should not make this html be identified as a URI\nhttp://foobar.com\n    </p>\n  </body>\n</html>"
  },
  {
    "path": "test/files/ignore.css",
    "content": "body {\n  color: orange;\n}\n"
  },
  {
    "path": "test/files/ignore.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Should ignore link and style elements with data attribute</title>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"ignore.css\" data-premailer=\"ignore\" />\n    <style type=\"text/css\" data-premailer=\"ignore\">\n      h1 {\n        color: red;\n      }\n    </style>\n  </head>\n  <body>\n    <h1>Content</h1>\n  </body>\n</html>\n"
  },
  {
    "path": "test/files/import.css",
    "content": "/*\n * Premailer styles - should import\n *\n * $Package: Premailer $\n * $Date: 2009-02-09 17:15:56 -0800 (Mon, 09 Feb 2009) $WCDATE$ $\n * $Rev: 95 $WCREV$ $\n */\n\n.hide {\n\tdisplay: none;\n}\n"
  },
  {
    "path": "test/files/iso-8859-2.html",
    "content": "<body>In Hungary we use some special accented characters:    .</body>\n"
  },
  {
    "path": "test/files/iso-8859-5.html",
    "content": "<html>\n<head>\n<meta http-equiv=\"Content-type\" content=\"text/html; charset=iso-8859-5\">\n</head>\n<body>\n<p> </p>\n</body>\n</html>"
  },
  {
    "path": "test/files/no_css.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n\t<meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\">\n\t<title>Premailer No CSS Test</title>\n\t</style>\n</head>\n<body>\n<p class=\"hide\" id=\"hide01\">This line should be hidden.</p>\n</body>\n</html>\n"
  },
  {
    "path": "test/files/noimport.css",
    "content": "/*\n * Premailer styles - should not import\n *\n * $Package: Premailer $\n * $Date: 2009-02-09 17:15:56 -0800 (Mon, 09 Feb 2009) $WCDATE$ $\n * $Rev: 95 $WCREV$ $\n */\n\nbody {\n\tbackground: none red !important;\n}\n"
  },
  {
    "path": "test/files/styles.css",
    "content": "/*\n * Premailer styles\n *\n * $Package: Premailer $\n * $Date: 2009-02-09 17:15:56 -0800 (Mon, 09 Feb 2009) $WCDATE$ $\n * $Rev: 95 $WCREV$ $\n */\n\n@import \"noimport.css\" print;\n\n\n/*** structure and table cells ***/\nbody{font:13px/1.231 \"Arial\",sans-serif;color: #fff;background-color: #9EBF00;}\n\n #wrapper { width: 100%; margin: 2em 0; background-color: #9EBF00; color: #fff; }\n\n.container { margin: 0 auto; line-height: 130%; color: #4d4d4d; text-align: left; }\n\n.frame { background-color: #b6d93f; font-size: 1px; line-height: 1px;  }\n\n.hairline { background-color: #9ebf00; font-size: 1px; line-height: 1px; }\n\n.masthead, .gutter { color: #999; background-color: #fff; }\n\n.content { line-height: 158%; color: #999; background-color: #fff; }\n\n#webversion, #footer { margin: 0 auto; text-align: center; font-size: 85%; }\n\n\n/*** general styles ***/\nh1, h1 a, h2, h2 a {  color: #9ebf00; }\n\nh1 span { padding: 0 .5em; background: #fff; }\n\nh1 {\n\tmargin: 32px 0 19px;\n\tfont: bold 85% \"Verdana\", sans-serif;\n\ttext-transform: uppercase;\n\ttext-align: center;\n\tletter-spacing: .1em;\n\tbackground: transparent url(\"dots_h.gif\") repeat-x 0 55%;\n}\n\nh2 { margin: 0 0 .4em; font: normal 205%/109% \"Arial\", sans-serif; }\n\nh3, h3 a { color: #808080; }\n\nh3 { margin: 0 0 .1em; font: normal 165%/109% \"Arial\", sans-serif; }\n\ntable{ border-collapse:collapse;border-spacing:0; border: 0; }\n\ncaption,th,td {text-align:left;font-weight:normal; margin: 0; padding: 0; }\n\npre,code,kbd,samp,tt{font-family:monospace;line-height:100%;}\n\nth, td { vertical-align: top; }\n\na { text-decoration: none; }\n\nblockquote { margin: 0; padding: 5px 30px; text-align: center; }\n\nblockquote, blockquote p { font: italic 16px/145% \"Georgia\", serif; }\n\np, blockquote { color: #999; }\n\np { margin: 0 0 1em; font: normal 100%/158% \"Arial\", sans-serif; vertical-align: top; }\n\n\n.hide { text-align: center; color: red; font-size: 150%; }\n\n/*** specific elements ***/\nh2 + h3 { font-style: italic;}\np[attr~=quote]  { font-style: italic;}\nul li:first-of-type { font-style: italic;}\n\n.content p a, .content li a { color: #8AAD09; text-decoration: underline; }\n\n.content p.dt { margin-bottom: .8em; font: italic 95%/135% \"Arial\", sans-serif; }\n\nimg.right { float: right; margin: 0 0 30px 20px; }\n\n.contact { text-align: center; background: #9EC03B url(\"contact_bg.png\") repeat 0 0; }\n\n.contact, .contact p, .contact a { color: #fff; text-decoration: none; }\n\n.contact p { margin-bottom: 0; line-height: 140%; }\n\n.contact a:hover { text-decoration: underline; }\n\n#webversion, #webversion a {\n\tfont: bold 12px/28px \"Trebuchet\", \"Trebuchet MS\", serif;\n\tcolor: #fff;\n\tbackground: #9ebf00;\n}\n\n#credit { padding-bottom: 20px; }\n\n#credit, #credit a {\n\tcolor: #fff;\n\tfont: normal 10px/13px \"Verdana\", sans-serif;\n\ttext-align: center;\n}\n\n#contact_info { padding: 5px; }\n"
  },
  {
    "path": "test/files/xhtml.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html>\n<head>\n  <title>Title</title>\n</head>\n<body>\n<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n  <br/>\n  Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>\n</body>\n</html>\n"
  },
  {
    "path": "test/future_tests.rb",
    "content": "# encoding: UTF-8\nrequire File.expand_path(File.dirname(__FILE__)) + '/helper'\n\nclass TestPremailer < Premailer::TestCase\n  def test_related_attributes\n    flunk 'Not implemented'\n    local_setup\n\n    # h1 { text-align: center; }\n    assert_equal 'center', @doc.at('h1')['align']\n\n    # td { vertical-align: top; }\n    assert_equal 'top', @doc.at('td')['valign']\n\n    # p { vertical-align: top; } -- not allowed\n    assert_nil @doc.at('p')['valign']\n\n    # no align attr is specified for <p> elements, so it should not appear\n    assert_nil @doc.at('p.unaligned')['align']\n\n    # .contact { background: #9EC03B url(\"contact_bg.png\") repeat 0 0; }\n    assert_equal '#9EC03B', @doc.at('td.contact')['bgcolor']\n\n    # body { background-color: #9EBF00; }\n    assert_equal '#9EBF00', @doc.at('body')['bgcolor']\n  end\n\n  def test_merging_cellpadding\n    flunk 'Not implemented'\n    local_setup('cellpadding.html', {:prefer_cellpadding => true})\n    assert_equal '0', @doc.at('#t1')['cellpadding']\n    assert_match /padding\\:/i, @doc.at('#t1 td')['style']\n\n    assert_equal '5', @doc.at('#t2')['cellpadding']\n    refute_match /padding\\:/i, @doc.at('#t2 td')['style']\n\n    assert_nil @doc.at('#t3')['cellpadding']\n    assert_match /padding\\:/i, @doc.at('#t3 td')['style']\n\n    assert_nil @doc.at('#t4')['cellpadding']\n    assert_match /padding\\:/i, @doc.at('#t4a')['style']\n    assert_match /padding\\:/i, @doc.at('#t4b')['style']\n  end\n\n  def test_preserving_media_queries\n    flunk 'Not implemented'\n    local_setup\n    assert_match /display\\: none/i, @doc.at('#iphone')['style']\n  end\nend\n"
  },
  {
    "path": "test/helper.rb",
    "content": "require 'bundler/setup'\nrequire 'maxitest/autorun'\nrequire 'webmock/minitest'\nrequire 'premailer'\n\nclass Premailer::TestCase < Minitest::Test\n  BASE_URI  = 'http://premailer.dev/'\n  BASE_PATH =  File.expand_path(File.dirname(__FILE__)) + '/files'\n\n  def setup\n    stub_request(:any, /premailer\\.dev\\/*/).to_return do |request|\n      file_path = BASE_PATH + URI.parse(request.uri).path\n      if File.exists?(file_path)\n        { :status => 200, :body => File.open(file_path) }\n      else\n        { :status => 404, :body => \"#{file_path} not found\" }\n      end\n    end\n\n    stub_request(:get, /my\\.example\\.com\\:8080\\/*/).to_return(:status => 200, :body => \"\", :headers => {})\n  end\n\n  def default_test; end\n\n  protected\n  def local_setup(f = 'base.html', opts = {})\n    base_file = BASE_PATH + '/' + f\n    premailer = Premailer.new(base_file, opts)\n    premailer.to_inline_css\n    @doc = premailer.processed_doc\n  end\n\n  def remote_setup(f = 'base.html', opts = {})\n    @premailer = Premailer.new(BASE_URI + \"#{f}\", opts)\n    @premailer.to_inline_css\n    @doc = @premailer.processed_doc\n  end\n\nend\n"
  },
  {
    "path": "test/test_adapter.rb",
    "content": "require File.expand_path(File.dirname(__FILE__)) + '/helper'\n\nclass TestAdapter < Premailer::TestCase\n\n  def test_default_to_best_available\n    require 'hpricot'\n    assert_equal 'Premailer::Adapter::Hpricot', Premailer::Adapter.use.name\n  end\n\n  def test_settable_via_symbol\n    Premailer::Adapter.use = :hpricot\n    assert_equal 'Premailer::Adapter::Hpricot', Premailer::Adapter.use.name\n  end\n\n  def test_adapters_are_findable_by_symbol\n    assert_equal 'Premailer::Adapter::Hpricot', Premailer::Adapter.find(:hpricot).name\n  end\n\n  def test_adapters_are_findable_by_class\n    assert_equal 'Premailer::Adapter::Hpricot', Premailer::Adapter.find(Premailer::Adapter::Hpricot).name\n  end\n\n  def test_raises_argument_error\n    assert_raises(ArgumentError, \"Invalid adapter: unknown\") {\n      Premailer::Adapter.find(:unknown)\n    }\n  end\n\nend\n"
  },
  {
    "path": "test/test_html_to_plain_text.rb",
    "content": "# encoding: utf-8\nrequire File.expand_path(File.dirname(__FILE__)) + '/helper'\n\nclass TestHtmlToPlainText < Premailer::TestCase\n  include HtmlToPlainText\n\n  def test_to_plain_text_with_fragment\n    premailer = Premailer.new('<p>Test</p>', :with_html_string => true)\n    assert_match /Test/, premailer.to_plain_text\n  end\n\n  def test_to_plain_text_with_body\n    html = <<END_HTML\n    <html>\n    <title>Ignore me</title>\n    <body>\n\t\t<p>Test</p>\n\t\t</body>\n\t\t</html>\nEND_HTML\n\n    premailer = Premailer.new(html, :with_html_string => true)\n    assert_match /Test/, premailer.to_plain_text\n  end\n\n  def test_to_plain_text_with_malformed_body\n    html = <<END_HTML\n    <html>\n    <title>Ignore me</title>\n    <body>\n\t\t<p>Test\nEND_HTML\n\n    premailer = Premailer.new(html, :with_html_string => true)\n    assert_match /Test/, premailer.to_plain_text\n  end\n\n  def test_specialchars\n    assert_plaintext 'cédille garçon & à ñ', 'c&eacute;dille gar&#231;on &amp; &agrave; &ntilde;'\n  end\n\n  def test_stripping_whitespace\n    assert_plaintext \"text\\ntext\", \"  \\ttext\\ntext\\n\"\n    assert_plaintext \"a\\na\", \"  \\na \\n a \\t\"\n    assert_plaintext \"a\\n\\na\", \"  \\na \\n\\t \\n \\n a \\t\"\n    assert_plaintext \"test text\", \"test text&nbsp;\"\n    assert_plaintext \"test text\", \"test        text\"\n  end\n\n  def test_wrapping_spans\n    html = <<END_HTML\n    <html>\n    <body>\n\t\t<p><span>Test</span>\n\t\t<span>line 2</span>\n\t\t</p>\nEND_HTML\n\n    premailer = Premailer.new(html, :with_html_string => true)\n    assert_match /Test line 2/, premailer.to_plain_text\n  end\n\n  def test_line_breaks\n    assert_plaintext \"Test text\\nTest text\", \"Test text\\r\\nTest text\"\n    assert_plaintext \"Test text\\nTest text\", \"Test text\\rTest text\"\n  end\n\n  def test_lists\n    assert_plaintext \"* item 1\\n* item 2\", \"<li class='123'>item 1</li> <li>item 2</li>\\n\"\n    assert_plaintext \"* item 1\\n* item 2\\n* item 3\", \"<li>item 1</li> \\t\\n <li>item 2</li> <li> item 3</li>\\n\"\n  end\n\n  def test_stripping_html\n    assert_plaintext 'test text', \"<p class=\\\"123'45 , att\\\" att=tester>test <span class='te\\\"st'>text</span>\\n\"\n  end\n\n  def test_stripping_ignored_blocks\n    html = <<END_HTML\n    <p>test</p>\n    <!-- start text/html -->\n      <img src=\"logo.png\" alt=\"logo\">\n    <!-- end text/html -->\n    <p>text</p>\nEND_HTML\n    premailer = Premailer.new(html, :with_html_string => true)\n    assert_match /test\\n\\ntext/, premailer.to_plain_text\n  end\n\n  def test_paragraphs_and_breaks\n    assert_plaintext \"Test text\\n\\nTest text\", \"<p>Test text</p><p>Test text</p>\"\n    assert_plaintext \"Test text\\n\\nTest text\", \"\\n<p>Test text</p>\\n\\n\\n\\t<p>Test text</p>\\n\"\n    assert_plaintext \"Test text\\nTest text\", \"\\n<p>Test text<br/>Test text</p>\\n\"\n    assert_plaintext \"Test text\\nTest text\", \"\\n<p>Test text<br> \\tTest text<br></p>\\n\"\n    assert_plaintext \"Test text\\n\\nTest text\", \"Test text<br><BR />Test text\"\n  end\n\n  def test_headings\n    assert_plaintext \"****\\nTest\\n****\", \"<h1>Test</h1>\"\n    assert_plaintext \"****\\nTest\\n****\", \"\\t<h1>\\nTest</h1> \"\n    assert_plaintext \"***********\\nTest line 1\\nTest 2\\n***********\", \"\\t<h1>\\nTest line 1<br>Test 2</h1> \"\n    assert_plaintext \"****\\nTest\\n****\\n\\n****\\nTest\\n****\", \"<h1>Test</h1> <h1>Test</h1>\"\n    assert_plaintext \"----\\nTest\\n----\", \"<h2>Test</h2>\"\n    assert_plaintext \"Test\\n----\", \"<h3> <span class='a'>Test </span></h3>\"\n  end\n\n  def test_wrapping_lines\n    raw = ''\n    100.times { raw += 'test ' }\n\n    txt = convert_to_text(raw, 20)\n\n    lens = []\n    txt.each_line { |l| lens << l.length }\n    assert lens.max <= 20\n  end\n\n  def test_wrapping_lines_with_spaces\n    assert_plaintext \"Long line\\nnew line\", 'Long     line new line', nil ,10\n  end\n\n  def test_img_alt_tags\n    # ensure html imag tags that aren't self-closed are parsed,\n    # along with accepting both '' and \"\" as attribute quotes\n\n    # <img alt=\"\" />\n    assert_plaintext 'Example ( http://example.com/ )', '<a href=\"http://example.com/\"><img src=\"http://example.ru/hello.jpg\" alt=\"Example\"/></a>'\n    # <img alt=\"\">\n    assert_plaintext 'Example ( http://example.com/ )', '<a href=\"http://example.com/\"><img src=\"http://example.ru/hello.jpg\" alt=\"Example\"></a>'\n    # <img alt='' />\n    assert_plaintext 'Example ( http://example.com/ )', \"<a href='http://example.com/'><img src='http://example.ru/hello.jpg' alt='Example'/></a>\"\n    # <img alt=''>\n    assert_plaintext 'Example ( http://example.com/ )', \"<a href='http://example.com/'><img src='http://example.ru/hello.jpg' alt='Example'></a>\"\n  end\n\n  def test_links\n    # basic\n    assert_plaintext 'Link ( http://example.com/ )', '<a href=\"http://example.com/\">Link</a>'\n\n    # nested html\n    assert_plaintext 'Link ( http://example.com/ )', '<a href=\"http://example.com/\"><span class=\"a\">Link</span></a>'\n\n    # nested html with new line\n    assert_plaintext 'Link ( http://example.com/ )', \"<a href='http://example.com/'>\\n\\t<span class='a'>Link</span>\\n\\t</a>\"\n\n    # mailto\n    assert_plaintext 'Contact Us ( contact@example.org )', \"<a href='mailto:contact@example.org'>Contact Us</a>\"\n\n    # complex link\n    assert_plaintext 'Link ( http://example.com:80/~user?aaa=bb&c=d,e,f#foo )', '<a href=\"http://example.com:80/~user?aaa=bb&amp;c=d,e,f#foo\">Link</a>'\n\n    # attributes\n    assert_plaintext 'Link ( http://example.com/ )', '<a title=\\'title\\' href=\"http://example.com/\">Link</a>'\n\n    # spacing\n    assert_plaintext 'Link ( http://example.com/ )', '<a href=\"   http://example.com/ \"> Link </a>'\n\n    # multiple\n    assert_plaintext 'Link A ( http://example.com/a/ ) Link B ( http://example.com/b/ )', '<a href=\"http://example.com/a/\">Link A</a> <a href=\"http://example.com/b/\">Link B</a>'\n\n    # merge links\n    assert_plaintext 'Link ( %%LINK%% )', '<a href=\"%%LINK%%\">Link</a>'\n    assert_plaintext 'Link ( [LINK] )', '<a href=\"[LINK]\">Link</a>'\n    assert_plaintext 'Link ( {LINK} )', '<a href=\"{LINK}\">Link</a>'\n\n    # unsubscribe\n    assert_plaintext 'Link ( [[!unsubscribe]] )', '<a href=\"[[!unsubscribe]]\">Link</a>'\n\n    # empty link gets dropped, and shouldn't run forever\n    assert_plaintext((\"This is some more text\\n\\n\" * 14 + \"This is some more text\"), \"<a href=\\\"test\\\"></a>#{\"\\n<p>This is some more text</p>\" * 15}\")\n\n    # links that go outside of line should wrap nicely\n    assert_plaintext \"Long text before the actual link and then LINK TEXT \\n( http://www.long.link ) and then more text that does not wrap\", 'Long text before the actual link and then <a href=\"http://www.long.link\"/>LINK TEXT</a> and then more text that does not wrap'\n  end\n\n  # see https://github.com/alexdunae/premailer/issues/72\n  def test_multiple_links_per_line\n    assert_plaintext 'This is link1 ( http://www.google.com ) and link2 ( http://www.google.com ) is next.',\n                     '<p>This is <a href=\"http://www.google.com\" >link1</a> and <a href=\"http://www.google.com\" >link2 </a> is next.</p>',\n                     nil, 10000\n  end\n\n  # see https://github.com/alexdunae/premailer/issues/72\n  def test_links_within_headings\n    assert_plaintext \"****************************\\nTest ( http://example.com/ )\\n****************************\",\n                     \"<h1><a href='http://example.com/'>Test</a></h1>\"\n  end\n\n  def assert_plaintext(out, raw, msg = nil, line_length = 65)\n    assert_equal out, convert_to_text(raw, line_length), msg\n  end\nend\n"
  },
  {
    "path": "test/test_links.rb",
    "content": "# encoding: UTF-8\nrequire File.expand_path(File.dirname(__FILE__)) + '/helper'\n\nclass TestLinks < Premailer::TestCase\n  def test_empty_query_string\n    premailer = Premailer.new('<p>Test</p>', :with_html_string => true, :link_query_string => ' ')\n    premailer.to_inline_css\n  end\n\n  def test_appending_link_query_string\n    qs = 'utm_source=1234&tracking=good&amp;doublescape'\n    opts = {:base_url => 'http://example.com/',  :link_query_string => qs, :with_html_string => true, :adapter => :hpricot}\n\n    appendable = [\n        '/',\n        opts[:base_url],\n        'https://example.com/tester',\n        'images/',\n        \"#{opts[:base_url]}test.html?cn=tf&amp;c=20&amp;ord=random\",\n        '?query=string'\n    ]\n\n    not_appendable = [\n        '%DONOTCONVERT%',\n        '{DONOTCONVERT}',\n        '[DONOTCONVERT]',\n        '<DONOTCONVERT>',\n        '{@msg-txturl}',\n        '[[!unsubscribe]]',\n        '#relative',\n        'tel:5555551212',\n        'http://example.net/',\n        'mailto:premailer@example.com',\n        'ftp://example.com',\n        'gopher://gopher.floodgap.com/1/fun/twitpher'\n    ]\n\n    html = appendable.collect {|url| \"<a href='#{url}'>Link</a>\" }\n\n    premailer = Premailer.new(html.to_s, opts)\n    premailer.to_inline_css\n\n    premailer.processed_doc.search('a').each do |el|\n      href = el.attributes['href'].to_s\n      next if href.nil? or href.empty?\n      uri = URI.parse(href)\n      assert_match qs, uri.query, \"missing query string for #{el.to_s}\"\n    end\n\n    html = not_appendable.collect {|url| \"<a href='#{url}'>Link</a>\" }\n\n    premailer = Premailer.new(html.to_s, opts)\n    premailer.to_inline_css\n\n    premailer.processed_doc.search('a').each do |el|\n      href = el['href']\n      next if href.nil? or href.empty?\n      assert not_appendable.include?(href), \"link #{href} should not be converted: see #{not_appendable.to_s}\"\n    end\n  end\n\n  def test_stripping_extra_question_marks_from_query_string\n    qs = '??utm_source=1234'\n\n    premailer = Premailer.new(\"<a href='/test/?'>Link</a> <a href='/test/'>Link</a>\", :link_query_string => qs, :with_html_string => true)\n    premailer.to_inline_css\n\n    premailer.processed_doc.search('a').each do |a|\n      assert_equal '/test/?utm_source=1234', a['href'].to_s\n    end\n\n    premailer = Premailer.new(\"<a href='/test/?123&456'>Link</a>\", :link_query_string => qs, :with_html_string => true)\n    premailer.to_inline_css\n\n    assert_equal '/test/?123&456&amp;utm_source=1234', premailer.processed_doc.at('a')['href']\n  end\n\n  def test_unescape_ampersand\n    qs = 'utm_source=1234'\n\n    premailer = Premailer.new(\"<a href='/test/?q=query'>Link</a>\", :link_query_string => qs, :with_html_string => true, :unescaped_ampersand => true)\n    premailer.to_inline_css\n\n    premailer.processed_doc.search('a').each do |a|\n      assert_equal '/test/?q=query&utm_source=1234', a['href'].to_s\n    end\n  end\n\n  def test_preserving_links\n    html = \"<a href='http://example.com/index.php?pram1=one&pram2=two'>Link</a>\"\n    premailer = Premailer.new(html.to_s, :link_query_string => '', :with_html_string => true)\n    premailer.to_inline_css\n\n    assert_equal 'http://example.com/index.php?pram1=one&pram2=two', premailer.processed_doc.at('a')['href']\n\n    html = \"<a href='http://example.com/index.php?pram1=one&pram2=two'>Link</a>\"\n    premailer = Premailer.new(html.to_s, :link_query_string => 'qs', :with_html_string => true)\n    premailer.to_inline_css\n\n    assert_equal 'http://example.com/index.php?pram1=one&pram2=two&amp;qs', premailer.processed_doc.at('a')['href']\n\n  end\n\n  def test_resolving_urls_from_string\n    ['test.html', '/test.html', './test.html',\n     'test/../test.html', 'test/../test/../test.html'].each do |q|\n      assert_equal 'http://example.com/test.html', Premailer.resolve_link(q, 'http://example.com/'), q\n    end\n\n    assert_equal 'https://example.net:80/~basedir/test.html?var=1#anchor', Premailer.resolve_link('test/../test/../test.html?var=1#anchor', 'https://example.net:80/~basedir/')\n  end\n\n  def test_resolving_urls_from_uri\n    base_uri = URI.parse('http://example.com/')\n    ['test.html', '/test.html', './test.html',\n     'test/../test.html', 'test/../test/../test.html'].each do |q|\n      assert_equal 'http://example.com/test.html', Premailer.resolve_link(q, base_uri), q\n    end\n\n    base_uri = URI.parse('https://example.net:80/~basedir/')\n    assert_equal 'https://example.net:80/~basedir/test.html?var=1#anchor', Premailer.resolve_link('test/../test/../test.html?var=1#anchor', base_uri)\n\n    # base URI with a query string\n    base_uri = URI.parse('http://example.com/dir/index.cfm?newsletterID=16')\n    assert_equal 'http://example.com/dir/index.cfm?link=15', Premailer.resolve_link('?link=15', base_uri)\n\n    # URI preceded by a space\n    base_uri = URI.parse('http://example.com/')\n    assert_equal 'http://example.com/path', Premailer.resolve_link(' path', base_uri)\n  end\n\n  def test_resolving_urls_from_html_string\n    # The inner URI is on its own line to ensure that the impl doesn't match\n    # URIs based on start of line.\n    base_uri = \"<html><head></head><body>\\nhttp://example.com/\\n</body>\"\n    ['test.html', '/test.html', './test.html',\n     'test/../test.html', 'test/../test/../test.html'].each do |q|\n      Premailer.resolve_link(q, base_uri)\n    end\n  end\n\n  def test_resolving_urls_in_doc\n    # force Nokogiri since this consistenly segfaults with Hpricot\n    base_file = File.dirname(__FILE__) + '/files/base.html'\n    base_url = 'https://my.example.com:8080/test-path.html'\n    premailer = Premailer.new(base_file, :base_url => base_url, :adapter => :nokogiri)\n    premailer.to_inline_css\n    pdoc = premailer.processed_doc\n    doc = premailer.doc\n\n    # unchanged links\n    ['#l02', '#l03', '#l05', '#l06', '#l07', '#l08',\n     '#l09', '#l10', '#l11', '#l12', '#l13'].each do |link_id|\n      assert_equal doc.at(link_id).attributes['href'], pdoc.at(link_id).attributes['href'], link_id\n    end\n\n    assert_equal 'https://my.example.com:8080/', pdoc.at('#l01').attributes['href'].to_s\n    assert_equal 'https://my.example.com:8080/images/', pdoc.at('#l04').attributes['href'].to_s\n  end\n\n  def test_convertable_inline_links\n    convertable = [\n        'my/path/to',\n        'other/path',\n        '/'\n    ]\n\n    html = convertable.collect {|url| \"<a href='#{url}'>Link</a>\" }\n    premailer = Premailer.new(html.to_s, :base_url => \"http://example.com\", :with_html_string => true)\n\n    premailer.processed_doc.search('a').each do |el|\n      href = el.attributes['href'].to_s\n      assert(href =~ /http:\\/\\/example.com/, \"link #{href} is not absolute\")\n    end\n  end\n\n  def test_non_convertable_inline_links\n    not_convertable = [\n        '%DONOTCONVERT%',\n        '{DONOTCONVERT}',\n        '[DONOTCONVERT]',\n        '<DONOTCONVERT>',\n        '{@msg-txturl}',\n        '[[!unsubscribe]]',\n        '#relative',\n        'tel:5555551212',\n        'mailto:premailer@example.com',\n        'ftp://example.com',\n        'gopher://gopher.floodgap.com/1/fun/twitpher',\n        'cid:13443452066.10392logo.jpeg@inline_attachment'\n    ]\n\n    html = not_convertable.collect {|url| \"<a href='#{url}'>Link</a>\" }\n\n    premailer = Premailer.new(html.to_s, :base_url => \"example.com\", :with_html_string => true)\n    premailer.to_inline_css\n\n    premailer.processed_doc.search('a').each do |el|\n      href = el.attributes['href'].to_s\n      assert not_convertable.include?(href), \"link #{href} should not be converted: see #{not_convertable.inspect}\"\n    end\n  end\nend\n"
  },
  {
    "path": "test/test_misc.rb",
    "content": "# encoding: UTF-8\nrequire File.expand_path(File.dirname(__FILE__)) + '/helper'\n\n# Random tests for specific issues.\n#\n# The test suite will be cleaned up at some point soon.\nclass TestMisc < Premailer::TestCase\n\n  # in response to http://github.com/alexdunae/premailer/issues#issue/4\n  #\n  # NB: 2010-11-16 -- after reverting to Hpricot this test can no longer pass.\n  # It's too much of an edge case to get any dev time.\n  def test_parsing_extra_quotes\n    io = StringIO.new('<p></p>\n    <h3 \"id=\"WAR\"><a name=\"WAR\"></a>Writes and Resources</h3>\n    <table></table>')\n    premailer = Premailer.new(io, :adapter => :nokogiri)\n    assert_match /<h3>[\\s]*<a name=\"WAR\">[\\s]*<\\/a>[\\s]*Writes and Resources[\\s]*<\\/h3>/i, premailer.to_inline_css\n  end\n\n  def test_styles_in_the_body\n    html = <<END_HTML\n    <html>\n    <body>\n    <style type=\"text/css\"> p { color: red; } </style>\n\t\t<p>Test</p>\n\t\t</body>\n\t\t</html>\nEND_HTML\n\n    premailer = Premailer.new(html, :with_html_string => true)\n    premailer.to_inline_css\n\n    assert_match /color\\: red/i,  premailer.processed_doc.at('p')['style']\n  end\n\n  def test_commented_out_styles_in_the_body\n    html = <<END_HTML\n    <html>\n    <body>\n    <style type=\"text/css\"> <!-- p { color: red; } --> </style>\n\t\t<p>Test</p>\n\t\t</body>\n\t\t</html>\nEND_HTML\n\n    premailer = Premailer.new(html, :with_html_string => true)\n    premailer.to_inline_css\n\n    assert_match /color\\: red/i,  premailer.processed_doc.at('p')['style']\n  end\n\n  def test_not_applying_styles_to_the_head\n    html = <<END_HTML\n    <html>\n    <head>\n    <title>Title</title>\n    <style type=\"text/css\"> * { color: red; } </style>\n    </head>\n    <body>\n\t\t<p><a>Test</a></p>\n\t\t</body>\n\t\t</html>\nEND_HTML\n\n    [:nokogiri, :hpricot].each do |adapter|\n      premailer = Premailer.new(html, :with_html_string => true, :adapter => adapter)\n      premailer.to_inline_css\n\n      h = premailer.processed_doc.at('head')\n      assert_nil h['style']\n\n      t = premailer.processed_doc.at('title')\n      assert_nil t['style']\n    end\n  end\n\n  def test_multiple_identical_ids\n    html = <<-END_HTML\n    <html>\n    <head>\n    <style type=\"text/css\"> #the_id { color: red; } </style>\n    </head>\n    <body>\n\t\t<p id=\"the_id\">Test</p>\n\t\t<p id=\"the_id\">Test</p>\n\t\t</body>\n\t\t</html>\n    END_HTML\n\n    premailer = Premailer.new(html, :with_html_string => true)\n    premailer.to_inline_css\n    premailer.processed_doc.search('p').each do |el|\n      assert_match /red/i, el['style']\n    end\n  end\n\n  def test_preserving_styles\n    html = <<END_HTML\n    <html>\n    <head>\n    <link rel=\"stylesheet\" href=\"#\"/>\n    <style type=\"text/css\"> a:hover { color: red; } </style>\n    </head>\n    <body>\n\t\t<p><a>Test</a></p>\n\t\t</body>\n\t\t</html>\nEND_HTML\n    [:nokogiri, :hpricot].each do |adapter|\n      premailer = Premailer.new(html, :with_html_string => true, :preserve_styles => true,  :adapter => adapter)\n      premailer.to_inline_css\n      assert_equal 1, premailer.processed_doc.search('head link').length\n      assert_equal 1, premailer.processed_doc.search('head style').length\n\n      premailer = Premailer.new(html, :with_html_string => true, :preserve_styles => false, :adapter => adapter)\n      premailer.to_inline_css\n      assert_nil premailer.processed_doc.at('body link')\n\n      # should be preserved as unmergeable\n\n      assert_match /color: red/i, premailer.processed_doc.at('body style').inner_html\n\n      assert_match /a:hover/i, premailer.processed_doc.at('style').inner_html\n\n    end\n  end\n\n  def test_unmergable_rules\n    html = <<END_HTML\n    <html> <head> <style type=\"text/css\"> a { color:blue; } a:hover { color: red; } </style> </head>\n\t\t<p><a>Test</a></p>\n\t\t</body> </html>\nEND_HTML\n\n    premailer = Premailer.new(html, :with_html_string => true, :verbose => true)\n    premailer.to_inline_css\n\n    # blue should be inlined\n    refute_match /a\\:hover[\\s]*\\{[\\s]*color\\:[\\s]*blue[\\s]*;[\\s]*\\}/i, premailer.processed_doc.at('body style').inner_html\n    # red should remain in <style> block\n    assert_match /a\\:hover[\\s]*\\{[\\s]*color\\:[\\s]*red;[\\s]*\\}/i, premailer.processed_doc.at('body style').inner_html\n  end\n\n  def test_unmergable_media_queries\n    html = <<END_HTML\n    <html> <head>\n    <style type=\"text/css\">\n    a { color: blue; }\n    @media (min-width:500px) {\n      a { color: red; }\n    }\n    @media screen and (orientation: portrait) {\n      a { color: green; }\n    }\n    </style>\n    </head>\n    <body>\n    <p><a>Test</a></p>\n    </body> </html>\nEND_HTML\n\n    [:nokogiri, :hpricot].each do |adapter|\n      premailer = Premailer.new(html, :with_html_string => true, :adapter => adapter)\n      premailer.to_inline_css\n\n      style_tag = premailer.processed_doc.at('body style')\n      assert style_tag, \"#{adapter} failed to add a body style tag\"\n\n      style_tag_contents = style_tag.inner_html\n\n      assert_equal \"color: blue;\", premailer.processed_doc.at('a').attributes['style'].to_s,\n                   \"#{adapter}: Failed to inline the default style\"\n      assert_match /@media \\(min-width:500px\\) \\{.*?a \\{.*?color: red;.*?\\}.*?\\}/m, style_tag_contents,\n                   \"#{adapter}: Failed to add media query with no type to style\"\n      assert_match /@media screen and \\(orientation: portrait\\) \\{.*?a \\{.*?color: green;.*?\\}.*?\\}/m, style_tag_contents,\n                   \"#{adapter}: Failed to add media query with type to style\"\n    end\n\n  end\n\n  def test_unmergable_rules_with_no_body\n    html = <<END_HTML\n    <html>\n    <style type=\"text/css\"> a:hover { color: red; } </style>\n\t\t<p><a>Test</a></p>\n\t\t</html>\nEND_HTML\n\n    premailer = Premailer.new(html, :with_html_string => true)\n    premailer.to_inline_css\n    assert_match /a\\:hover[\\s]*\\{[\\s]*color\\:[\\s]*red;[\\s]*\\}/i, premailer.processed_doc.at('style').inner_html\n  end\n\n  # in response to https://github.com/alexdunae/premailer/issues#issue/7\n  def test_ignoring_link_pseudo_selectors\n    html = <<END_HTML\n    <html>\n    <style type=\"text/css\"> td a:link.top_links { color: red; } </style>\n    <body>\n\t\t<td><a class=\"top_links\">Test</a></td>\n\t\t</body>\n\t\t</html>\nEND_HTML\n\n    premailer = Premailer.new(html, :with_html_string => true)\n    premailer.to_inline_css\n    assert_match /color: red/, premailer.processed_doc.at('a').attributes['style'].to_s\n  end\n\n  # in response to https://github.com/alexdunae/premailer/issues#issue/7\n  #\n  # fails sometimes in JRuby, see https://github.com/alexdunae/premailer/issues/79\n  def test_parsing_bad_markup_around_tables\n    html = <<END_HTML\n    <html>\n    <style type=\"text/css\">\n      .style3 { font-size: xx-large; }\n      .style5 { background-color: #000080; }\n    </style>\n\t\t<tr>\n\t\t\t\t\t\t<td valign=\"top\" class=\"style3\">\n\t\t\t\t\t\t<!-- MSCellType=\"ContentHead\" -->\n\t\t\t\t\t\t<strong>PROMOCION CURSOS PRESENCIALES</strong></td>\n\t\t\t\t\t\t<strong>\n\t\t\t\t\t\t<td valign=\"top\" style=\"height: 125px\" class=\"style5\">\n\t\t\t\t\t\t<!-- MSCellType=\"DecArea\" -->\n\t\t\t\t\t\t<img alt=\"\" src=\"../../images/CertisegGold.GIF\" width=\"608\" height=\"87\" /></td>\n\t\t</tr>\nEND_HTML\n\n    premailer = Premailer.new(html, :with_html_string => true)\n    premailer.to_inline_css\n    assert_match /font-size: xx-large/, premailer.processed_doc.search('.style3').first.attributes['style'].to_s\n    refute_match /background: #000080/, premailer.processed_doc.search('.style5').first.attributes['style'].to_s\n    assert_match /#000080/, premailer.processed_doc.search('.style5').first.attributes['bgcolor'].to_s\n  end\n\n  # in response to https://github.com/alexdunae/premailer/issues/56\n  def test_inline_important\n    html = <<END_HTML\n    <html>\n    <style type=\"text/css\">\n      p { color: red !important; }\n    </style>\n    <body>\n      <p style='color: green !important;'>test</p></div>\n    </body>\n    </html>\nEND_HTML\n\n    premailer = Premailer.new(html, :with_html_string => true, :adapter => :nokogiri)\n    premailer.to_inline_css\n    assert_equal 'color: green !important;', premailer.processed_doc.search('p').first.attributes['style'].to_s\n  end\n\n  # in response to https://github.com/alexdunae/premailer/issues/28\n  def test_handling_shorthand_auto_properties\n    html = <<END_HTML\n    <html>\n    <style type=\"text/css\">\n      #page { margin: 0; margin-left: auto; margin-right: auto; }\n      p { border: 1px solid black; border-right: none; }\n\n    </style>\n    <body>\n      <div id='page'><p>test</p></div>\n    </body>\n    </html>\nEND_HTML\n\n    premailer = Premailer.new(html, :with_html_string => true)\n    premailer.to_inline_css\n\n    assert_match /margin: 0 auto/, premailer.processed_doc.search('#page').first.attributes['style'].to_s\n    assert_match /border-style: solid none solid solid;/, premailer.processed_doc.search('p').first.attributes['style'].to_s\n  end\n\n  def test_sorting_style_attributes\n    html = <<END_HTML\n    <html>\n    <style type=\"text/css\">\n      #page { right: 10px; left: 5px }\n    </style>\n    <body>\n      <div id='page'>test</div>\n    </body>\n    </html>\nEND_HTML\n\n    premailer = Premailer.new(html, :with_html_string => true)\n    premailer.to_inline_css\n    assert_equal \"left: 5px; right: 10px;\", premailer.processed_doc.search('#page').first.attributes['style'].to_s\n  end\n\n  def test_removing_scripts\n    html = <<END_HTML\n    <html>\n    <head>\n      <script>script to be removed</script>\n    </head>\n    <body>\n      content\n    </body>\n    </html>\nEND_HTML\n\n    [:nokogiri, :hpricot].each do |adapter|\n      premailer = Premailer.new(html, :with_html_string => true, :remove_scripts => true, :adapter => adapter)\n      premailer.to_inline_css\n      assert_equal 0, premailer.processed_doc.search('script').length\n    end\n\n    [:nokogiri, :hpricot].each do |adapter|\n      premailer = Premailer.new(html, :with_html_string => true, :remove_scripts => false, :adapter => adapter)\n      premailer.to_inline_css\n      assert_equal 1, premailer.processed_doc.search('script').length\n    end\n  end\n\n  def test_strip_important_from_attributes\n    html = <<END_HTML\n    <html>\n    <head>\n      <style>td { background-color: #FF0000 !important; }</style>\n    </head>\n    <body>\n      <table><tr><td>red</td></tr></table>\n    </body>\n    </html>\nEND_HTML\n\n    [:nokogiri, :hpricot].each do |adapter|\n      premailer = Premailer.new(html, :with_html_string => true, :adapter => adapter)\n      assert_match 'bgcolor=\"#FF0000\"', premailer.to_inline_css\n    end\n  end\n\n  def test_scripts_with_nokogiri\n    html = <<END_HTML\n    <html>\n    <body>\n    <script type=\"application/ld+json\">\n    {\n      \"@context\": \"http://schema.org\",\n      \"@type\": \"Person\",\n      \"name\": \"John Doe\",\n      \"jobTitle\": \"Graduate research assistant\",\n      \"affiliation\": \"University of Dreams\",\n      \"additionalName\": \"Johnny\",\n      \"url\": \"http://www.example.com\",\n      \"address\": {\n        \"@type\": \"PostalAddress\",\n        \"streetAddress\": \"1234 Peach Drive\",\n        \"addressLocality\": \"Wonderland\",\n        \"addressRegion\": \"Georgia\"\n      }\n    }\n    </script\n    </body>\n    </html>\nEND_HTML\n\n    premailer = Premailer.new(html, :with_html_string => true, :remove_scripts => false, :adapter => :nokogiri)\n    premailer.to_inline_css\n\n    assert !premailer.processed_doc.css('script[type=\"application/ld+json\"]').first.children.first.cdata?\n  end\n\n  def test_style_without_data_in_content\n    html = <<END_HTML\n    <html>\n    <head>\n      <style>#logo {content:url(good.png)};}</style>\n    </head>\n    <body>\n      <image id=\"logo\"/>\n    </body>\n    </html>\nEND_HTML\n    [:nokogiri, :hpricot].each do |adapter|\n      premailer = Premailer.new(html, :with_html_string => true, :adapter => adapter)\n      assert_match 'content: url(good.png)', premailer.to_inline_css\n    end\n  end\n\n  def test_style_with_data_in_content\n    html = <<END_HTML\n    <html>\n    <head>\n      <style>#logo {content: url(data:image/png;base64,LOTSOFSTUFF)};}</style>\n    </head>\n    <body>\n      <image id=\"logo\"/>\n    </body>\n    </html>\nEND_HTML\n    [:nokogiri, :hpricot].each do |adapter|\n      premailer = Premailer.new(html, :with_html_string => true, :adapter => adapter)\n      assert_match 'content: url(data:image/png;base64,LOTSOFSTUFF)', premailer.to_inline_css\n    end\n  end\n\nend\n"
  },
  {
    "path": "test/test_premailer.rb",
    "content": "# -*- encoding: UTF-8 -*-\n\nrequire File.expand_path(File.dirname(__FILE__)) + '/helper'\n\nclass TestPremailer < Premailer::TestCase\n  def test_special_characters_nokogiri\n    html = \t'<p>cédille c&eacute; & garçon gar&#231;on à &agrave; &nbsp; &amp; &copy;</p>'\n    premailer = Premailer.new(html, :with_html_string => true, :adapter => :nokogiri)\n    premailer.to_inline_css\n    assert_equal 'c&eacute;dille c&eacute; &amp; gar&ccedil;on gar&ccedil;on &agrave; &agrave; &nbsp; &amp; &copy;', premailer.processed_doc.at('p').inner_html\n  end\n\n  def test_special_characters_nokogiri_remote\n    remote_setup('chars.html', :adapter => :nokogiri)\n    @premailer.to_inline_css\n    assert_equal 'c&eacute;dille c&eacute; &amp; gar&ccedil;on gar&ccedil;on &agrave; &agrave; &nbsp; &amp; &copy;', @premailer.processed_doc.at('p').inner_html\n  end\n\n  #def test_cyrillic_nokogiri_remote\n  #  if RUBY_VERSION =~ /1.9/\n  #    remote_setup('iso-8859-5.html', :adapter => :nokogiri) #, :encoding => 'iso-8859-5')\n  #  \t@premailer.to_inline_css\n  #    assert_equal Encoding.find('ISO-8859-5'), @premailer.processed_doc.at('p').inner_html.encoding\n  #  end\n  #end\n\n  # TODO: this passes when run from rake but not when run from:\n  #  ruby -Itest test/test_premailer.rb -n test_special_characters_hpricot\n  def test_special_characters_hpricot\n    html = \t'<p>cédille c&eacute; & garçon gar&#231;on à &agrave; &nbsp; &amp;</p>'\n    premailer = Premailer.new(html, :with_html_string => true, :adapter => :hpricot)\n    premailer.to_inline_css\n    assert_equal 'c&eacute;dille c&eacute; &amp; gar&ccedil;on gar&ccedil;on &agrave; &agrave; &nbsp; &amp;', premailer.processed_doc.at('p').inner_html\n  end\n\n  def test_detecting_html\n    [:nokogiri, :hpricot].each do |adapter|\n      remote_setup('base.html', :adapter => adapter)\n      assert !@premailer.is_xhtml?\n    end\n  end\n\n  def test_detecting_xhtml\n    [:nokogiri, :hpricot].each do |adapter|\n      remote_setup('xhtml.html', :adapter => adapter)\n      assert @premailer.is_xhtml?\n    end\n  end\n\n  def test_self_closing_xhtml_tags\n    [:nokogiri, :hpricot].each do |adapter|\n      remote_setup('xhtml.html', :adapter => adapter)\n      assert_match /<br[\\s]*\\/>/, @premailer.to_s\n      assert_match /<br[\\s]*\\/>/, @premailer.to_inline_css\n    end\n  end\n\n  def test_non_self_closing_html_tags\n    [:nokogiri, :hpricot].each do |adapter|\n      remote_setup('html4.html', :adapter => adapter)\n      assert_match /<br>/, @premailer.to_s\n      assert_match /<br>/, @premailer.to_inline_css\n    end\n  end\n\n  def test_mailtos_with_query_strings\n    html = <<END_HTML\n    <html>\n\t\t<a href=\"mailto:info@example.com?subject=Programmübersicht&amp;body=Lorem ipsum dolor sit amet.\">Test</a>\n\t\t</html>\nEND_HTML\n\n    qs = 'testing=123'\n\n    [:nokogiri, :hpricot].each do |adapter|\n      premailer = Premailer.new(html, :with_html_string => true, :link_query_string => qs, :adapter => adapter)\n      premailer.to_inline_css\n      refute_match /testing=123/, premailer.processed_doc.search('a').first.attributes['href'].to_s\n    end\n  end\n\n  def test_escaping_strings\n    local_setup\n\n    str = %q{url(\"/images/test.png\");}\n    assert_equal(\"url(\\'/images/test.png\\');\", Premailer.escape_string(str))\n  end\n\n  def test_preserving_ignored_style_elements\n    [:nokogiri, :hpricot].each do |adapter|\n      local_setup('ignore.html', :adapter => adapter)\n\n      assert_nil @doc.at('h1')['style']\n    end\n  end\n\n  def test_preserving_ignored_link_elements\n    [:nokogiri, :hpricot].each do |adapter|\n      local_setup('ignore.html', :adapter => adapter)\n\n      assert_nil @doc.at('body')['style']\n    end\n  end\n\n  def test_importing_local_css\n    # , :hpricot\n    [:nokogiri].each do |adapter|\n      local_setup('base.html', :adapter => adapter)\n\n      # noimport.css (print stylesheet) sets body { background } to red\n      refute_match /red/, @doc.at('body').attributes['style'].to_s\n\n      # import.css sets .hide to { display: none }\n      assert_match /display: none/, @doc.at('#hide01').attributes['style'].to_s\n    end\n  end\n\n  def test_css_to_attributes\n    [:nokogiri, :hpricot].each do |adapter|\n      html = '<td style=\"background-color: #FFF;\"></td>'\n      premailer = Premailer.new(html, {:with_html_string => true, :adapter => adapter, :css_to_attributes => true})\n      premailer.to_inline_css\n      assert_equal ';', premailer.processed_doc.search('td').first.attributes['style'].to_s\n      assert_equal '#FFF', premailer.processed_doc.search('td').first.attributes['bgcolor'].to_s\n    end\n  end\n\n  def test_avoid_changing_css_to_attributes\n    [:nokogiri, :hpricot].each do |adapter|\n      html = '<td style=\"background-color: #FFF;\"></td>'\n      premailer = Premailer.new(html, {:with_html_string => true, :adapter => adapter, :css_to_attributes => false})\n      premailer.to_inline_css\n      assert_match /background: #FFF/, premailer.processed_doc.search('td').first.attributes['style'].to_s\n    end\n  end\n\n  def test_importing_remote_css\n    [:nokogiri, :hpricot].each do |adapter|\n      remote_setup('base.html', :adapter => adapter)\n\n      # noimport.css (print stylesheet) sets body { background } to red\n      refute_match /red/, @doc.at('body')['style']\n\n      # import.css sets .hide to { display: none }\n      assert_match /display: none/, @doc.at('#hide01')['style']\n    end\n  end\n\n  def test_importing_css_as_string\n    files_base = File.expand_path(File.dirname(__FILE__)) + '/files/'\n\n    css_string = IO.read(File.join(files_base, 'import.css'))\n\n    [:nokogiri, :hpricot].each do |adapter|\n      premailer = Premailer.new(File.join(files_base, 'no_css.html'), {:css_string => css_string, :adapter => adapter})\n      premailer.to_inline_css\n      @doc = premailer.processed_doc\n\n      # import.css sets .hide to { display: none }\n      assert_match /display: none/, @doc.at('#hide01')['style']\n    end\n  end\n\n  def test_local_remote_check\n    assert Premailer.local_data?( StringIO.new('a') )\n    assert Premailer.local_data?( '/path/' )\n    assert !Premailer.local_data?( 'http://example.com/path/' )\n\n    # the old way is deprecated but should still work\n    premailer = Premailer.new( StringIO.new('a') )\n    silence_stderr do\n      assert premailer.local_uri?( '/path/' )\n    end\n  end\n\n  def test_initialize_can_accept_io_object\n    [:nokogiri, :hpricot].each do |adapter|\n      io = StringIO.new('hi mom')\n      premailer = Premailer.new(io, :adapter => adapter)\n      assert_match /hi mom/, premailer.to_inline_css\n    end\n  end\n\n  def test_initialize_can_accept_html_string\n    [:nokogiri, :hpricot].each do |adapter|\n      premailer = Premailer.new('<p>test</p>', :with_html_string => true, :adapter => adapter)\n      assert_match /test/, premailer.to_inline_css\n    end\n  end\n\n  def test_initialize_no_escape_attributes_option\n    html = <<END_HTML\n    <html> <body>\n    <a id=\"google\" href=\"http://google.com\">Google</a>\n    <a id=\"noescape\" href=\"{{link_url}}\">Link</a>\n\t\t</body> </html>\nEND_HTML\n\n    [:nokogiri, :hpricot].each do |adapter|\n      pm = Premailer.new(html, :with_html_string => true, :adapter => adapter, :escape_url_attributes => false)\n      pm.to_inline_css\n      doc = pm.processed_doc\n      assert_equal doc.at('#google')['href'], 'http://google.com'\n      assert_equal doc.at('#noescape')['href'], '{{link_url}}'\n    end\n  end\n\n  def test_remove_ids\n    html = <<END_HTML\n    <html> <head> <style type=\"text/css\"> #remove { color:blue; } </style> </head>\n    <body>\n\t\t<p id=\"remove\"><a href=\"#keep\">Test</a></p>\n\t\t<p id=\"keep\">Test</p>\n\t\t</body> </html>\nEND_HTML\n\n    [:nokogiri, :hpricot].each do |adapter|\n      pm = Premailer.new(html, :with_html_string => true, :remove_ids => true, :adapter => adapter)\n      pm.to_inline_css\n      doc = pm.processed_doc\n      assert_nil doc.at('#remove')\n      assert_nil doc.at('#keep')\n      hashed_id = doc.at('a')['href'][1..-1]\n      refute_nil doc.at(\"\\##{hashed_id}\")\n    end\n  end\n\n  def test_reset_contenteditable\n    html = <<-___\n    <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n    <html> <head> <style type=\"text/css\"> #remove { color:blue; } </style> </head>\n    <body>\n    <div contenteditable=\"true\" id=\"editable\"> Test </div>\n    </body> </html>\n    ___\n    [:nokogiri, :hpricot].each do |adapter|\n      pm = Premailer.new(html, :with_html_string => true, :reset_contenteditable => true, :adapter => adapter)\n      pm.to_inline_css\n      doc = pm.processed_doc\n      assert_nil doc.at('#editable')['contenteditable'],\n                 \"#{adapter}: contenteditable attribute not removed\"\n    end\n  end\n\n  def test_carriage_returns_as_entities\n    html = <<-html\n    <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n    <html>\n    <body>\\n\\r<p>test</p>\\n\\r<p>test</p>\n    </body></html>\n    html\n\n    [:nokogiri, :hpricot].each do |adapter|\n      pm = Premailer.new(html, :with_html_string => true, :adapter => adapter)\n      assert_match /\\r/, pm.to_inline_css\n    end\n  end\n\n\n  def test_advanced_selectors\n    remote_setup('base.html', :adapter => :nokogiri)\n    assert_match /italic/, @doc.at('h2 + h3')['style']\n    assert_match /italic/, @doc.at('p[attr~=quote]')['style']\n    assert_match /italic/, @doc.at('ul li:first-of-type')['style']\n\n    remote_setup('base.html', :adapter => :hpricot)\n    assert_match /italic/, @doc.at('p[@attr~=\"quote\"]')['style']\n    assert_match /italic/, @doc.at('ul li:first-of-type')['style']\n  end\n\n  def test_premailer_related_attributes\n    html = <<END_HTML\n    <html> <head> <style>table { -premailer-width: 500; } td { -premailer-height: 20}; </style>\n    <body>\n\t\t<table> <tr> <td> Test </td> </tr> </table>\n\t\t</body> </html>\nEND_HTML\n\n    [:nokogiri, :hpricot].each do |adapter|\n      pm = Premailer.new(html, :with_html_string => true, :adapter => adapter)\n      pm.to_inline_css\n      doc = pm.processed_doc\n      assert_equal '500', doc.at('table')['width']\n      assert_equal '20', doc.at('td')['height']\n    end\n  end\n\n  def test_include_link_tags_option\n    local_setup('base.html', :adapter => :nokogiri, :include_link_tags => true)\n    assert_match /1\\.231/, @doc.at('body').attributes['style'].to_s\n    assert_match /display: none/, @doc.at('.hide').attributes['style'].to_s\n\n    local_setup('base.html', :adapter => :nokogiri, :include_link_tags => false)\n    refute_match /1\\.231/, @doc.at('body').attributes['style'].to_s\n    assert_match /display: none/, @doc.at('.hide').attributes['style'].to_s\n  end\n\n  def test_include_style_tags_option\n    local_setup('base.html', :adapter => :nokogiri, :include_style_tags => true)\n    assert_match /1\\.231/, @doc.at('body').attributes['style'].to_s\n    assert_match /display: block/, @doc.at('#iphone').attributes['style'].to_s\n\n    local_setup('base.html', :adapter => :nokogiri, :include_style_tags => false)\n    assert_match /1\\.231/, @doc.at('body').attributes['style'].to_s\n    refute_match /display: block/, @doc.at('#iphone').attributes['style'].to_s\n  end\n\n  def test_input_encoding\n    html_special_characters = \"Ää, Öö, Üü\"\n    expected_html = \"<!DOCTYPE html PUBLIC \\\"-//W3C//DTD HTML 4.0 Transitional//EN\\\" \\\"http://www.w3.org/TR/REC-html40/loose.dtd\\\">\\n<html><body><p>\" + html_special_characters + \"</p></body></html>\\n\"\n    pm = Premailer.new(html_special_characters, :with_html_string => true, :adapter => :nokogiri, :input_encoding => \"UTF-8\")\n    assert_equal expected_html, pm.to_inline_css\n  end\n\n  # output_encoding option should return HTML Entities when set to US-ASCII\n  def test_output_encoding\n    html_special_characters = \"©\"\n    html_entities_characters = \"&#169;\"\n    expected_html = \"<!DOCTYPE html PUBLIC \\\"-//W3C//DTD HTML 4.0 Transitional//EN\\\" \\\"http://www.w3.org/TR/REC-html40/loose.dtd\\\">\\n<html><body><p>\" + html_entities_characters + \"</p></body></html>\\n\"\n    pm = Premailer.new(html_special_characters, :output_encoding => \"US-ASCII\", :with_html_string => true, :adapter => :nokogiri, :input_encoding => \"UTF-8\");\n    assert_equal expected_html, pm.to_inline_css\n  end\n\n  def test_meta_encoding_downcase\n    meta_encoding = '<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">'\n    expected_html = Regexp.new(Regexp.escape('<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">'), Regexp::IGNORECASE)\n    pm = Premailer.new(meta_encoding, :with_html_string => true, :adapter => :nokogiri, :input_encoding => \"utf-8\")\n    assert_match expected_html, pm.to_inline_css\n  end\n\n  def test_meta_encoding_upcase\n    meta_encoding = '<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">'\n    expected_html = Regexp.new(Regexp.escape('<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">'), Regexp::IGNORECASE)\n    pm = Premailer.new(meta_encoding, :with_html_string => true, :adapter => :nokogiri, :input_encoding => \"UTF-8\")\n    assert_match expected_html, pm.to_inline_css\n  end\n\n  def test_htmlentities\n    html_entities = \"&#8217;\"\n    expected_html = \"<!DOCTYPE html PUBLIC \\\"-//W3C//DTD HTML 4.0 Transitional//EN\\\" \\\"http://www.w3.org/TR/REC-html40/loose.dtd\\\">\\n<html><body><p>'</p></body></html>\\n\"\n    pm = Premailer.new(html_entities, :with_html_string => true, :adapter => :nokogiri, :replace_html_entities => true)\n    assert_equal expected_html, pm.to_inline_css\n  end\n\n  # If a line other than the first line in the html string begins with a URI\n  # Premailer should not identify the html string as a URI. Otherwise the following\n  # exception would be raised: ActionView::Template::Error: bad URI(is not URI?)\n  def test_line_starting_with_uri_in_html_with_linked_css\n    files_base = File.expand_path(File.dirname(__FILE__)) + '/files/'\n    html_string = IO.read(File.join(files_base, 'html_with_uri.html'))\n\n    premailer = Premailer.new(html_string, :with_html_string => true)\n    premailer.to_inline_css\n  end\n\n  def test_empty_html_nokogiri\n    html = \"\"\n    css = \"a:hover {color:red;}\"\n\n    pm = Premailer.new(html, :with_html_string => true, :css_string => css, :adapter => :nokogiri)\n    pm.to_inline_css\n  end\n\n  def silence_stderr(&block)\n    orig_stderr = $stderr\n    $stderr = File.open(File::NULL, 'w')\n    block.call\n  ensure\n    $stderr = orig_stderr\n  end\nend\n"
  },
  {
    "path": "test/test_warnings.rb",
    "content": "# encoding: UTF-8\nrequire File.expand_path(File.dirname(__FILE__)) + '/helper'\n\nclass TestWarnings < Premailer::TestCase\n  def test_element_warnings\n    html = <<END_HTML\n    <!DOCTYPE html>\n    <html>\n    <head><link rel=\"alternate\" href=\"http://example.com/\"></head>\n    <body>\n    <form method=\"post\"> Test </form>\n    </body>\n\t\t</html>\nEND_HTML\n    \n    [:nokogiri, :hpricot].each do |adapter|\n      warnings = get_warnings(html, adapter)\n      assert_equal 2, warnings.length\n      assert warnings.any? { |w| w[:message] == 'form HTML element'}\n      assert warnings.any? { |w| w[:message] == 'link HTML element'}\n    end\n  end\n\n  def test_css_warnings\n    html = <<END_HTML\n    <!DOCTYPE html>\n    <html><body>\n    <div style=\"margin: 5px; height: 100px;\">Test</div>\n    </body></html>\nEND_HTML\n\n    [:nokogiri, :hpricot].each do |adapter|\n      warnings = get_warnings(html, adapter)\n      assert_equal 2, warnings.length\n      assert warnings.any? { |w| w[:message] == 'height CSS property'}\n      assert warnings.any? { |w| w[:message] == 'margin CSS property'}\n    end\n  end\n\n  def test_css_aliased_warnings\n    html = <<END_HTML\n    <!DOCTYPE html>\n    <html><body>\n    <div style=\"margin-top: 5px;\">Test</div>\n    </body></html>\nEND_HTML\n\n    [:nokogiri, :hpricot].each do |adapter|\n      warnings = get_warnings(html, adapter)\n      assert_equal 1, warnings.length\n      assert warnings.any? { |w| w[:message] == 'margin-top CSS property'}\n    end\n  end\n\n  def test_attribute_warnings\n    html = <<END_HTML\n    <!DOCTYPE html>\n    <html><body>\n    <img src=\"#\" ismap>\n    </body></html>\nEND_HTML\n\n    [:nokogiri, :hpricot].each do |adapter|\n      warnings = get_warnings(html, adapter)\n      assert_equal 1, warnings.length\n      assert warnings.any? { |w| w[:message] == 'ismap HTML attribute'}\n    end\n  end\n\n  def test_warn_level\n    html = <<END_HTML\n    <!DOCTYPE html>\n    <html><body>\n    <div style=\"color: red; font-family: sans-serif;\">Test</div>\n    </body></html>\nEND_HTML\n\n    [:nokogiri, :hpricot].each do |adapter|\n      warnings = get_warnings(html, adapter, Premailer::Warnings::SAFE)\n      assert_equal 2, warnings.length\n    end\n\n    [:nokogiri, :hpricot].each do |adapter|\n      warnings = get_warnings(html, adapter, Premailer::Warnings::POOR)\n      assert_equal 1, warnings.length\n    end\n  end\n  \nprotected\n  def get_warnings(html, adapter = :nokogiri, warn_level = Premailer::Warnings::SAFE)\n    pm = Premailer.new(html, {:adpater => adapter, :with_html_string => true, :warn_level => warn_level})\n    pm.to_inline_css\n    pm.check_client_support  \n  end\nend\n"
  }
]