Created
October 7, 2014 20:49
-
-
Save seafarer/b1b94c58ee3f688568e1 to your computer and use it in GitHub Desktop.
advanced css splitter to deal with < IE 9 4095 selector bug. Via https://github.com/zweilove/css_splitter/blob/master/lib/css_splitter/splitter.rb and https://gist.github.com/ethanhinson/7521333
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Requre a specific version in this file: | |
# gem 'zurb-foundation', '=4.3.1' | |
require 'zurb-foundation' | |
# Require any additional compass plugins here. | |
class Splitter | |
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? | |
# The infile remains the first file | |
file_id = 1 | |
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 | |
# returns the specified split of the passed css_string | |
def self.split_string(css_string, split = 1, max_selectors = MAX_SELECTORS_DEFAULT) | |
rules = split_string_into_rules(css_string) | |
extract_part rules, split, max_selectors | |
end | |
# splits string into array of rules (also strips comments) | |
def self.split_string_into_rules(css_string) | |
strip_comments(css_string).chomp.scan /[^}]*}/ | |
end | |
# extracts the specified part of an overlong CSS string | |
def self.extract_part(rules, part = 1, max_selectors = MAX_SELECTORS_DEFAULT) | |
return if rules.first.nil? | |
charset_statement, rules[0] = extract_charset(rules.first) | |
return if rules.nil? | |
output = charset_statement || "" | |
selectors_count = 0 | |
selector_range = max_selectors * (part - 1) + 1 .. max_selectors * part # e.g (4096..8190) | |
current_media = nil | |
selectors_in_media = 0 | |
first_hit = true | |
rules.each do |rule| | |
media_part = extract_media(rule) | |
if media_part | |
current_media = media_part | |
selectors_in_media = 0 | |
end | |
rule_selectors_count = count_selectors_of_rule rule | |
selectors_count += rule_selectors_count | |
if rule =~ /\A\s*}\z$/ | |
current_media = nil | |
# skip the line if the close bracket is the first rule for the new file | |
next if first_hit | |
end | |
if selector_range.cover? selectors_count # add rule to current output if within selector_range | |
if media_part | |
output << media_part | |
elsif first_hit && current_media | |
output << current_media | |
end | |
selectors_in_media += rule_selectors_count if current_media.present? | |
output << rule | |
first_hit = false | |
elsif selectors_count > selector_range.end # stop writing to output | |
break | |
end | |
end | |
if current_media.present? and selectors_in_media > 0 | |
output << '}' | |
end | |
output | |
end | |
# count selectors of one individual CSS rule | |
def self.count_selectors_of_rule(rule) | |
parts = strip_comments(rule).partition(/\{/) | |
parts[1].empty? ? 0 : parts.first.scan(/,/).count.to_i + 1 | |
end | |
# count selectors of a CSS stylesheet (not used by SprocketsEngine) | |
def self.count_selectors(css_file) | |
raise "file could not be found" unless File.exists? css_file | |
rules = split_string_into_rules(File.read css_file) | |
return if rules.first.nil? | |
rules.sum{ |rule| count_selectors_of_rule(rule) } | |
end | |
private | |
def self.extract_media(rule) | |
if rule.sub!(/^\s*(@media[^{]*{)([^{}]*{[^}]*})$/) { $2 } | |
$1 | |
end | |
end | |
# extracts potential charset declaration from the first rule | |
def self.extract_charset(rule) | |
if rule.include?('charset') | |
rule.partition(/^\@charset[^;]+;/)[1,2] | |
else | |
[nil, rule] | |
end | |
end | |
def self.strip_comments(s) | |
s.gsub(/\/\*.*?\*\//m, "") | |
end | |
end | |
# Set this to the root of your project when deployed: | |
http_path = "../" | |
css_dir = "css" | |
sass_dir = "scss" | |
images_dir = "images" | |
javascripts_dir = "js" | |
fonts_dir = "fonts" | |
# You can select your preferred output style here (can be overridden via the command line): | |
# output_style = :expanded or :nested or :compact or :compressed | |
output_style = :expanded | |
# To enable relative paths to assets via compass helper functions. Uncomment: | |
# relative_assets = true | |
# To disable debugging comments that display the original location of your selectors. Uncomment: | |
# line_comments = false | |
line_comments = true | |
# If you prefer the indented syntax, you might want to regenerate this | |
# project again passing --syntax sass, or you can uncomment this: | |
# preferred_syntax = :sass | |
# and then run: | |
# sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass | |
on_stylesheet_saved do |path| | |
Splitter.split(path) unless path[/\d+$/] | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment