Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also .

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also .
Choose a Base Repository
fecori/libcss2less
thomaspierson/libcss2less
sickill/css2less
91Dovzhenko/libcss2less
AlexanderDegterev/libcss2less
ChaosPower/libcss2less
EdsonF/libcss2less
EihabShadeed/libcss2less
Fusty/libcss2less
Imagine-Programming/libcss2less
JamesFT/libcss2less
JamesKhoury/libcss2less
ShashiDeopa1/libcss2less
Tash1ro/libcss2less
YaroslavB/libcss2less
a-shardar-iit/libcss2less
alingam/libcss2less
andrew703/libcss2less
astipili/libcss2less
baejina/libcss2less
beau6183/libcss2less
binjuhor/libcss2less
borovez/libcss2less
brentini/libcss2less
cabda/libcss2less
choonkeat/css2less
chuaaron/libcss2less
csmithmedia/libcss2less
danmv/libcss2less
denji/css2less
dhfromkorea/libcss2less
dipu06/libcss2less
e911miri/libcss2less
gabrielcury/libcss2less
gjudki/libcss2less
grizzlys/libcss2less
grundprinzip/libcss2less
hexdump42/libcss2less
htmlstrap/libcss2less
jamesgiroux/libcss2less
jarey/libcss2less
jgkim7/libcss2less
joffff/libcss2less
joshuapekera/libcss2less
kararade/libcss2less
kublaj/libcss2less
leonmsaia/libcss2less
lizenhuii/libcss2less
medium89/libcss2less
mindsharelabs/libcss2less
nanqiu/css2less
netcon-source/css2less
netconstructor/libcss2less
ngandamalla/libcss2less
nguyenlamzx/libcss2less
nutangwari/libcss2less
pedroresende/libcss2less
prestonporter/libcss2less
qinglingquan/css2less
quangthien27/libcss2less
rajeshodayanchal/libcss2less
ricardofrance/libcss2less
royriojas/libcss2less
rroch/libcss2less
saske505/libcss2less
sdgdsffdsfff/css2less
sguilfoyle/libcss2less
shabbarabbas/libcss2less
shinvdu/libcss2less
shook2012/libcss2less
sonukry/libcss2less
spark85/libcss2less
symsec/libcss2less
t-web/css2less
tdhung80/libcss2less
theylooksotired/libcss2less
tools-alexuser01/libcss2less
ungly/libcss2less
vadeneev/libcss2less
vcichecka/libcss2less
virginiarcruz/libcss2less
wildcn/css2less
wizsilex/libcss2less
yesu7/libcss2less
yunikim322/libcss2less
yuzin81/libcss2less
zeropopular/libcss2less
Nothing to show
Choose a base branch
Nothing to show
...
Choose a Head Repository
fecori/libcss2less
thomaspierson/libcss2less
sickill/css2less
91Dovzhenko/libcss2less
AlexanderDegterev/libcss2less
ChaosPower/libcss2less
EdsonF/libcss2less
EihabShadeed/libcss2less
Fusty/libcss2less
Imagine-Programming/libcss2less
JamesFT/libcss2less
JamesKhoury/libcss2less
ShashiDeopa1/libcss2less
Tash1ro/libcss2less
YaroslavB/libcss2less
a-shardar-iit/libcss2less
alingam/libcss2less
andrew703/libcss2less
astipili/libcss2less
baejina/libcss2less
beau6183/libcss2less
binjuhor/libcss2less
borovez/libcss2less
brentini/libcss2less
cabda/libcss2less
choonkeat/css2less
chuaaron/libcss2less
csmithmedia/libcss2less
danmv/libcss2less
denji/css2less
dhfromkorea/libcss2less
dipu06/libcss2less
e911miri/libcss2less
gabrielcury/libcss2less
gjudki/libcss2less
grizzlys/libcss2less
grundprinzip/libcss2less
hexdump42/libcss2less
htmlstrap/libcss2less
jamesgiroux/libcss2less
jarey/libcss2less
jgkim7/libcss2less
joffff/libcss2less
joshuapekera/libcss2less
kararade/libcss2less
kublaj/libcss2less
leonmsaia/libcss2less
lizenhuii/libcss2less
medium89/libcss2less
mindsharelabs/libcss2less
nanqiu/css2less
netcon-source/css2less
netconstructor/libcss2less
ngandamalla/libcss2less
nguyenlamzx/libcss2less
nutangwari/libcss2less
pedroresende/libcss2less
prestonporter/libcss2less
qinglingquan/css2less
quangthien27/libcss2less
rajeshodayanchal/libcss2less
ricardofrance/libcss2less
royriojas/libcss2less
rroch/libcss2less
saske505/libcss2less
sdgdsffdsfff/css2less
sguilfoyle/libcss2less
shabbarabbas/libcss2less
shinvdu/libcss2less
shook2012/libcss2less
sonukry/libcss2less
spark85/libcss2less
symsec/libcss2less
t-web/css2less
tdhung80/libcss2less
theylooksotired/libcss2less
tools-alexuser01/libcss2less
ungly/libcss2less
vadeneev/libcss2less
vcichecka/libcss2less
virginiarcruz/libcss2less
wildcn/css2less
wizsilex/libcss2less
yesu7/libcss2less
yunikim322/libcss2less
yuzin81/libcss2less
zeropopular/libcss2less
Nothing to show
Choose a head branch
Nothing to show
Checking mergeability… Don’t worry, you can still create the pull request.
  • 2 commits
  • 3 files changed
  • 0 commit comments
  • 2 contributors
Commits on Jun 02, 2013
Introducing color extraction and vendor prefix handling
This commit allows to automatically extract all colors that are used
in the css file into global variables that can be changed more easily.
In addition this adds an option to automatically extract vendor
prefixed logic into mixins so that they become easier digestible.

Essentially this means, that this block:

    .service-block .span4 {
      color: red;
      -webkit-transition:all 0.3s ease-in-out;
      -moz-transition:all 0.3s ease-in-out;
      -o-transition:all 0.3s ease-in-out;
      transition:all 0.3s ease-in-out;
    }

will become this in LESS

    @color0: red;

    .vp-transition(@p0; @p1; @p2) {
      -webkit-transition: @p0 @p1 @p2;
      -moz-transition: @p0 @p1 @p2;
      -o-transition: @p0 @p1 @p2;
      transition: @p0 @p1 @p2;
    }

    .service-block .span4 {
      color: @color0;
      .vp-transition(all; 0.3s; ease-in-out);
    }

The goal of these modifications is to make transitioning with a given
CSS code base easier. The new options are available to the command
line via:

    Usage: css2less [options] filename
    -c, --colors                     Automatically extract colors from less
    -m, --mixins                     Automatically extract vendor prefixed mixins
    -h, --help                       Show this message]

The code is portable Ruby 1.8.7 and Ruby 1.9
Commits on Jun 03, 2013
Merge pull request #18 from grundprinzip/master
Automatic Color Extraction and Vendor Prefixed Modules
Showing with 307 additions and 5 deletions.
  1. +28 −1 bin/css2less
  2. +151 −4 lib/css2less.rb
  3. +128 −0 spec/css2less_spec.rb
View
@@ -1,9 +1,36 @@
#!/usr/bin/env ruby
+require 'optparse'
require 'css2less'
+options = {}
+parser = OptionParser.new do |opts|
+ opts.banner = "Usage: css2less [options] filename"
+
+ opts.on("-c", "--colors", "Automatically extract colors from less") do |v|
+ options[:update_colors] = v
+ end
+
+ opts.on("-m", "--mixins", "Automatically extract vendor prefixed mixins") do |v|
+ options[:vendor_mixins] = v
+ end
+
+ opts.on_tail("-h", "--help", "Show this message") do
+ puts opts
+ exit
+ end
+
+end
+
+parser.parse!
+
+if ARGV.empty?
+ puts parser
+ exit
+end
+
css = File.read(ARGV[0])
-converter = Css2Less::Converter.new(css)
+converter = Css2Less::Converter.new(css, options)
converter.process_less
puts converter.get_less
View
@@ -15,18 +15,42 @@
#
# You should have received a copy of the GNU General Public License
# along with Foobar. If not, see <http://www.gnu.org/licenses/>.
+require 'set'
module Css2Less
+ # These are the official colors
+ CSS_COLORS = Set.new %w{aliceblue antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue blueviolet brown burlywood cadetblue chartreuse chocolate coral cornflowerblue cornsilk crimson cyan darkblue darkcyan darkgoldenrod darkgray darkgrey darkgreen darkkhaki darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen darkslateblue darkslategray darkslategrey darkturquoise darkviolet deeppink deepskyblue dimgray dimgrey dodgerblue firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite gold goldenrod gray grey green greenyellow honeydew hotpink indianred indigo ivory khaki lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan lightgoldenrodyellow lightgray lightgrey lightgreen lightpink lightsalmon lightseagreen lightskyblue lightslategray lightslategrey lightsteelblue lightyellow lime limegreen linen magenta maroon mediumaquamarine mediumblue mediumorchid mediumpurple mediumseagreen mediumslateblue mediumspringgreen mediumturquoise mediumvioletred midnightblue mintcream mistyrose moccasin navajowhite navy oldlace olive olivedrab orange orangered orchid palegoldenrod palegreen paleturquoise palevioletred papayawhip peachpuff peru pink plum powderblue purple red rosybrown royalblue saddlebrown salmon sandybrown seagreen seashell sienna silver skyblue slateblue slategray slategrey snow springgreen steelblue tan teal thistle tomato turquoise violet wheat white whitesmoke yellow yellowgreen}
+ VENDOR_PREFIXES_LIST = %w{-moz -o -ms -webkit}
+ VENDOR_PREFIXES = /^(-moz|-o|-ms|-webkit)-/
+
require 'enumerator'
+ # This is the CSS2Less converter class.
class Converter
- def initialize(css=nil)
+
+ # This is the constructor of the class
+ #
+ # The following options are supported:
+ #
+ # * Matching colors in the CSS document and replacing them
+ # update_colors => true
+ def initialize(css=nil, options = {})
if not css.nil?
@css = css
end
+
+ # Option merge, instead of rails reverse_merge
+ @options = {:update_colors => false, :vendor_mixins => false}.merge(options)
+
@tree = {}
@less = ''
+
+ # We want to store all color information
+ @colors = {}
+
+ # Storing all vendor prefix mixins here
+ @vendor_mixins = {}
end
def process_less
@@ -57,9 +81,109 @@ def cleanup
@less = ''
end
+ # Split set of rules into single item
+ def convert_rules(data)
+ data.split(';').map { |s| s.strip }.reject { |s| s.empty? }
+ end
+
+ def color?(value)
+ if CSS_COLORS.include?(value.strip) || /^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/.match(value.strip) != nil ||
+ /(rgba?)\(.*\)/.match(value)
+ true
+ else
+ false
+ end
+ end
+
+ # Check if the global index contains the color and replace the value
+ # accordingly
+ def convert_if_color(color)
+ if color?(color)
+ unless @colors.key?(color.strip)
+ @colors[color.strip] = "@color#{@colors.size}"
+ end
+ @colors[color.strip]
+ else
+ color
+ end
+ end
+
+ # Try to match a color of a set of rules
+ def match_color(style)
+ convert_rules(style).map { |r|
+ (key, value) = r.split(":").map { |e| e.strip }
+ if value.nil?
+ "#{key}"
+ else
+ "#{key}: #{value.split(/\s+/).map { |e| convert_if_color(e) }.join(" ")}"
+ end
+ }.join(";\n") << ";\n"
+ end
+
+ def match_vendor_prefix_mixin(style)
+ normal_rules = {}
+ prefixed_rules = {}
+
+ # First identify all those vendor prefixed rules that are similar
+ convert_rules(style).each { |e|
+ (key, value) = e.split(":").map { |e| e.strip }
+ if value.nil?
+ normal_rules[key] = nil
+ else
+ # If this is a vendor prefixed rule, collect all similar ones in a
+ # single entry
+ if key.match(VENDOR_PREFIXES)
+ rule_key = key.gsub(VENDOR_PREFIXES, "")
+ val = value.split(/\s+/).map { |e| e.strip }
+
+ if prefixed_rules.key?(rule_key) && prefixed_rules[rule_key] != val
+ # Abort, because we have different values for different vendor
+ # prefixed values, this can only mean intended different behavior
+ # for different browsers
+ return style
+ end
+
+ prefixed_rules[rule_key] = val
+ else
+ normal_rules[key] = value
+ end
+ end
+ }
+
+ # Now we have all information to proceed. First, we check if the mixin is
+ # already available globally. If not we announce it
+ prefixed_rules.each { |k,v|
+ unless @vendor_mixins.key?(k)
+ @vendor_mixins[k] = v.size
+ end
+
+ if normal_rules.key?(k)
+ normal_rules.delete(k)
+ normal_rules[".vp-#{k}(#{v.join("; ")})"] = nil
+ end
+ }
+
+ result = normal_rules.to_a.map { |e|
+ val = "#{e[0]}"
+ val << ": #{e[1]}" unless e[1].nil?
+ val}.join(";\n") << ";\n"
+ end
+
+ # This method is called for each selector that we want to add as a rule.
+ # Since we have plain CSS rules here, we should try to bring some order into
+ # the chaos.
def add_rule(tree, selectors, style)
return if style.nil? || style.empty?
+
+ # Stop recursion and add styles
if selectors.empty?
+
+ # Match and replace global colors
+ style = match_color(style) if @options[:update_colors]
+
+ # Match and replace global mixins for vendor specific behavior
+ style = match_vendor_prefix_mixin(style) if @options[:vendor_mixins]
+
(tree[:style] ||= ';') << style
else
first, rest = selectors.first, selectors[1..-1]
@@ -92,18 +216,41 @@ def generate_tree
end
end
+
+ def build_mixin_list(indent)
+ less = ""
+ @vendor_mixins.each { |k,v|
+ args = Array(0..v-1).map { |e| "@p#{e}" }
+ less << ".vp-#{k}(#{args.join("; ")}) {\n"
+ VENDOR_PREFIXES_LIST.each { |vp|
+ less << " " * (indent+4) << "#{vp}-#{k}: #{args.join(" ")};\n"
+ }
+ less << " " * (indent+4) << "#{k}: #{args.join(" ")};\n"
+ less << "}\n"
+ }
+ less << "\n"
+ end
+
def render_less(tree=nil, indent=0)
if tree.nil?
- tree = @tree
+ # This is the initial node, add all global vars / mixins here
+ @colors.each { |k,v|
+ @less << "#{v}: #{k};\n"
+ }
+ @less << "\n" if @colors.size > 0
+
+ @less << build_mixin_list(indent) if @options[:vendor_mixins]
+
+ tree = @tree
end
tree.each do |element, children|
if element == :style
- @less = @less + children.split(';').map { |s| s.strip }.reject { |s| s.empty? }.map { |s| s + ";" }.join("\n") + "\n"
+ @less = @less + convert_rules(children).map { |s| s + ";" }.join("\n") + "\n"
else
@less = @less + ' ' * indent + element + " {\n"
style = children.delete(:style)
if style
- @less = @less + style.split(';').map { |s| s.strip }.reject { |s| s.empty? }.map { |s| ' ' * (indent+4) + s + ";" }.join("\n") + "\n"
+ @less = @less + convert_rules(style).map { |s| ' ' * (indent+4) + s + ";" }.join("\n") + "\n"
end
render_less(children, indent + 4)
@less = @less + ' ' * indent + "}\n"
View
@@ -129,4 +129,132 @@
converter.process_less
converter.get_less.should eq(less)
end
+
+ it "should convert basic css colors into global variables" do
+ css = <<EOF
+#hello {
+ color: blue;
+}
+
+#hello #buddy {
+ background: red;
+ color: #333;
+}
+
+p {
+ color: rgb(1,1,1);
+ border: 1px dotted #e4e9f0;
+}
+EOF
+ less = <<EOF
+@color0: blue;
+@color1: red;
+@color2: #333;
+@color3: rgb(1,1,1);
+@color4: #e4e9f0;
+
+#hello {
+ color: @color0;
+ #buddy {
+ background: @color1;
+ color: @color2;
+ }
+}
+p {
+ color: @color3;
+ border: 1px dotted @color4;
+}
+EOF
+ converter = Css2Less::Converter.new(css, {:update_colors => true})
+ converter.process_less
+ converter.get_less.should eq(less)
+ end
+
+ it "should generate appropriate vendor mixins" do
+ css = <<EOF
+.thumbnail-kenburn img {
+ left:10px;
+ margin-left:-10px;
+ position:relative;
+ -webkit-transition: all 0.8s ease-in-out;
+ -moz-transition: all 0.8s ease-in-out;
+ -o-transition: all 0.8s ease-in-out;
+ -ms-transition: all 0.8s ease-in-out;
+ transition: all 0.8s ease-in-out;
+}
+.thumbnail-kenburn:hover img {
+ -webkit-transform: scale(1.2) rotate(2deg);
+ -moz-transform: scale(1.2) rotate(2deg);
+ -o-transform: scale(1.2) rotate(2deg);
+ -ms-transform: scale(1.2) rotate(2deg);
+ transform: scale(1.2) rotate(2deg);
+}
+
+/*Welcome Block*/
+.service-block .span4 {
+ padding:20px 30px;
+ text-align:center;
+ color: red;
+ margin-bottom:20px;
+ border-radius:2px;
+ -webkit-transition:all 0.3s ease-in-out;
+ -moz-transition:all 0.3s ease-in-out;
+ -o-transition:all 0.3s ease-in-out;
+ transition:all 0.3s ease-in-out;
+}
+EOF
+
+ less = <<EOF
+@color0: red;
+
+.vp-transition(@p0; @p1; @p2) {
+ -moz-transition: @p0 @p1 @p2;
+ -o-transition: @p0 @p1 @p2;
+ -ms-transition: @p0 @p1 @p2;
+ -webkit-transition: @p0 @p1 @p2;
+ transition: @p0 @p1 @p2;
+}
+.vp-transform(@p0; @p1) {
+ -moz-transform: @p0 @p1;
+ -o-transform: @p0 @p1;
+ -ms-transform: @p0 @p1;
+ -webkit-transform: @p0 @p1;
+ transform: @p0 @p1;
+}
+
+.thumbnail-kenburn {
+ img {
+ left: 10px;
+ margin-left: -10px;
+ position: relative;
+ .vp-transition(all;
+ 0.8s;
+ ease-in-out);
+ }
+}
+.thumbnail-kenburn:hover {
+ img {
+ .vp-transform(scale(1.2);
+ rotate(2deg));
+ }
+}
+.service-block {
+ .span4 {
+ padding: 20px 30px;
+ text-align: center;
+ color: @color0;
+ margin-bottom: 20px;
+ border-radius: 2px;
+ .vp-transition(all;
+ 0.3s;
+ ease-in-out);
+ }
+}
+EOF
+
+ converter = Css2Less::Converter.new(css, {:update_colors => true, :vendor_mixins => true})
+ converter.process_less
+ converter.get_less.should eq(less)
+
+ end
end

No commit comments for this range