Skip to content

Instantly share code, notes, and snippets.

@ChristianPeters
Created April 16, 2012 12:29
Show Gist options
  • Save ChristianPeters/2398394 to your computer and use it in GitHub Desktop.
Save ChristianPeters/2398394 to your computer and use it in GitHub Desktop.
Adapted for Rails Asset Pipeline: Split CSS files so that there are no more than a given number of selectors in one style sheet. This is a tool to cope with Internet Explorer's limitation of max. 4095 selectors per stylesheet.
#...
module MyProject
class Application < Rails::Application
config.assets.precompile += %w( ie6.css ie6_portion2.css ie7.css ie7_portion2.css ie8.css ie8_portion2.css ie9.css ie9_portion2.css)
#...
require 'css_splitter'
Rails.application.assets.register_engine '.split2', CssSplitter::SprocketsEngine
//= include 'ie8.css'
-#...
-# Rails Dev Boost compiles all templates that are included here regardless of browser requests
-# FIXME: Consider to remove Rails Dev Boost and this switch after updating to Rails 3.2
-# Switch testing_for to e.g. :ie7 when testing for ie7
- testing_for = nil
- if !Rails.env.development? || testing_for == :ie6
/[if IE 6]
= stylesheet_link_tag :ie6
= stylesheet_link_tag :ie6_portion2
- if !Rails.env.development? || testing_for == :ie7
/[if IE 7]
= stylesheet_link_tag :ie7
= stylesheet_link_tag :ie7_portion2
- if !Rails.env.development? || testing_for == :ie8
/[if IE 8]
= stylesheet_link_tag :ie8
= stylesheet_link_tag :ie8_portion2
- if !Rails.env.development? || [:ie6, :ie7, :ie8].include?(testing_for)
/[if lt IE 9]
= javascript_include_tag "//html5shim.googlecode.com/svn/trunk/html5.js"
= javascript_include_tag 'ie/selectivizr'
- if !Rails.env.development? || testing_for == :ie9
/[if IE 9]
= stylesheet_link_tag :ie9
= stylesheet_link_tag :ie9_portion2
require 'tilt'
module CssSplitter
class SprocketsEngine < Tilt::Template
def self.engine_initialized?
true
end
def prepare
end
def evaluate(scope, locals, &block)
part = scope.pathname.extname =~ /(\d+)$/ && $1 || 0
CssSplitter.split_string data, part.to_i
end
end
MAX_SELECTORS_DEFAULT = 4095
def self.split(infile, outdir = File.dirname(infile), max_selectors = MAX_SELECTORS_DEFAULT)
raise "infile could not be found" unless File.exists? infile
rules = IO.readlines(infile, "}")
return if rules.first.nil?
charset_statement, rules[0] = rules.first.partition(/^\@charset[^;]+;/)[1,2]
return if rules.nil?
file_id = 1 # The infile remains the first file
selectors_count = 0
output = nil
rules.each do |rule|
rule_selectors_count = count_selectors_of_rule rule
selectors_count += rule_selectors_count
# Nothing happens until the selectors limit is reached for the first time
if selectors_count > max_selectors
# Close current file if there is already one
output.close if output
# Prepare next file
file_id += 1
filename = File.join(outdir, File.basename(infile, File.extname(infile)) + "_#{file_id.to_s}" + File.extname(infile))
output = File.new(filename, "w")
output.write charset_statement
# Reset count with current rule count
selectors_count = rule_selectors_count
end
output.write rule if output
end
end
def self.split_string(css_string, part = 1, max_selectors = MAX_SELECTORS_DEFAULT)
rules = split_string_into_rules(css_string)
extract_part rules, part, max_selectors
end
def self.split_string_into_rules(css_string)
strip_comments(css_string).chomp.scan /[^}]*}/
end
def self.extract_part(rules, part = 1, max_selectors = MAX_SELECTORS_DEFAULT)
return if rules.first.nil?
charset_statement, rules[0] = rules.first.partition(/^\@charset[^;]+;/)[1,2]
return if rules.nil?
output = charset_statement
selectors_count = 0
selector_range = max_selectors * (part - 1) + 1 .. max_selectors * part
rules.each do |rule|
rule_selectors_count = count_selectors_of_rule rule
selectors_count += rule_selectors_count
if selector_range.cover? selectors_count
output << rule
elsif selectors_count > selector_range.end
break
end
end
output
end
def self.count_selectors(css_file)
raise "file could not be found" unless File.exists? css_file
rules = IO.readlines(css_file, '}')
return if rules.first.nil?
charset_statement, rules[0] = rules.first.partition(/^\@charset[^;]+;/)[1,2]
return if rules.first.nil?
rules.sum {|rule| count_selectors_of_rule(rule)}.tap do |result|
puts File.basename(css_file) + " contains #{result} selectors."
end
end
def self.count_selectors_of_rule(rule)
strip_comments(rule).partition(/\{/).first.scan(/,/).count.to_i + 1
end
private
def self.strip_comments(s)
s.gsub(/\/\/.*$/, "").gsub(/\/\*.*?\*\//, "")
end
end
@jhilden
Copy link

jhilden commented Sep 10, 2012

Hi @ChristianPeters,

pretty nifty workaround with the .splitx file endings.

Any plans to turn this into a gem?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment