initial commit
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
__pycache__
|
||||
.project
|
||||
.pydevproject
|
||||
parser.out
|
||||
.settings
|
||||
build
|
||||
20
LICENSE
Normal file
20
LICENSE
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2012 Jóhann T Maríusson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
107
README
Normal file
107
README
Normal file
@@ -0,0 +1,107 @@
|
||||
*
|
||||
* LESSCPY *
|
||||
*
|
||||
python LessCss Compiler.
|
||||
v0.6
|
||||
|
||||
A compiler written in python 3 for the lesscss language. For those of us not willing/able to
|
||||
have node.js installed in our environment. Not all features of lesscss are supported (yet).
|
||||
Some features wil probably never be supported (JavaScript evaluation). This program uses PLY
|
||||
(Python Lex-Yacc) to tokenize/parse the input.
|
||||
|
||||
This is an early version, so you are likly to find bugs.
|
||||
|
||||
For more information on lesscss:
|
||||
* http://lesscss.org/
|
||||
* https://github.com/cloudhead/less.js
|
||||
|
||||
Requirements
|
||||
============
|
||||
|
||||
* python 3+
|
||||
* ply (Python Lex-Yacc) python 3 version
|
||||
|
||||
For more information on ply:
|
||||
* http://www.dabeaz.com/ply/
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
python3 setup.py install
|
||||
|
||||
or simply place the package into your python path.
|
||||
|
||||
Compiler script Usage
|
||||
=====================
|
||||
|
||||
usage: lesscpy [-h] [-I INCLUDE] [-x] [-X] [-m] [-D] [-v] [-o OUT] [-S] [-V]
|
||||
[-L] [-N]
|
||||
target
|
||||
|
||||
positional arguments:
|
||||
target
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-I INCLUDE, --include INCLUDE
|
||||
Included less-files (comma separated)
|
||||
-x, --minify Minify output
|
||||
-X, --xminify Minify output, no end of block newlines
|
||||
-m, --min-ending Add '.min' into output filename. eg, name.min.css
|
||||
-D, --dry-run Dry run, do not write files
|
||||
-v, --verbose Verbose mode
|
||||
-o OUT, --out OUT Output directory
|
||||
|
||||
Debugging:
|
||||
-S, --scopemap Scopemap
|
||||
-V, --debug Debug mode
|
||||
-L, --lex-only Run lexer on target
|
||||
-N, --no-css No css output
|
||||
|
||||
Supported features
|
||||
==================
|
||||
* Variables
|
||||
* String interpolation
|
||||
* Mixins
|
||||
* Parametered mixins (class)
|
||||
* @arguments
|
||||
* Nesting
|
||||
* Escapes ~/e()
|
||||
* Expressions
|
||||
* Color functions:
|
||||
** lighten
|
||||
** darken
|
||||
** saturate
|
||||
** desaturate
|
||||
** spin
|
||||
** hue
|
||||
** saturation
|
||||
** lightness
|
||||
* Other functions:
|
||||
** round
|
||||
** increment
|
||||
** decrement
|
||||
** format '%('
|
||||
** add
|
||||
|
||||
Differences from lessc.js
|
||||
=========================
|
||||
* All MS filters and other strange vendor constructs must be escaped
|
||||
* All colors are auto-formatted to #nnnnnn. eg, #f7e923
|
||||
* Does not preserve css comments
|
||||
|
||||
Not supported (yet)
|
||||
===================
|
||||
* Keyframe blocks
|
||||
* Parametered mixins (id)
|
||||
* mixins (closures)
|
||||
* mixins (Nested)
|
||||
* Pattern-matching
|
||||
* Guard expressions
|
||||
* JavaScript evaluation
|
||||
|
||||
License
|
||||
=======
|
||||
See the LICENSE file
|
||||
|
||||
<jtm@robot.is>
|
||||
19
bin/lesscpy
Executable file
19
bin/lesscpy
Executable file
@@ -0,0 +1,19 @@
|
||||
#! /usr/bin/python3
|
||||
"""
|
||||
CSS/LESSCSS run script
|
||||
|
||||
http://lesscss.org/#docs
|
||||
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
import sys, os
|
||||
|
||||
path = os.path.abspath(sys.argv[0])
|
||||
while os.path.dirname(path) != path:
|
||||
if os.path.exists(os.path.join(path, 'lesscpy', '__init__.py')):
|
||||
sys.path.insert(0, path)
|
||||
break
|
||||
path = os.path.dirname(path)
|
||||
|
||||
from lesscpy.scripts import compiler
|
||||
compiler.run()
|
||||
0
lesscpy/__init__.py
Normal file
0
lesscpy/__init__.py
Normal file
154
lesscpy/less.ast
Normal file
154
lesscpy/less.ast
Normal file
@@ -0,0 +1,154 @@
|
||||
unit : statement_list
|
||||
|
||||
statement_list : statement_list statement
|
||||
| statement
|
||||
|
||||
statement : block_decl
|
||||
| variable_decl
|
||||
| mixin_decl
|
||||
| css_charset css_string ';'
|
||||
| css_namespace css_string ';'
|
||||
| css_namespace css_ident css_string ';'
|
||||
| css_import css_string ';'
|
||||
|
||||
mixin_decl : block_open_mixin declaration_list brace_close
|
||||
|
||||
block_decl : block_open declaration_list brace_close
|
||||
| block_open brace_close
|
||||
|
||||
block_open_mixin : css_class t_popen block_mixin_args t_pclose brace_open
|
||||
| css_class t_popen less_arguments t_pclose brace_open
|
||||
| css_class t_popen t_pclose brace_open
|
||||
|
||||
block_mixin_args : block_mixin_args ',' block_mixin_arg
|
||||
| block_mixin_arg
|
||||
|
||||
block_mixin_arg : less_variable ':' block_mixin_factor
|
||||
| block_mixin_factor
|
||||
| less_variable
|
||||
|
||||
block_mixin_factor : css_number
|
||||
| css_color
|
||||
| css_ident
|
||||
| css_string
|
||||
|
||||
block_open : identifier_list brace_open
|
||||
|
||||
mixin : identifier_list ';'
|
||||
|
||||
identifier_list : identifier_group
|
||||
| identifier_page
|
||||
| css_font_face
|
||||
|
||||
identifier_page : identifier_page dom_filter
|
||||
| css_page
|
||||
|
||||
identifier_group : identifier_group ',' identifier
|
||||
| identifier_group '+' identifier
|
||||
| identifier_group identifier
|
||||
| identifier
|
||||
| css_media
|
||||
|
||||
identifier : css_dom
|
||||
| css_id
|
||||
| css_class
|
||||
| dom_filter
|
||||
| css_color
|
||||
| less_combine
|
||||
| '*'
|
||||
| '>'
|
||||
|
||||
declaration_list : declaration_list declaration
|
||||
| declaration
|
||||
| property_decl
|
||||
| block_decl
|
||||
| variable_decl
|
||||
|
||||
variable_decl : less_variable ':' style_list ';'
|
||||
|
||||
property_decl : identifier_list t_popen argument_list t_pclose ';'
|
||||
| identifier_list t_popen t_pclose ';'
|
||||
| property ':' style_list ';'
|
||||
| property ':' style_list
|
||||
| property ':' ';'
|
||||
| mixin
|
||||
|
||||
property : css_property
|
||||
| css_vendor_property
|
||||
| css_ident
|
||||
|
||||
style_list : style_group
|
||||
| less_arguments
|
||||
|
||||
style_group : style_group ',' style
|
||||
| style_group style
|
||||
| style
|
||||
|
||||
style : expression
|
||||
| css_important
|
||||
| css_string
|
||||
| istring
|
||||
| css_vendor_property
|
||||
| css_property
|
||||
| css_ident
|
||||
| '~' istring
|
||||
| '~' css_string
|
||||
|
||||
dom_filter : css_dom filter_group
|
||||
| css_id filter_group
|
||||
| css_class filter_group
|
||||
| less_combine filter_group
|
||||
|
||||
filter_group : filter filter
|
||||
| filter
|
||||
|
||||
filter : css_filter
|
||||
| ':' css_ident
|
||||
| ':' css_filter
|
||||
| ':' ':' css_ident
|
||||
|
||||
expression : expression '+' expression
|
||||
| expression '-' expression
|
||||
| expression '*' expression
|
||||
| expression '/' expression
|
||||
| '-' t_popen expression t_pclose
|
||||
| t_popen expression t_pclose
|
||||
| factor
|
||||
|
||||
factor : color
|
||||
| number
|
||||
| variable
|
||||
| css_dom
|
||||
| fcall
|
||||
|
||||
fcall : css_ident t_popen argument_list t_pclose
|
||||
| css_property t_popen argument_list t_pclose
|
||||
| css_vendor_property t_popen argument_list t_pclose
|
||||
| less_open_format argument_list t_pclose
|
||||
|
||||
argument_list : argument_list ',' argument
|
||||
| argument_list argument
|
||||
| argument
|
||||
|
||||
argument : expression
|
||||
| css_string
|
||||
| istring
|
||||
| css_ident
|
||||
| css_id
|
||||
| css_uri
|
||||
| '='
|
||||
|
||||
istring : less_string
|
||||
|
||||
variable : '-' variable
|
||||
| t_popen variable t_pclose
|
||||
| less_variable
|
||||
|
||||
color : css_color
|
||||
|
||||
number : css_number
|
||||
| css_number_unit
|
||||
|
||||
brace_open : '{'
|
||||
|
||||
brace_close : '}'
|
||||
0
lesscpy/lessc/__init__.py
Normal file
0
lesscpy/lessc/__init__.py
Normal file
217
lesscpy/lessc/color.py
Normal file
217
lesscpy/lessc/color.py
Normal file
@@ -0,0 +1,217 @@
|
||||
"""
|
||||
LESSCPY Color functions
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
import colorsys
|
||||
|
||||
class LessColor():
|
||||
def process(self, expression):
|
||||
""" Process color expression
|
||||
@param tuple: color expression
|
||||
@return: string
|
||||
"""
|
||||
a, o, b = expression
|
||||
c1 = self.hex_to_rgb(a)
|
||||
c2 = self.hex_to_rgb(b)
|
||||
r = ['#']
|
||||
for i in range(3):
|
||||
v = self.operate(c1[i], c2[i], o)
|
||||
if v > 0xff: v = 0xff
|
||||
if v < 0: v = 0
|
||||
r.append("%02x" % v)
|
||||
return ''.join(r)
|
||||
|
||||
def operate(self, a, b, o):
|
||||
""" Do operation on colors
|
||||
@param string: color
|
||||
@param string: color
|
||||
@param string: operator
|
||||
"""
|
||||
operation = {
|
||||
'+': '__add__',
|
||||
'-': '__sub__',
|
||||
'*': '__mul__',
|
||||
'/': '__truediv__'
|
||||
}.get(o)
|
||||
v = getattr(a, operation)(b)
|
||||
return v
|
||||
|
||||
def rgb(self, r, g, b):
|
||||
""" RGB color function
|
||||
@param str: RED channel
|
||||
@param str: GREEN channel
|
||||
@param str: BLUE channel
|
||||
@return: str
|
||||
"""
|
||||
return self.__str_rgb((int(r), int(g), int(b)))
|
||||
|
||||
def hex_to_rgb(self, hex, *args):
|
||||
""" Convert HEX color code to rgb
|
||||
@param str: hex code
|
||||
@return tuple
|
||||
"""
|
||||
hex = hex.strip()
|
||||
if hex[0] == '#':
|
||||
hex = hex.strip('#').strip(';')
|
||||
if len(hex) == 3:
|
||||
hex = [c * 2 for c in hex]
|
||||
else:
|
||||
hex = [hex[i:i+2] for i in range(0, len(hex), 2)]
|
||||
return tuple(int(c, 16) for c in hex)
|
||||
return [int(hex, 16)] * 3
|
||||
|
||||
def hex_to_hls(self, hex, *args):
|
||||
""" Convert Hex color code to hls
|
||||
@param str: hex code
|
||||
@return tuple
|
||||
"""
|
||||
rgb = self.hex_to_rgb(hex)
|
||||
return colorsys.rgb_to_hls(*[c / 255.0 for c in rgb])
|
||||
|
||||
def hls(self, h, l, s, *args):
|
||||
"""
|
||||
Create HEX color value from hls
|
||||
@param h: hue (0 <= h <= 360)
|
||||
@param l: lightness (0 <= l <= 100)
|
||||
@param s: saturation (0 <= l <= 100)
|
||||
"""
|
||||
if type(l) == str: l = int(l.strip('%'))
|
||||
if type(s) == str: s = int(s.strip('%'))
|
||||
rgb = colorsys.hls_to_rgb(int(h) / 360, l / 100, s / 100)
|
||||
return self.__format_rgb(rgb)
|
||||
|
||||
def hsl(self, h, s, l, *args):
|
||||
"""
|
||||
Wrapper for hls
|
||||
"""
|
||||
return self.hls(h, l, s)
|
||||
|
||||
def hue(self, color, *args):
|
||||
"""
|
||||
Returns the hue channel of color
|
||||
@param str: hex code
|
||||
@return int
|
||||
"""
|
||||
h, l, s = self.hex_to_hls(color)
|
||||
return round(h * 360, 3)
|
||||
|
||||
def saturation(self, color, *args):
|
||||
"""
|
||||
Returns the saturation channel of color
|
||||
@param str: hex code
|
||||
@return int
|
||||
"""
|
||||
h, l, s = self.hex_to_hls(color)
|
||||
return round(s * 100)
|
||||
|
||||
def lightness(self, color, *args):
|
||||
"""
|
||||
Returns the lightness channel of color
|
||||
@param str: hex code
|
||||
@return int
|
||||
"""
|
||||
h, l, s = self.hex_to_hls(color)
|
||||
return round(l * 100)
|
||||
|
||||
def saturate(self, color, p, *args):
|
||||
"""
|
||||
"""
|
||||
if type(p) == str: p = int(p.strip('%'))
|
||||
h, l, s = self.hex_to_hls(color)
|
||||
rgb = colorsys.hls_to_rgb(h, l, s + (p / 100))
|
||||
return self.__format_rgb(rgb)
|
||||
|
||||
def desaturate(self, color, p, *args):
|
||||
"""
|
||||
"""
|
||||
if type(p) == str: p = int(p.strip('%'))
|
||||
h, l, s = self.hex_to_hls(color)
|
||||
rgb = colorsys.hls_to_rgb(h, l, s - (p / 100))
|
||||
return self.__format_rgb(rgb)
|
||||
|
||||
def lighten(self, color, p, *args):
|
||||
"""
|
||||
"""
|
||||
if type(p) == str: p = int(p.strip('%'))
|
||||
h, l, s = self.hex_to_hls(color)
|
||||
rgb = colorsys.hls_to_rgb(h, l + (p / 100), s)
|
||||
return self.__format_rgb(rgb)
|
||||
|
||||
def darken(self, color, p, *args):
|
||||
"""
|
||||
"""
|
||||
if type(p) == str: p = int(p.strip('%'))
|
||||
h, l, s = self.hex_to_hls(color)
|
||||
rgb = colorsys.hls_to_rgb(h, l - (p / 100), s)
|
||||
return self.__format_rgb(rgb)
|
||||
|
||||
def greyscale(self, color, *args):
|
||||
"""
|
||||
"""
|
||||
return self.desaturate(color, 100)
|
||||
|
||||
def fadein(self, color, pc, *args):
|
||||
"""
|
||||
"""
|
||||
pass
|
||||
|
||||
def fadeout(self, color, pc, *args):
|
||||
"""
|
||||
"""
|
||||
pass
|
||||
|
||||
def fade(self):
|
||||
"""
|
||||
"""
|
||||
pass
|
||||
|
||||
def spin(self, color, deg):
|
||||
"""
|
||||
"""
|
||||
if type(deg) == str: deg = int(deg.strip('%'))
|
||||
h, l, s = self.hex_to_hls(color)
|
||||
h = ((h * 360) + deg) % 360
|
||||
h = 360 + h if h < 0 else h
|
||||
rgb = colorsys.hls_to_rgb(h / 360, l, s)
|
||||
return self.__format_rgb(rgb)
|
||||
|
||||
def mix(self, color1, color2, weight):
|
||||
"""
|
||||
"""
|
||||
pass
|
||||
|
||||
def format(self, color):
|
||||
"""
|
||||
Format CSS Hex color code.
|
||||
uppercase becomes lowercase, 3 digit codes expand to 6 digit.
|
||||
@param string: color
|
||||
"""
|
||||
if type(color) == str and color[0] == '#':
|
||||
color = color.lower().strip().strip('#').strip(';')
|
||||
if len(color) == 3:
|
||||
color = ''.join([c * 2 for c in color])
|
||||
return '#%s' % color
|
||||
raise ValueError('Cannot format non-color')
|
||||
|
||||
def __format_rgb(self, rgb):
|
||||
""" Format RGB tuple to string
|
||||
@param tuple: RGB tuple
|
||||
@return: string
|
||||
"""
|
||||
color = (round(c * 255) for c in rgb)
|
||||
return self.__str_rgb(color)
|
||||
|
||||
def __str_rgb(self, rgb):
|
||||
"""
|
||||
"""
|
||||
return '#%s' % ''.join(["%02x" % v for v in
|
||||
[0xff
|
||||
if h > 0xff else
|
||||
0 if h < 0 else h
|
||||
for h in rgb]
|
||||
])
|
||||
|
||||
|
||||
87
lesscpy/lessc/formatter.py
Normal file
87
lesscpy/lessc/formatter.py
Normal file
@@ -0,0 +1,87 @@
|
||||
"""
|
||||
CSS Formatter class.
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
class Formatter(object):
|
||||
def format(self, parse, minify=False, xminify=False):
|
||||
""" Format css output from parser
|
||||
@param Parse-result object: Parse-result object
|
||||
@param bool: Minify flag
|
||||
@param bool: Skip end of block newlines
|
||||
@return: string
|
||||
"""
|
||||
eb = '\n'
|
||||
if xminify:
|
||||
eb = ''
|
||||
minify = True
|
||||
self.items = {}
|
||||
if minify:
|
||||
self.items.update({
|
||||
'nl': '',
|
||||
'tab': '',
|
||||
'ws': '',
|
||||
'endblock': eb
|
||||
})
|
||||
else:
|
||||
self.items.update({
|
||||
'nl': '\n',
|
||||
'tab': '\t',
|
||||
'ws': ' ',
|
||||
'endblock': eb
|
||||
})
|
||||
self.out = []
|
||||
if parse.result:
|
||||
for u in parse.result:
|
||||
self.out.extend(self.fprint(u))
|
||||
return ''.join(self.out)
|
||||
|
||||
def fprint(self, node):
|
||||
""" Format node.
|
||||
@param Node object: Node object
|
||||
"""
|
||||
out = []
|
||||
if not node: return out
|
||||
if 'proplist' in node.parsed:
|
||||
node.parsed['proplist'] = ''.join([self.sprintf(p.format, p.parsed)
|
||||
for p in node.parsed['proplist']
|
||||
if p])
|
||||
if node.parsed['proplist']:
|
||||
out.append(self.sprintf(node.format, node.parsed))
|
||||
else:
|
||||
out.append(self.sprintf(node.format, node.parsed))
|
||||
if 'inner' in node.parsed:
|
||||
if node._blocktype:
|
||||
out.append(self.fblockinner(node))
|
||||
else:
|
||||
for iu in node.parsed['inner']:
|
||||
out.extend(self.fprint(iu))
|
||||
return out
|
||||
|
||||
def fblockinner(self, node):
|
||||
""" Format inner block type
|
||||
@param Node: node
|
||||
@return: str
|
||||
"""
|
||||
sub = []
|
||||
for iu in node.parsed['inner']:
|
||||
sub.extend(self.fprint(iu))
|
||||
sub = ''.join(sub)
|
||||
if sub:
|
||||
if self.items['tab']:
|
||||
sub = '\t'+''.join(sub)
|
||||
sub = sub.replace('\n', '\n\t').rstrip('\t')
|
||||
node.parsed['proplist'] = sub
|
||||
return self.sprintf(node.format, node.parsed)
|
||||
return ''
|
||||
|
||||
def sprintf(self, frm, items):
|
||||
""" Perform format action
|
||||
@param string: Format string
|
||||
@param dict: format items
|
||||
@return: string
|
||||
"""
|
||||
items.update(self.items)
|
||||
return frm % items
|
||||
202
lesscpy/lessc/lexer.py
Normal file
202
lesscpy/lessc/lexer.py
Normal file
@@ -0,0 +1,202 @@
|
||||
"""
|
||||
Lexer for LESSCSS.
|
||||
|
||||
http://www.dabeaz.com/ply/ply.html
|
||||
http://www.w3.org/TR/CSS21/grammar.html#scanner
|
||||
http://lesscss.org/#docs
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
import re
|
||||
import ply.lex as lex
|
||||
|
||||
from lesscpy.lib import dom
|
||||
from lesscpy.lib import css
|
||||
|
||||
class LessLexer:
|
||||
states = (
|
||||
('parn', 'inclusive'),
|
||||
)
|
||||
literals = ',{}>=%!/*-+:;()~';
|
||||
tokens = [
|
||||
'css_ident',
|
||||
'css_dom',
|
||||
'css_class',
|
||||
'css_id',
|
||||
'css_property',
|
||||
'css_vendor_property',
|
||||
'css_comment',
|
||||
'css_string',
|
||||
'css_color',
|
||||
'css_filter',
|
||||
'css_number',
|
||||
'css_number_unit',
|
||||
'css_important',
|
||||
'css_vendor_hack',
|
||||
'css_uri',
|
||||
|
||||
'less_variable',
|
||||
'less_comment',
|
||||
'less_string',
|
||||
'less_open_format',
|
||||
'less_combine',
|
||||
|
||||
't_ws',
|
||||
't_popen',
|
||||
't_pclose',
|
||||
]
|
||||
reserved = {
|
||||
'@media' : 'css_media',
|
||||
'@page': 'css_page',
|
||||
'@import' : 'css_import',
|
||||
'@charset' : 'css_charset',
|
||||
'@font-face' : 'css_font_face',
|
||||
'@namespace' : 'css_namespace',
|
||||
'@keyframes' : 'css_keyframes',
|
||||
'@-moz-keyframes' : 'css_keyframes',
|
||||
'@-webkit-keyframes' : 'css_keyframes',
|
||||
|
||||
'@arguments': 'less_arguments',
|
||||
}
|
||||
tokens = tokens + list(set(reserved.values()))
|
||||
|
||||
def __init__(self):
|
||||
self.build(reflags=re.UNICODE|re.IGNORECASE)
|
||||
|
||||
def t_css_filter(self, t):
|
||||
(r'\[[^\]]*\]'
|
||||
'|(not|lang|nth-[a-z\-]+)\(.+\)'
|
||||
'|and[ \t]\(.+\)')
|
||||
return t
|
||||
|
||||
def t_css_ident(self, t):
|
||||
(r'([\-\.\#]?'
|
||||
'|@[@\-]?)'
|
||||
'([_a-z]'
|
||||
'|[\200-\377]'
|
||||
'|\\\[0-9a-f]{1,6}([ \t\f])?'
|
||||
'|\\\[^\s\r\n0-9a-f])'
|
||||
'([_a-z0-9\-]|[\200-\377]'
|
||||
'|\\\[0-9a-f]{1,6}([ \t\f])?'
|
||||
'|\\\[^\s\r\n0-9a-f])*[ \t]?')
|
||||
v = t.value.strip()
|
||||
c = v[0]
|
||||
if c == '.':
|
||||
t.type = 'css_class'
|
||||
elif c == '#':
|
||||
t.type = 'css_id'
|
||||
try:
|
||||
int(v[1:], 16)
|
||||
t.type = 'css_color'
|
||||
except ValueError:
|
||||
pass
|
||||
elif v in css.propertys:
|
||||
t.type = 'css_property'
|
||||
t.value = t.value.strip()
|
||||
elif v.lower() in dom.html:
|
||||
t.type = 'css_dom'
|
||||
t.value = t.value
|
||||
elif c == '@':
|
||||
if v in LessLexer.reserved:
|
||||
t.type = LessLexer.reserved[v]
|
||||
else:
|
||||
t.type = 'less_variable'
|
||||
elif c == '-':
|
||||
t.type = 'css_vendor_property'
|
||||
return t
|
||||
|
||||
def t_css_color(self, t):
|
||||
r'\#[0-9]([0-9a-f]{5}|[0-9a-f]{2})'
|
||||
return t
|
||||
|
||||
def t_parn_css_uri(self, t):
|
||||
(r'data:[^\)]+'
|
||||
'|(([a-z]+://)?'
|
||||
'('
|
||||
'([\.\w:]+[\\/])+'
|
||||
'|([a-z][\w\.\-]+(\.[a-z0-9]+))'
|
||||
'(\#[a-z]+)?)'
|
||||
')+')
|
||||
return t
|
||||
|
||||
def t_parn_css_ident(self, t):
|
||||
(r'(([_a-z]'
|
||||
'|[\200-\377]'
|
||||
'|\\\[0-9a-f]{1,6}([ \t\f])?'
|
||||
'|\\\[^\r\n\s0-9a-f])'
|
||||
'([_a-z0-9\-]|[\200-\377]'
|
||||
'|\\\[0-9a-f]{1,6}([ \t\f])?'
|
||||
'|\\\[^\r\n\s0-9a-f])*)')
|
||||
return t
|
||||
|
||||
def t_css_number(self, t):
|
||||
r'-?(\d*\.\d+|\d+)(s|%|in|ex|[ecm]m|p[txc]|deg|g?rad|ms?|k?hz)?'
|
||||
return t
|
||||
|
||||
def t_newline(self, t):
|
||||
r'\n+'
|
||||
t.lexer.lineno += len(t.value)
|
||||
|
||||
def t_css_comment(self, t):
|
||||
r'(/\*(.|\n)*?\*/)'
|
||||
t.lexer.lineno += t.value.count('\n')
|
||||
pass
|
||||
|
||||
def t_less_comment(self, t):
|
||||
r'//.*'
|
||||
pass
|
||||
|
||||
def t_css_important(self, t):
|
||||
r'!\s*important'
|
||||
t.value = '!important'
|
||||
return t
|
||||
|
||||
def t_t_ws(self, t):
|
||||
r'[ \t]+'
|
||||
pass
|
||||
|
||||
def t_t_popen(self, t):
|
||||
r'\('
|
||||
t.lexer.push_state('parn')
|
||||
return t
|
||||
|
||||
def t_less_open_format(self, t):
|
||||
r'%\('
|
||||
t.lexer.push_state('parn')
|
||||
return t
|
||||
|
||||
def t_t_pclose(self, t):
|
||||
r'\)'
|
||||
t.lexer.pop_state()
|
||||
return t
|
||||
|
||||
def t_less_combine(self, t):
|
||||
r'&[ \t]?'
|
||||
return t
|
||||
|
||||
t_less_string = (r'"([^"@]*@\{[^"\}]+\}[^"]*)+"'
|
||||
'|\'([^\'@]*@\{[^\'\}]+\}[^\']*)+\'')
|
||||
t_css_string = r'"[^"]*"|\'[^\']*\''
|
||||
|
||||
# Error handling rule
|
||||
def t_error(self, t):
|
||||
raise SyntaxError("Illegal character '%s' line %d" % (t.value[0], t.lexer.lineno))
|
||||
t.lexer.skip(1)
|
||||
|
||||
# Build the lexer
|
||||
def build(self, **kwargs):
|
||||
self.lexer = lex.lex(module=self, **kwargs)
|
||||
|
||||
def file(self, filename):
|
||||
with open(filename) as f:
|
||||
self.lexer.input(f.read())
|
||||
return self.lexer
|
||||
|
||||
def input(self, filename):
|
||||
with open(filename) as f:
|
||||
self.lexer.input(f.read())
|
||||
|
||||
def token(self):
|
||||
return self.lexer.token()
|
||||
691
lesscpy/lessc/parser.py
Normal file
691
lesscpy/lessc/parser.py
Normal file
@@ -0,0 +1,691 @@
|
||||
"""
|
||||
LESSCSS Parser.
|
||||
|
||||
http://www.dabeaz.com/ply/ply.html
|
||||
http://www.w3.org/TR/CSS21/grammar.html#scanner
|
||||
http://lesscss.org/#docs
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
import os
|
||||
import ply.yacc
|
||||
from . import lexer
|
||||
from . import utility
|
||||
from .color import LessColor
|
||||
from lesscpy.plib import *
|
||||
|
||||
class LessParser(object):
|
||||
precedence = (
|
||||
('left', '+', '-'),
|
||||
('left', '*', '/'),
|
||||
)
|
||||
def __init__(self,
|
||||
lex_optimize=True,
|
||||
yacc_optimize=True,
|
||||
yacctab='yacctab',
|
||||
yacc_debug=False,
|
||||
scope=None,
|
||||
outputdir='/tmp',
|
||||
importlvl=0):
|
||||
""" Parser object
|
||||
@param bool: Optimized lexer
|
||||
@param bool: Optimized parser
|
||||
@param string: Yacc tables file
|
||||
@param bool: Debug mode
|
||||
@param dict: Included scope
|
||||
"""
|
||||
self.importlvl = importlvl
|
||||
self.lex = lexer.LessLexer()
|
||||
if not yacctab:
|
||||
yacctab = 'yacctab'
|
||||
|
||||
self.ignored = ('t_ws', 'css_comment', 'less_comment',
|
||||
'css_vendor_hack', 'css_keyframes')
|
||||
|
||||
self.tokens = [t for t in self.lex.tokens
|
||||
if t not in self.ignored]
|
||||
self.parser = ply.yacc.yacc(
|
||||
module=self,
|
||||
start='unit',
|
||||
debug=yacc_debug,
|
||||
optimize=yacc_optimize,
|
||||
tabmodule=yacctab,
|
||||
outputdir=outputdir
|
||||
)
|
||||
self.scope = scope if scope else []
|
||||
self.stash = {}
|
||||
self.result = None
|
||||
self.target = None
|
||||
|
||||
def parse(self, filename='', debuglevel=0):
|
||||
""" Parse file.
|
||||
@param string: Filename
|
||||
@param int: Debuglevel
|
||||
"""
|
||||
self._create_scope()
|
||||
self.target = filename
|
||||
self.result = self.parser.parse(filename, lexer=self.lex, debug=debuglevel)
|
||||
|
||||
def scopemap(self):
|
||||
""" Output scopemap.
|
||||
"""
|
||||
if self.result:
|
||||
utility.print_recursive(self.result)
|
||||
|
||||
def p_unit(self, p):
|
||||
""" unit : statement_list
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_statement_list_aux(self, p):
|
||||
""" statement_list : statement_list statement
|
||||
"""
|
||||
p[1].extend([p[2]])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_statement_list(self, p):
|
||||
""" statement_list : statement
|
||||
"""
|
||||
p[0] = [p[1]]
|
||||
|
||||
def p_statement(self, p):
|
||||
""" statement : block_decl
|
||||
| variable_decl
|
||||
| mixin_decl
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_statement_aux(self, p):
|
||||
""" statement : css_charset css_string ';'
|
||||
| css_namespace css_string ';'
|
||||
"""
|
||||
p[0] = Statement(p)
|
||||
p[0].parse(None)
|
||||
|
||||
def p_statement_namespace(self, p):
|
||||
""" statement : css_namespace css_ident css_string ';'
|
||||
"""
|
||||
p[0] = Statement(p)
|
||||
p[0].parse(None)
|
||||
|
||||
def p_statement_import(self, p):
|
||||
""" statement : css_import css_string ';'
|
||||
"""
|
||||
if self.importlvl > 8:
|
||||
raise ImportError('Recrusive import level too deep > 8 (circular import ?)')
|
||||
ipath = utility.destring(p[2])
|
||||
fn, fe = os.path.splitext(ipath)
|
||||
if not fe or fe.lower() == '.less':
|
||||
try:
|
||||
cpath = os.path.dirname(os.path.abspath(self.target))
|
||||
if not fe: ipath += '.less'
|
||||
filename = "%s%s%s" % (cpath, os.sep, ipath)
|
||||
if os.path.exists(filename):
|
||||
recurse = LessParser(importlvl=self.importlvl+1)
|
||||
recurse.parse(filename=filename, debuglevel=0)
|
||||
self.update_scope(recurse.scope)
|
||||
else:
|
||||
err = "Cannot import '%s', file not found" % filename
|
||||
self.handle_error(err, p, 'W')
|
||||
p[0] = None
|
||||
except ImportError as e:
|
||||
self.handle_error(e, p)
|
||||
else:
|
||||
p[0] = Statement(p)
|
||||
p[0].parse(None)
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
def p_mixin_decl(self, p):
|
||||
""" mixin_decl : block_open_mixin declaration_list brace_close
|
||||
"""
|
||||
try:
|
||||
mixin = Mixin(p)
|
||||
mixin.parse(self.scope, self.stash)
|
||||
self.scope[-1]['__mixins__'][mixin.name.strip()] = mixin
|
||||
except SyntaxError as e:
|
||||
self.handle_error(e, p)
|
||||
p[0] = None
|
||||
|
||||
def p_block_decl(self, p):
|
||||
""" block_decl : block_open declaration_list brace_close
|
||||
"""
|
||||
try:
|
||||
block = Block(p)
|
||||
block.parse(self.scope)
|
||||
self.scope[-1]['__blocks__'].append(block)
|
||||
p[0] = block
|
||||
except SyntaxError as e:
|
||||
self.handle_error(e, p)
|
||||
p[0] = None
|
||||
|
||||
def p_block_empty(self, p):
|
||||
""" block_decl : block_open brace_close
|
||||
"""
|
||||
p[0] = None
|
||||
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
|
||||
def p_block_open_mixin(self, p):
|
||||
""" block_open_mixin : css_class t_popen block_mixin_args t_pclose brace_open
|
||||
"""
|
||||
self.scope[-1]['current'] = '__mixin__'
|
||||
p[0] = list(p)[1:5]
|
||||
|
||||
def p_block_open_mixin_aux(self, p):
|
||||
""" block_open_mixin : css_class t_popen less_arguments t_pclose brace_open
|
||||
"""
|
||||
self.scope[-1]['current'] = '__mixin__'
|
||||
p[0] = list(p)[1:5]
|
||||
|
||||
def p_block_open_mixin_empty(self, p):
|
||||
""" block_open_mixin : css_class t_popen t_pclose brace_open
|
||||
"""
|
||||
self.scope[-1]['current'] = '__mixin__'
|
||||
p[0] = [p[1]]
|
||||
|
||||
def p_block_mixin_args_aux(self, p):
|
||||
""" block_mixin_args : block_mixin_args ',' block_mixin_arg
|
||||
"""
|
||||
p[1].extend([p[2], p[3]])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_block_mixin_args(self, p):
|
||||
""" block_mixin_args : block_mixin_arg
|
||||
"""
|
||||
p[0] = [p[1]]
|
||||
|
||||
def p_block_mixin_arg_def(self, p):
|
||||
""" block_mixin_arg : less_variable ':' block_mixin_factor
|
||||
"""
|
||||
p[0] = list(p)[1:4]
|
||||
|
||||
def p_block_mixin_arg(self, p):
|
||||
""" block_mixin_arg : block_mixin_factor
|
||||
| less_variable
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_block_mixin_factor(self, p):
|
||||
""" block_mixin_factor : css_number
|
||||
| css_color
|
||||
| css_ident
|
||||
| css_string
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
|
||||
def p_block_open(self, p):
|
||||
""" block_open : identifier_list brace_open
|
||||
"""
|
||||
name = ["%s " % t
|
||||
if t in '>+'
|
||||
else t
|
||||
for t in utility.flatten(p[1])]
|
||||
self.scope[-1]['current'] = ''.join(name).strip()
|
||||
p[0] = p[1]
|
||||
|
||||
def p_identifier_list_mixin(self, p):
|
||||
""" mixin : identifier_list ';'
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_identifier_list(self, p):
|
||||
""" identifier_list : identifier_group
|
||||
| identifier_page
|
||||
| css_font_face
|
||||
"""
|
||||
if type(p[1]) is list:
|
||||
p[0] = p[1]
|
||||
else:
|
||||
p[0] = [p[1]]
|
||||
|
||||
def p_identifier_page_aux(self, p):
|
||||
""" identifier_page : identifier_page dom_filter
|
||||
"""
|
||||
p[1].extend(p[2])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_identifier_page(self, p):
|
||||
""" identifier_page : css_page
|
||||
"""
|
||||
p[0] = [p[1]]
|
||||
|
||||
def p_identifier_group_op(self, p):
|
||||
""" identifier_group : identifier_group ',' identifier
|
||||
| identifier_group '+' identifier
|
||||
"""
|
||||
p[1].extend([p[2], p[3]])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_identifier_group_aux(self, p):
|
||||
""" identifier_group : identifier_group identifier
|
||||
"""
|
||||
p[1].extend([p[2]])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_identifier_group(self, p):
|
||||
""" identifier_group : identifier
|
||||
"""
|
||||
p[0] = [p[1]]
|
||||
|
||||
def p_identifier_group_media(self, p):
|
||||
""" identifier_group : css_media
|
||||
"""
|
||||
p[0] = [p[1]]
|
||||
|
||||
def p_identifier(self, p):
|
||||
""" identifier : css_dom
|
||||
| css_id
|
||||
| css_class
|
||||
| dom_filter
|
||||
| css_color
|
||||
| less_combine
|
||||
| '*'
|
||||
| '>'
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
|
||||
def p_declaration_list_aux(self, p):
|
||||
""" declaration_list : declaration_list declaration
|
||||
"""
|
||||
p[1].extend([p[2]])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_declaration_list(self, p):
|
||||
""" declaration_list : declaration
|
||||
"""
|
||||
p[0] = [p[1]]
|
||||
|
||||
def p_declaration(self, p):
|
||||
""" declaration : property_decl
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_declaration_block(self, p):
|
||||
""" declaration : block_decl
|
||||
| variable_decl
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_variable_decl(self, p):
|
||||
""" variable_decl : less_variable ':' style_list ';'
|
||||
"""
|
||||
current = self.scope[-1]
|
||||
try:
|
||||
v = Variable(p)
|
||||
v.parse(self.scope)
|
||||
if 'current' in current and current['current'] == '__mixin__':
|
||||
self.stash[v.name()] = v
|
||||
else:
|
||||
current[v.name()] = v
|
||||
except SyntaxError as e:
|
||||
print(e)
|
||||
p[0] = None
|
||||
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
def p_property_mixin_call(self, p):
|
||||
""" property_decl : identifier_list t_popen argument_list t_pclose ';'
|
||||
"""
|
||||
n = p[1][0]
|
||||
if n in self.scope[0]['__mixins__']:
|
||||
if self.scope[-1]['current'] != '__mixin__':
|
||||
try:
|
||||
p[0] = self.scope[0]['__mixins__'][n].call(p[3], self.scope)
|
||||
except SyntaxError as e:
|
||||
self.handle_error(e, p)
|
||||
p[0] = None
|
||||
else:
|
||||
p[0] = self.scope[0]['__mixins__'][n]
|
||||
else:
|
||||
self.handle_error('Mixin not found in scope: ´%s´' % n, p)
|
||||
p[0] = None
|
||||
|
||||
|
||||
def p_property_mixin_call_empty(self, p):
|
||||
""" property_decl : identifier_list t_popen t_pclose ';'
|
||||
"""
|
||||
n = p[1][0]
|
||||
if n in self.scope[0]['__mixins__']:
|
||||
try:
|
||||
p[0] = self.scope[0]['__mixins__'][n].call(None)
|
||||
except SyntaxError as e:
|
||||
self.handle_error(e, p)
|
||||
p[0] = None
|
||||
else:
|
||||
p[0] = None
|
||||
|
||||
def p_property_mixin(self, p):
|
||||
""" property_decl : mixin
|
||||
"""
|
||||
m = ''.join([u.strip() for u in p[1]])
|
||||
l = utility.block_search(m, self.scope)
|
||||
if l:
|
||||
p[0] = [b.parsed['proplist'] for b in l]
|
||||
elif m in self.scope[0]['__mixins__']:
|
||||
try:
|
||||
p[0] = self.scope[0]['__mixins__'][m].call(None)
|
||||
except SyntaxError as e:
|
||||
self.handle_error(e, p)
|
||||
p[0] = None
|
||||
else:
|
||||
p[0] = []
|
||||
|
||||
|
||||
def p_property_decl(self, p):
|
||||
""" property_decl : property ':' style_list ';'
|
||||
| property ':' style_list
|
||||
"""
|
||||
p[0] = Property(p)
|
||||
if self.scope[-1]['current'] != '__mixin__':
|
||||
try:
|
||||
p[0].parse(self.scope)
|
||||
except SyntaxError as e:
|
||||
self.handle_error(e, p)
|
||||
p[0] = None
|
||||
|
||||
def p_prop_decl_bad(self, p):
|
||||
""" property_decl : property ':' ';'
|
||||
"""
|
||||
p[0] = None
|
||||
|
||||
def p_property(self, p):
|
||||
""" property : css_property
|
||||
| css_vendor_property
|
||||
| css_ident
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
def p_style_list(self, p):
|
||||
""" style_list : style_group
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_less_style_list(self, p):
|
||||
""" style_list : less_arguments
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_style_group_sep(self, p):
|
||||
""" style_group : style_group ',' style
|
||||
"""
|
||||
p[1].extend([p[2], p[3]])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_style_group_aux(self, p):
|
||||
""" style_group : style_group style
|
||||
"""
|
||||
p[1].extend([p[2]])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_style_group(self, p):
|
||||
""" style_group : style
|
||||
"""
|
||||
p[0] = [p[1]]
|
||||
|
||||
def p_style(self, p):
|
||||
""" style : expression
|
||||
| css_important
|
||||
| css_string
|
||||
| istring
|
||||
| css_vendor_property
|
||||
| css_property
|
||||
| css_ident
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_style_escape(self, p):
|
||||
""" style : '~' istring
|
||||
| '~' css_string
|
||||
"""
|
||||
try:
|
||||
p[0] = Call(p)#.parse(self.scope)
|
||||
except SyntaxError as e:
|
||||
self.handle_error(e, p)
|
||||
p[0] = None
|
||||
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
|
||||
def p_dom_filter(self, p):
|
||||
""" dom_filter : css_dom filter_group
|
||||
| css_id filter_group
|
||||
| css_class filter_group
|
||||
| less_combine filter_group
|
||||
"""
|
||||
p[0] = [p[1], p[2]]
|
||||
|
||||
def p_filter_group_aux(self, p):
|
||||
""" filter_group : filter filter
|
||||
"""
|
||||
p[1].extend([p[2]])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_filter_group(self, p):
|
||||
""" filter_group : filter
|
||||
"""
|
||||
p[0] = [p[1]]
|
||||
|
||||
def p_filter(self, p):
|
||||
""" filter : css_filter
|
||||
| ':' css_ident
|
||||
| ':' css_filter
|
||||
| ':' ':' css_ident
|
||||
"""
|
||||
p[0] = list(p)[1:]
|
||||
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
def p_expression_aux(self, p):
|
||||
'''expression : expression '+' expression
|
||||
| expression '-' expression
|
||||
| expression '*' expression
|
||||
| expression '/' expression
|
||||
'''
|
||||
try:
|
||||
p[0] = Expression(p)
|
||||
except SyntaxError as e:
|
||||
print(e)
|
||||
p[0] = None
|
||||
|
||||
def p_expression_p_neg(self, p):
|
||||
""" expression : '-' t_popen expression t_pclose
|
||||
"""
|
||||
p[0] = [p[1], p[3]]
|
||||
|
||||
def p_expression_p(self, p):
|
||||
""" expression : t_popen expression t_pclose
|
||||
"""
|
||||
p[0] = p[2]
|
||||
|
||||
def p_expression(self, p):
|
||||
""" expression : factor
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_factor(self, p):
|
||||
"""factor : color
|
||||
| number
|
||||
| variable
|
||||
| css_dom
|
||||
| fcall
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
|
||||
def p_fcall(self, p):
|
||||
""" fcall : css_ident t_popen argument_list t_pclose
|
||||
| css_property t_popen argument_list t_pclose
|
||||
| css_vendor_property t_popen argument_list t_pclose
|
||||
"""
|
||||
p[0] = Call(p)
|
||||
if self.scope[-1]['current'] != '__mixin__':
|
||||
try:
|
||||
p[0] = p[0].parse(self.scope)
|
||||
except SyntaxError as e:
|
||||
self.handle_error(e, p)
|
||||
p[0] = None
|
||||
|
||||
def p_fcall_format(self, p):
|
||||
""" fcall : less_open_format argument_list t_pclose
|
||||
"""
|
||||
try:
|
||||
p[0] = Call(p).parse(self.scope)
|
||||
except SyntaxError as e:
|
||||
self.handle_error(e, p)
|
||||
p[0] = None
|
||||
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
|
||||
def p_argument_list_aux_1(self, p):
|
||||
""" argument_list : argument_list ',' argument
|
||||
"""
|
||||
p[1].extend([p[2], p[3]])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_argument_list_aux(self, p):
|
||||
""" argument_list : argument_list argument
|
||||
"""
|
||||
p[1].extend([p[2]])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_argument_list(self, p):
|
||||
""" argument_list : argument
|
||||
"""
|
||||
p[0] = [p[1]]
|
||||
|
||||
def p_argument(self, p):
|
||||
""" argument : expression
|
||||
| css_string
|
||||
| istring
|
||||
| css_ident
|
||||
| css_id
|
||||
| css_uri
|
||||
| '='
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
def p_interpolated_str(self, p):
|
||||
""" istring : less_string
|
||||
"""
|
||||
try:
|
||||
p[0] = String(p)#.parse(self.scope)
|
||||
except SyntaxError as e:
|
||||
self.handle_error(e, p, 'W')
|
||||
p[0] = p[1]
|
||||
|
||||
def p_variable_neg(self, p):
|
||||
""" variable : '-' variable
|
||||
"""
|
||||
p[0] = '-' + p[2]
|
||||
|
||||
def p_variable_strange(self, p):
|
||||
""" variable : t_popen variable t_pclose
|
||||
"""
|
||||
p[0] = p[2]
|
||||
|
||||
def p_variable(self, p):
|
||||
""" variable : less_variable
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
|
||||
def p_color(self, p):
|
||||
""" color : css_color
|
||||
"""
|
||||
p[0] = LessColor().format(p[1])
|
||||
|
||||
def p_number(self, p):
|
||||
""" number : css_number
|
||||
| css_number_unit
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
|
||||
def p_scope_open(self, p):
|
||||
""" brace_open : '{'
|
||||
"""
|
||||
self._create_scope()
|
||||
p[0] = p[1]
|
||||
|
||||
def p_scope_close(self, p):
|
||||
""" brace_close : '}'
|
||||
"""
|
||||
self.scope.pop()
|
||||
p[0] = p[1]
|
||||
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
|
||||
def _create_scope(self):
|
||||
""" Create a scope.
|
||||
"""
|
||||
self.scope.append({'__blocks__': [], '__mixins__': {}})
|
||||
|
||||
def update_scope(self, scope):
|
||||
"""
|
||||
"""
|
||||
blocks = [b for b in self.scope[0]['__blocks__']]
|
||||
blocks.extend([b for b in scope[0]['__blocks__']])
|
||||
self.scope[0].update(scope[0])
|
||||
self.scope[0]['__blocks__'] = blocks
|
||||
|
||||
def p_error(self, t):
|
||||
""" Internal error handler
|
||||
@param Lex token: Error token
|
||||
"""
|
||||
if t: print("E: line: %d, Syntax Error, token: `%s`, `%s`"
|
||||
% (t.lineno, t.type, t.value))
|
||||
while True:
|
||||
t = self.lex.token()
|
||||
if not t or t.value == '}':
|
||||
if len(self.scope) > 1:
|
||||
self.scope.pop()
|
||||
break
|
||||
self.parser.restart()
|
||||
return t
|
||||
|
||||
def handle_error(self, e, p, t='E'):
|
||||
""" Custom error handler
|
||||
@param Exception: Exception
|
||||
@param Parser token: Parser token
|
||||
@param string: Error level
|
||||
"""
|
||||
l = [n for n in [p.lineno(i) for i in range(len(p))] if n]
|
||||
l = l[0] if l else -1
|
||||
print("%s: line: %d: " % (t, l), end='')
|
||||
print(e)
|
||||
|
||||
154
lesscpy/lessc/utility.py
Normal file
154
lesscpy/lessc/utility.py
Normal file
@@ -0,0 +1,154 @@
|
||||
"""
|
||||
Utility functions
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
import collections
|
||||
|
||||
def flatten(l):
|
||||
"""
|
||||
Flatten list.
|
||||
@param l: list
|
||||
@return: generator
|
||||
"""
|
||||
for el in l:
|
||||
if isinstance(el, collections.Iterable) and not isinstance(el, str):
|
||||
for sub in flatten(el):
|
||||
yield sub
|
||||
else:
|
||||
yield el
|
||||
|
||||
def block_search(name, scope):
|
||||
""" Search scope blocks for name
|
||||
@param str: name
|
||||
@param list: scope
|
||||
@return: list
|
||||
"""
|
||||
def search(m, l):
|
||||
"""
|
||||
"""
|
||||
prop = []
|
||||
for b in l:
|
||||
n = b._cident
|
||||
if m == n:
|
||||
prop.append(b)
|
||||
elif m in b._parts:
|
||||
prop.append(b)
|
||||
elif m.startswith(n):
|
||||
r = search(m, b.parsed['inner'])
|
||||
if r: prop.extend(r)
|
||||
return prop
|
||||
l = len(scope)
|
||||
i = 1
|
||||
prop = []
|
||||
while (l-i) >= 0:
|
||||
if scope[-i]['__blocks__']:
|
||||
b = search(name, scope[-i]['__blocks__'])
|
||||
if b: prop.extend(b)
|
||||
i += 1
|
||||
# HACK
|
||||
if '>' in name:
|
||||
name = ''.join([c for c in name if c != '>'])
|
||||
i = 0
|
||||
while (l-i) >= 0:
|
||||
if scope[-i]['__blocks__']:
|
||||
b = search(name, scope[-i]['__blocks__'])
|
||||
if b: prop.extend(b)
|
||||
i += 1
|
||||
return prop
|
||||
|
||||
def destring(v):
|
||||
""" Strip quotes
|
||||
@param string: value
|
||||
@return: string
|
||||
"""
|
||||
return v.strip('"\'')
|
||||
|
||||
def analyze_number(var, err=''):
|
||||
""" Analyse number for type and split from unit
|
||||
@param str: value
|
||||
@return: tuple (number, unit)
|
||||
"""
|
||||
u = None
|
||||
if type(var) is not str:
|
||||
return (var, u)
|
||||
if is_color(var):
|
||||
return (var, 'color')
|
||||
var = var.strip()
|
||||
n = var
|
||||
if not '.' in var:
|
||||
try:
|
||||
n = int(var)
|
||||
return (n, u)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
try:
|
||||
n = float(var)
|
||||
except (ValueError, TypeError):
|
||||
try:
|
||||
n = ''.join([c for c in var if c in '0123456789.-'])
|
||||
n = float(n) if '.' in n else int(n)
|
||||
u = ''.join([c for c in var if c not in '0123456789.-'])
|
||||
except ValueError:
|
||||
raise SyntaxError('%s ´%s´' % (err, var))
|
||||
return (n, u)
|
||||
|
||||
def with_unit(n, u):
|
||||
""" Return number with unit
|
||||
@param int/float: value
|
||||
@param str: unit
|
||||
@return: mixed
|
||||
"""
|
||||
if n == 0: return 0
|
||||
if u:
|
||||
return "%s%s" % (str(n), u)
|
||||
return n
|
||||
|
||||
def is_color(v):
|
||||
""" Is CSS color
|
||||
@param mixed: value
|
||||
@return: bool
|
||||
"""
|
||||
if not v or type(v) is not str:
|
||||
return False
|
||||
l = len(v)
|
||||
if l == 4 or l == 7:
|
||||
try:
|
||||
int(v[1:], 16)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def is_variable(v):
|
||||
""" Check if string is LESS variable
|
||||
@param string: check
|
||||
@return: bool
|
||||
"""
|
||||
if type(v) is str:
|
||||
return (v.startswith('@') or v.startswith('-@'))
|
||||
return False
|
||||
|
||||
def print_recursive(ll, lvl=0):
|
||||
""" Scopemap printer
|
||||
@param list: parser result
|
||||
@param int: depth
|
||||
"""
|
||||
if not ll: return
|
||||
pad = ''.join(['. '] * lvl)
|
||||
if type(ll) is list:
|
||||
ll = flatten(ll)
|
||||
for l in ll:
|
||||
t = type(l)
|
||||
if t == str:
|
||||
print(pad + l)
|
||||
else:
|
||||
print_recursive(l, lvl+1)
|
||||
elif hasattr(ll, '_p'):
|
||||
print(pad + str(type(ll)))
|
||||
print_recursive(ll._p, lvl+1)
|
||||
else:
|
||||
print(pad + ll)
|
||||
|
||||
|
||||
0
lesscpy/lib/__init__.py
Normal file
0
lesscpy/lib/__init__.py
Normal file
293
lesscpy/lib/css.py
Normal file
293
lesscpy/lib/css.py
Normal file
@@ -0,0 +1,293 @@
|
||||
"""
|
||||
CSS syntax names.
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
css2 = [
|
||||
'azimuth',
|
||||
'background-attachment',
|
||||
'background-color',
|
||||
'background-image',
|
||||
'background-position',
|
||||
'background-repeat',
|
||||
'background',
|
||||
'border-collapse',
|
||||
'border-color',
|
||||
'border-spacing',
|
||||
'border-style',
|
||||
'border-top',
|
||||
'border-right',
|
||||
'border-bottom',
|
||||
'border-left',
|
||||
'border-top-color',
|
||||
'border-right-color',
|
||||
'border-bottom-color',
|
||||
'border-left-color',
|
||||
'border-top-style',
|
||||
'border-right-style',
|
||||
'border-bottom-style',
|
||||
'border-left-style',
|
||||
'border-top-width',
|
||||
'border-right-width',
|
||||
'border-bottom-width',
|
||||
'border-left-width',
|
||||
'border-width',
|
||||
'border',
|
||||
'bottom',
|
||||
'caption-side',
|
||||
'clear',
|
||||
'clip',
|
||||
'color',
|
||||
'content',
|
||||
'counter-increment',
|
||||
'counter-reset',
|
||||
'cue-after',
|
||||
'cue-before',
|
||||
'cue',
|
||||
'cursor',
|
||||
'direction',
|
||||
'display',
|
||||
'elevation',
|
||||
'empty-cells',
|
||||
'float',
|
||||
'font-family',
|
||||
'font-size',
|
||||
'font-style',
|
||||
'font-variant',
|
||||
'font-weight',
|
||||
'font',
|
||||
'height',
|
||||
'left',
|
||||
'letter-spacing',
|
||||
'line-height',
|
||||
'list-style-image',
|
||||
'list-style-position',
|
||||
'list-style-type',
|
||||
'list-style',
|
||||
'margin-right',
|
||||
'margin-left',
|
||||
'margin-top',
|
||||
'margin-bottom',
|
||||
'margin',
|
||||
'max-height',
|
||||
'max-width',
|
||||
'min-height',
|
||||
'min-width',
|
||||
'orphans',
|
||||
'outline-color',
|
||||
'outline-style',
|
||||
'outline-width',
|
||||
'outline',
|
||||
'overflow',
|
||||
'padding-top',
|
||||
'padding-right',
|
||||
'padding-bottom',
|
||||
'padding-left',
|
||||
'padding',
|
||||
'page-break-after',
|
||||
'page-break-before',
|
||||
'page-break-inside',
|
||||
'pause-after',
|
||||
'pause-before',
|
||||
'pause',
|
||||
'pitch-range',
|
||||
'pitch',
|
||||
'play-during',
|
||||
'position',
|
||||
'quotes',
|
||||
'richness',
|
||||
'right',
|
||||
'speak-header',
|
||||
'speak-numeral',
|
||||
'speak-punctuation',
|
||||
'speak',
|
||||
'speech-rate',
|
||||
'stress',
|
||||
'table-layout',
|
||||
'text-align',
|
||||
'text-decoration',
|
||||
'text-indent',
|
||||
'text-transform',
|
||||
'top',
|
||||
'unicode-bidi',
|
||||
'vertical-align',
|
||||
'visibility',
|
||||
'voice-family',
|
||||
'volume',
|
||||
'white-space',
|
||||
'widows',
|
||||
'width',
|
||||
'word-spacing',
|
||||
'z-index',
|
||||
]
|
||||
css3 = [
|
||||
'alignment-adjust',
|
||||
'alignment-baseline',
|
||||
'animation',
|
||||
'animation-delay',
|
||||
'animation-direction',
|
||||
'animation-duration',
|
||||
'animation-iteration-count',
|
||||
'animation-name',
|
||||
'animation-play-state',
|
||||
'animation-timing-function',
|
||||
'appearance',
|
||||
'backface-visibility',
|
||||
'background-clip',
|
||||
'background-origin',
|
||||
'background-size',
|
||||
'baseline-shift',
|
||||
'bookmark-label',
|
||||
'bookmark-level',
|
||||
'bookmark-target',
|
||||
'border-bottom-left-radius',
|
||||
'border-bottom-right-radius',
|
||||
'border-image',
|
||||
'border-image-outset',
|
||||
'border-image-repeat',
|
||||
'border-image-slice',
|
||||
'border-image-source',
|
||||
'border-image-width',
|
||||
'border-radius',
|
||||
'border-top-left-radius',
|
||||
'border-top-right-radius',
|
||||
'box-align',
|
||||
'box-decoration-break',
|
||||
'box-direction',
|
||||
'box-flex',
|
||||
'box-flex-group',
|
||||
'box-lines',
|
||||
'box-ordinal-group',
|
||||
'box-orient',
|
||||
'box-pack',
|
||||
'box-shadow',
|
||||
'box-sizing',
|
||||
'color-profile',
|
||||
'column-count',
|
||||
'column-fill',
|
||||
'column-gap',
|
||||
'column-rule',
|
||||
'column-rule-color',
|
||||
'column-rule-style',
|
||||
'column-rule-width',
|
||||
'column-span',
|
||||
'column-width',
|
||||
'columns',
|
||||
'crop',
|
||||
'dominant-baseline',
|
||||
'drop-initial-after-adjust',
|
||||
'drop-initial-after-align',
|
||||
'drop-initial-before-adjust',
|
||||
'drop-initial-before-align',
|
||||
'drop-initial-size',
|
||||
'drop-initial-value',
|
||||
'fit',
|
||||
'fit-position',
|
||||
'float-offset',
|
||||
'font-size-adjust',
|
||||
'font-stretch',
|
||||
'grid-columns',
|
||||
'grid-rows',
|
||||
'hanging-punctuation',
|
||||
'hyphenate-after',
|
||||
'hyphenate-before',
|
||||
'hyphenate-character',
|
||||
'hyphenate-lines',
|
||||
'hyphenate-resource',
|
||||
'hyphens',
|
||||
'icon',
|
||||
'image-orientation',
|
||||
'image-resolution',
|
||||
'inline-box-align',
|
||||
'line-stacking',
|
||||
'line-stacking-ruby',
|
||||
'line-stacking-shift',
|
||||
'line-stacking-strategy',
|
||||
'mark',
|
||||
'mark-after',
|
||||
'mark-before',
|
||||
'marks',
|
||||
'marquee-direction',
|
||||
'marquee-play-count',
|
||||
'marquee-speed',
|
||||
'marquee-style',
|
||||
'move-to',
|
||||
'nav-down',
|
||||
'nav-index',
|
||||
'nav-left',
|
||||
'nav-right',
|
||||
'nav-up',
|
||||
'opacity',
|
||||
'outline-offset',
|
||||
'overflow-style',
|
||||
'overflow-x',
|
||||
'overflow-y',
|
||||
'page',
|
||||
'page-policy',
|
||||
'perspective',
|
||||
'perspective-origin',
|
||||
'phonemes',
|
||||
'punctuation-trim',
|
||||
'rendering-intent',
|
||||
'resize',
|
||||
'rest',
|
||||
'rest-after',
|
||||
'rest-before',
|
||||
'rotation',
|
||||
'rotation-point',
|
||||
'ruby-align',
|
||||
'ruby-overhang',
|
||||
'ruby-position',
|
||||
'ruby-span',
|
||||
'size',
|
||||
'string-set',
|
||||
'target',
|
||||
'target-name',
|
||||
'target-new',
|
||||
'target-position',
|
||||
'text-align-last',
|
||||
'text-height',
|
||||
'text-justify',
|
||||
'text-outline',
|
||||
'text-overflow',
|
||||
'text-shadow',
|
||||
'text-wrap',
|
||||
'transform',
|
||||
'transform-origin',
|
||||
'transform-style',
|
||||
'transition',
|
||||
'transition-delay',
|
||||
'transition-duration',
|
||||
'transition-property',
|
||||
'transition-timing-function',
|
||||
'voice-balance',
|
||||
'voice-duration',
|
||||
'voice-pitch',
|
||||
'voice-pitch-range',
|
||||
'voice-rate',
|
||||
'voice-stress',
|
||||
'voice-volume',
|
||||
'word-break',
|
||||
'word-wrap'
|
||||
]
|
||||
vendor_prefix = [
|
||||
'-ms-',
|
||||
'-moz-',
|
||||
'-o-',
|
||||
'-atsc-',
|
||||
'-wap-',
|
||||
'-webkit-',
|
||||
'-khtml-'
|
||||
'-xv-',
|
||||
'mso-',
|
||||
]
|
||||
vendor_ugly = [
|
||||
'filter',
|
||||
'accelerator',
|
||||
'behavior',
|
||||
'filter',
|
||||
'zoom',
|
||||
]
|
||||
propertys = css2 + css3 + vendor_ugly
|
||||
142
lesscpy/lib/dom.py
Normal file
142
lesscpy/lib/dom.py
Normal file
@@ -0,0 +1,142 @@
|
||||
"""
|
||||
HTML DOM names
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
html4 = [
|
||||
'a',
|
||||
'abbr',
|
||||
'acronym',
|
||||
'address',
|
||||
'applet',
|
||||
'area',
|
||||
'b',
|
||||
'base',
|
||||
'basefont',
|
||||
'bdo',
|
||||
'big',
|
||||
'blockquote',
|
||||
'body',
|
||||
'br',
|
||||
'button',
|
||||
'caption',
|
||||
'center',
|
||||
'cite',
|
||||
'code',
|
||||
'col',
|
||||
'colgroup',
|
||||
'dd',
|
||||
'del',
|
||||
'dfn',
|
||||
'dir',
|
||||
'div',
|
||||
'dl',
|
||||
'dt',
|
||||
'em',
|
||||
'fieldset',
|
||||
'font',
|
||||
'form',
|
||||
'frame',
|
||||
'frameset',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'head',
|
||||
'hr',
|
||||
'html',
|
||||
'i',
|
||||
'iframe',
|
||||
'img',
|
||||
'input',
|
||||
'ins',
|
||||
'kbd',
|
||||
'label',
|
||||
'legend',
|
||||
'li',
|
||||
# 'link',
|
||||
'map',
|
||||
'mark',
|
||||
'menu',
|
||||
'meta',
|
||||
'noframes',
|
||||
'noscript',
|
||||
'object',
|
||||
'ol',
|
||||
'optgroup',
|
||||
'option',
|
||||
'p',
|
||||
'param',
|
||||
'pre',
|
||||
'q',
|
||||
's',
|
||||
'samp',
|
||||
'script',
|
||||
'select',
|
||||
'small',
|
||||
'span',
|
||||
'strike',
|
||||
'strong',
|
||||
'style',
|
||||
'sub',
|
||||
'sup',
|
||||
'table',
|
||||
'tbody',
|
||||
'td',
|
||||
'textarea',
|
||||
'tfoot',
|
||||
'th',
|
||||
'thead',
|
||||
'title',
|
||||
'tr',
|
||||
'tt',
|
||||
'u',
|
||||
'ul',
|
||||
'var',
|
||||
|
||||
'print',
|
||||
'screen',
|
||||
'all',
|
||||
'projection',
|
||||
|
||||
# 'focus',
|
||||
# 'hover',
|
||||
]
|
||||
html5 = [
|
||||
'article',
|
||||
'aside',
|
||||
'audio',
|
||||
'bdi',
|
||||
'canvas',
|
||||
'command',
|
||||
'datalist',
|
||||
'details',
|
||||
'embed',
|
||||
'figcaption',
|
||||
'figure',
|
||||
'footer',
|
||||
'header',
|
||||
'hgroup',
|
||||
'keygen',
|
||||
'mark',
|
||||
'meter',
|
||||
'nav',
|
||||
'output',
|
||||
'progress',
|
||||
'rp',
|
||||
'rt',
|
||||
'ruby',
|
||||
'section',
|
||||
'source',
|
||||
'summary',
|
||||
'time',
|
||||
'track',
|
||||
'video',
|
||||
'wbr',
|
||||
]
|
||||
html = html4
|
||||
html.extend(html5)
|
||||
18
lesscpy/plib/__init__.py
Normal file
18
lesscpy/plib/__init__.py
Normal file
@@ -0,0 +1,18 @@
|
||||
__all__ = [
|
||||
'Block',
|
||||
'Call',
|
||||
'Expression',
|
||||
'Mixin',
|
||||
'Property',
|
||||
'Statement',
|
||||
'String',
|
||||
'Variable'
|
||||
]
|
||||
from .block import Block
|
||||
from .call import Call
|
||||
from .expression import Expression
|
||||
from .mixin import Mixin
|
||||
from .property import Property
|
||||
from .statement import Statement
|
||||
from .string import String
|
||||
from .variable import Variable
|
||||
81
lesscpy/plib/block.py
Normal file
81
lesscpy/plib/block.py
Normal file
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
Block node.
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
from collections import OrderedDict
|
||||
|
||||
import lesscpy.lessc.utility as utility
|
||||
from .process import Process
|
||||
|
||||
class Block(Process):
|
||||
format = "%(identifier)s%(ws)s{%(nl)s%(proplist)s}%(endblock)s"
|
||||
|
||||
def parse(self, scope):
|
||||
""" Parse Node
|
||||
@param list: current scope
|
||||
"""
|
||||
self._blocktype = None
|
||||
self.scope = scope
|
||||
self._proplist()
|
||||
self.parsed['inner'] = [p for p in self._p[2]
|
||||
if type(p) is type(self)]
|
||||
self._pname()
|
||||
if self._name.startswith('@media'):
|
||||
self._blocktype = 'inner'
|
||||
self.parsed['identifier'] = self._ident.strip()
|
||||
return self
|
||||
|
||||
def merge(self, block):
|
||||
"""
|
||||
"""
|
||||
assert(type(block) is Block)
|
||||
self.parsed['proplist'].extend(block.parsed['proplist'])
|
||||
|
||||
def _pname(self):
|
||||
""" Parse block name and identifier
|
||||
"""
|
||||
name = ["%s " % t
|
||||
if t in '>+'
|
||||
else t
|
||||
for t in utility.flatten(self._p[1])]
|
||||
self._name = ''.join(name)
|
||||
self._ident = self._fullname(name)
|
||||
self._cident = self._ident.replace(' ', '')
|
||||
|
||||
def _fullname(self, name):
|
||||
"""
|
||||
"""
|
||||
parent = list(utility.flatten([s['current']
|
||||
for s in self.scope
|
||||
if 'current' in s]))
|
||||
if parent:
|
||||
parent.reverse()
|
||||
if parent[-1].startswith('@media'):
|
||||
parent.pop()
|
||||
names = [p.strip()
|
||||
for p in self._name.split(',')]
|
||||
self._parts = names
|
||||
for p in parent:
|
||||
parts = p.split(',')
|
||||
names = [n.replace('&', p)
|
||||
if '&' in n
|
||||
else
|
||||
"%s %s" % (p.strip(), n)
|
||||
for n in names
|
||||
for p in parts]
|
||||
return ', '.join(names) if len(names) < 6 else ',\n'.join(names)
|
||||
|
||||
def _proplist(self):
|
||||
""" Reduce property list to remove redundant styles.
|
||||
Multiple porperties of the same type overwrite,
|
||||
so we can safely take only the last.
|
||||
"""
|
||||
proplist = [p for p in utility.flatten(self._p[2])
|
||||
if p and type(p) != type(self)]
|
||||
store = OrderedDict()
|
||||
for p in proplist:
|
||||
store[p.parsed['property']] = p
|
||||
self.parsed['proplist'] = store.values()
|
||||
101
lesscpy/plib/call.py
Normal file
101
lesscpy/plib/call.py
Normal file
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
Call Node.
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
import re
|
||||
from urllib.parse import quote as urlquote
|
||||
from .process import Process
|
||||
from lesscpy.lessc.color import LessColor
|
||||
from .expression import Expression
|
||||
import lesscpy.lessc.utility as utility
|
||||
|
||||
class Call(Process):
|
||||
def parse(self, scope):
|
||||
""" Parse Node
|
||||
@param list: current scope
|
||||
"""
|
||||
self.scope = scope
|
||||
call = list(utility.flatten(self._p[1:]))
|
||||
name = call[0]
|
||||
if name == '%(':
|
||||
name = 'sformat'
|
||||
elif name == '~':
|
||||
name = 'e'
|
||||
color = LessColor()
|
||||
call = self.process_tokens(call[1:])
|
||||
args = [t for t in call
|
||||
if type(t) is not str or t not in '(),']
|
||||
if hasattr(self, name):
|
||||
try:
|
||||
return getattr(self, name)(*args)
|
||||
except ValueError:
|
||||
pass
|
||||
if hasattr(color, name):
|
||||
try:
|
||||
return getattr(color, name)(*args)
|
||||
except ValueError:
|
||||
pass
|
||||
call = ' '.join([str(t) for t in call[1:-1]]).replace(' = ', '=')
|
||||
return ["%s(%s)" % (name, call)]
|
||||
|
||||
def e(self, string):
|
||||
""" Less Escape.
|
||||
@param string: value
|
||||
@return string
|
||||
"""
|
||||
return utility.destring(string.strip('~'))
|
||||
|
||||
def sformat(self, *args):
|
||||
""" String format
|
||||
@param list: values
|
||||
@return string
|
||||
"""
|
||||
format = args[0]
|
||||
items = []
|
||||
m = re.findall('(%[asdA])', format)
|
||||
i = 1
|
||||
for n in m:
|
||||
v = {
|
||||
'%d' : int,
|
||||
'%A' : urlquote,
|
||||
'%s' : utility.destring,
|
||||
}.get(n, str)(args[i])
|
||||
items.append(v)
|
||||
i += 1
|
||||
format = format.replace('%A', '%s')
|
||||
return format % tuple(items)
|
||||
|
||||
def increment(self, v):
|
||||
""" Increment function
|
||||
@param Mixed: value
|
||||
@return: incremented value
|
||||
"""
|
||||
n, u = utility.analyze_number(v)
|
||||
return utility.with_unit(n+1, u)
|
||||
|
||||
def decrement(self, v):
|
||||
""" Decrement function
|
||||
@param Mixed: value
|
||||
@return: incremented value
|
||||
"""
|
||||
n, u = utility.analyze_number(v)
|
||||
return utility.with_unit(n-1, u)
|
||||
|
||||
def add(self, *args):
|
||||
""" Add integers
|
||||
@param list: values
|
||||
@return: int
|
||||
"""
|
||||
return sum([int(v) for v in args])
|
||||
|
||||
def round(self, v):
|
||||
""" Round number
|
||||
@param Mixed: value
|
||||
@return: rounded value
|
||||
"""
|
||||
n, u = utility.analyze_number(v)
|
||||
return utility.with_unit(round(float(n)), u)
|
||||
|
||||
100
lesscpy/plib/expression.py
Normal file
100
lesscpy/plib/expression.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""
|
||||
Expression Node.
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
import lesscpy.lessc.utility as utility
|
||||
from .process import Process
|
||||
from lesscpy.lessc import color
|
||||
|
||||
class Expression(Process):
|
||||
def parse(self, scope):
|
||||
""" Parse Node
|
||||
@param list: current scope
|
||||
"""
|
||||
self.scope = scope
|
||||
expr = [self._p[1], self._p[2], self._p[3]]
|
||||
while True:
|
||||
done = True
|
||||
expr = [self.neg(t) for t in expr]
|
||||
if any(t for t in expr if hasattr(t, 'parse')):
|
||||
expr = [t.parse(scope) if hasattr(t, 'parse')
|
||||
else t
|
||||
for t in expr]
|
||||
done = False
|
||||
if any(t for t in expr if utility.is_variable(t)):
|
||||
expr = self.replace_vars(expr)
|
||||
done = False
|
||||
expr = list(utility.flatten(expr))
|
||||
if done: break
|
||||
p = self.process(expr)
|
||||
return p
|
||||
|
||||
def neg(self, t):
|
||||
"""
|
||||
"""
|
||||
if t and type(t) is list and t[0] == '-':
|
||||
v = t[1]
|
||||
if len(t) > 1 and hasattr(t[1], 'parse'):
|
||||
v = t[1].parse(self.scope)
|
||||
if type(v) is str:
|
||||
return '-' + v
|
||||
return -v
|
||||
return t
|
||||
|
||||
def process(self, expression):
|
||||
"""
|
||||
"""
|
||||
assert(len(expression) == 3)
|
||||
A, O, B = expression
|
||||
a, ua = utility.analyze_number(A, 'Illegal element in expression')
|
||||
b, ub = utility.analyze_number(B, 'Illegal element in expression')
|
||||
if(a is False or b is False):
|
||||
return self.expression()
|
||||
if ua == 'color' or ub == 'color':
|
||||
return color.LessColor().process(expression)
|
||||
out = self.operate(a, b, O)
|
||||
if type(a) is int and type(b) is int:
|
||||
out = int(out)
|
||||
return self.units(out, ua, ub)
|
||||
|
||||
def units(self, v, ua, ub):
|
||||
"""
|
||||
"""
|
||||
if v == 0: return v;
|
||||
if ua and ub:
|
||||
if ua == ub:
|
||||
return str(v) + ua
|
||||
else:
|
||||
raise SyntaxError("Error in expression %s != %s" % (ua, ub))
|
||||
elif ua:
|
||||
return str(v) + ua
|
||||
elif ub:
|
||||
return str(v) + ub
|
||||
return v
|
||||
|
||||
def operate(self, a, b, o):
|
||||
"""
|
||||
"""
|
||||
# print("´%s´ ´%s´ ´%s´" % (a, b, o))
|
||||
operation = {
|
||||
'+': '__add__',
|
||||
'-': '__sub__',
|
||||
'*': '__mul__',
|
||||
'/': '__truediv__'
|
||||
}.get(o)
|
||||
v = getattr(a, operation)(b)
|
||||
if v is NotImplemented:
|
||||
v = getattr(b, operation)(a)
|
||||
return v
|
||||
|
||||
def expression(self):
|
||||
"""
|
||||
"""
|
||||
return [self._p[1], self._p[2], self._p[3]]
|
||||
|
||||
|
||||
|
||||
|
||||
97
lesscpy/plib/mixin.py
Normal file
97
lesscpy/plib/mixin.py
Normal file
@@ -0,0 +1,97 @@
|
||||
"""
|
||||
Parametered Mixin Node.
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
from collections import deque
|
||||
import lesscpy.lessc.utility as utility
|
||||
import copy
|
||||
from .process import Process
|
||||
from .node import Node
|
||||
|
||||
class Mixin(Process):
|
||||
""" Mixin Node.
|
||||
"""
|
||||
def parse(self, scope, stash=None):
|
||||
""" Parse Node
|
||||
@param list: current scope
|
||||
"""
|
||||
self.stash = stash
|
||||
self.name = self._p[1][0]
|
||||
if len(self._p[1]) > 1:
|
||||
if type(self._p[1][2]) is list:
|
||||
self.argv = [[u[0], u[2]] if type(u) is list
|
||||
else [u, None]
|
||||
for u in self._p[1][2]
|
||||
if u and u != ',']
|
||||
else:
|
||||
self.argv = self._p[1][2]
|
||||
self.argc = len(self.argv)
|
||||
else:
|
||||
self.argv = []
|
||||
self.argc = 0
|
||||
self.prop = self._p[2]
|
||||
|
||||
def call(self, args, scope=[{}]):
|
||||
""" Call mixin function.
|
||||
@param list: Arguments passed to mixin
|
||||
@return: Property list
|
||||
"""
|
||||
if self.argv == '@arguments':
|
||||
return [self.replace_arguments(copy.copy(p), args).parse(self.scope)
|
||||
for p in self.prop
|
||||
if p]
|
||||
self.scope = scope
|
||||
self.scope[0].update(self.stash)
|
||||
prop = [copy.copy(p) for p in self.prop if p]
|
||||
prop = utility.flatten([p.call(args, self.scope)
|
||||
if type(p) is Mixin else p
|
||||
for p in prop])
|
||||
self._process_args(args)
|
||||
return [p.parse(self.scope) for p in prop]
|
||||
|
||||
def _process_args(self, args):
|
||||
""" Process arguments to mixin call.
|
||||
Handle the @arguments variable
|
||||
@param list: arguments
|
||||
"""
|
||||
variables = []
|
||||
if args:
|
||||
args = [a for a in self.process_tokens(args)
|
||||
if a != ',']
|
||||
else:
|
||||
args = []
|
||||
args = deque(args)
|
||||
for v in self.argv:
|
||||
if args:
|
||||
u = args.popleft()
|
||||
variables.append((v[0], u))
|
||||
else:
|
||||
variables.append(v)
|
||||
for v in variables:
|
||||
self._create_variable(v)
|
||||
# Special @arguments
|
||||
arguments = [v[1] for v in variables]
|
||||
arguments.extend(args)
|
||||
self._create_variable(('@arguments', [arguments]))
|
||||
|
||||
def _create_variable(self, l):
|
||||
""" Create new variable in scope, from list or index
|
||||
@param tuple: name/value pair
|
||||
"""
|
||||
assert(len(l) == 2)
|
||||
var = Node()
|
||||
var._name, var._value = l
|
||||
self.scope[0][var._name] = var
|
||||
|
||||
def replace_arguments(self, p, args):
|
||||
""" Replace the special @arguments variable
|
||||
@param Property object: Property object
|
||||
@param list: Replacement list
|
||||
@return: Property object
|
||||
"""
|
||||
p._p[3] = args
|
||||
return p
|
||||
|
||||
18
lesscpy/plib/node.py
Normal file
18
lesscpy/plib/node.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
Parser node base class.
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
class Node(object):
|
||||
def name(self):
|
||||
""" @return: node name"""
|
||||
return self._name
|
||||
|
||||
def value(self):
|
||||
""" @return: node value"""
|
||||
return self._value
|
||||
|
||||
def parse(self, scope):
|
||||
raise NotImplementedError()
|
||||
84
lesscpy/plib/process.py
Normal file
84
lesscpy/plib/process.py
Normal file
@@ -0,0 +1,84 @@
|
||||
"""
|
||||
Base process Node
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
import lesscpy.lessc.utility as utility
|
||||
from .node import Node
|
||||
|
||||
class Process(Node):
|
||||
def __init__(self, p):
|
||||
self._p = list(p)
|
||||
self.lines = [j for j in [p.lineno(i)
|
||||
for i in range(len(self._p))]
|
||||
if j]
|
||||
self.scope = None
|
||||
self.parsed = {}
|
||||
|
||||
def process_tokens(self, tokens):
|
||||
"""
|
||||
"""
|
||||
while True:
|
||||
done = True
|
||||
if any(t for t in tokens if hasattr(t, 'parse')):
|
||||
tokens = [t.parse(self.scope) if hasattr(t, 'parse')
|
||||
else t
|
||||
for t in tokens]
|
||||
done = False
|
||||
if any(t for t in tokens if utility.is_variable(t)):
|
||||
tokens = self.replace_vars(tokens)
|
||||
done = False
|
||||
tokens = list(utility.flatten(tokens))
|
||||
if done: break
|
||||
return tokens
|
||||
|
||||
def replace_vars(self, tokens):
|
||||
"""
|
||||
Replace variables in tokenlist
|
||||
"""
|
||||
return [self.swap(t)
|
||||
if utility.is_variable(t)
|
||||
else t
|
||||
for t in tokens]
|
||||
|
||||
def swap(self, var):
|
||||
"""
|
||||
Swap single variable
|
||||
"""
|
||||
if not self.scope:
|
||||
raise SyntaxError("Unknown variable ´%s´" % var)
|
||||
pad = ''
|
||||
pre = ''
|
||||
if var.endswith(' '):
|
||||
var = var.strip()
|
||||
pad = ' '
|
||||
if var.startswith('-'):
|
||||
var = ''.join(var[1:])
|
||||
pre = '-'
|
||||
r = var.startswith('@@')
|
||||
t = ''.join(var[1:]) if r else var
|
||||
i = len(self.scope)
|
||||
while i >=0:
|
||||
i -= 1
|
||||
if t in self.scope[i]:
|
||||
f = self.scope[i][t].value()
|
||||
if r:
|
||||
return self.swap("%s@%s%s" % (pre, f[0].strip('"\''), pad))
|
||||
return self.ftok(f, pre, pad)
|
||||
raise SyntaxError("Unknown variable ´%s´" % var)
|
||||
|
||||
def ftok(self, t, pre, pad):
|
||||
"""
|
||||
"""
|
||||
try:
|
||||
r = ''.join(t)
|
||||
except TypeError:
|
||||
r = t[0] if type(t) is list else str(t)
|
||||
if pad and type(r) is str:
|
||||
r += pad
|
||||
if pre and type(r) is str:
|
||||
r = pre + r
|
||||
return r
|
||||
|
||||
40
lesscpy/plib/property.py
Normal file
40
lesscpy/plib/property.py
Normal file
@@ -0,0 +1,40 @@
|
||||
"""
|
||||
Property Node
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
import lesscpy.lessc.utility as utility
|
||||
from .process import Process
|
||||
|
||||
class Property(Process):
|
||||
format = "%(tab)s%(property)s:%(ws)s%(style)s;%(nl)s"
|
||||
|
||||
def parse(self, scope):
|
||||
""" Parse Node
|
||||
@param list: current scope
|
||||
"""
|
||||
self.scope = scope
|
||||
self.parsed = {}
|
||||
tokens = list(utility.flatten([self._p[1], self._p[3]]))
|
||||
self.parsed['property'] = tokens[0]
|
||||
style = self.preprocess(tokens[1:])
|
||||
style = self.process_tokens(style)
|
||||
style = ' '.join([str(t).strip() for t in style
|
||||
if t is not None]).replace(' , ', ', ')
|
||||
self.parsed['style'] = style
|
||||
return self
|
||||
|
||||
def preprocess(self, style):
|
||||
""" Preprocess for annoying shorthand CSS declarations
|
||||
@param list: style
|
||||
@return: list
|
||||
"""
|
||||
if self.parsed['property'] == 'font':
|
||||
style = [''.join(u.expression())
|
||||
if hasattr(u, 'expression')
|
||||
else u
|
||||
for u in style]
|
||||
return style
|
||||
|
||||
20
lesscpy/plib/statement.py
Normal file
20
lesscpy/plib/statement.py
Normal file
@@ -0,0 +1,20 @@
|
||||
"""
|
||||
Statement Node
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
from .process import Process
|
||||
|
||||
class Statement(Process):
|
||||
format = "%(identifier)s%(ws)s%(value)s;%(nl)s"
|
||||
|
||||
def parse(self, scope):
|
||||
""" Parse Node
|
||||
@param list: current scope
|
||||
"""
|
||||
self._ident = self._p[1].strip()
|
||||
self.parsed['identifier'] = self._ident
|
||||
self.parsed['value'] = self._p[2]
|
||||
|
||||
27
lesscpy/plib/string.py
Normal file
27
lesscpy/plib/string.py
Normal file
@@ -0,0 +1,27 @@
|
||||
"""
|
||||
String Node
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
import re
|
||||
from .process import Process
|
||||
|
||||
class String(Process):
|
||||
def parse(self, scope):
|
||||
""" Parse Node
|
||||
@param list: current scope
|
||||
@return: string
|
||||
"""
|
||||
self.scope = scope
|
||||
return re.sub(r'@\{([^\}]+)\}', lambda m: self.format(m.group(1)), self._p[1])
|
||||
|
||||
def format(self, var):
|
||||
""" Format variable for replacement
|
||||
@param string: var
|
||||
@return: string
|
||||
"""
|
||||
var = '@' + var
|
||||
var = self.swap(var)
|
||||
return var.strip("\"'")
|
||||
18
lesscpy/plib/variable.py
Normal file
18
lesscpy/plib/variable.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
Variable Node
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details.
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
from .process import Process
|
||||
|
||||
class Variable(Process):
|
||||
def name(self):
|
||||
return self._p[1]
|
||||
|
||||
def value(self):
|
||||
return self._p[3]
|
||||
|
||||
def parse(self, scope):
|
||||
return ''
|
||||
0
lesscpy/scripts/__init__.py
Normal file
0
lesscpy/scripts/__init__.py
Normal file
127
lesscpy/scripts/compiler.py
Normal file
127
lesscpy/scripts/compiler.py
Normal file
@@ -0,0 +1,127 @@
|
||||
"""
|
||||
CSS/LESSCSS run script
|
||||
|
||||
http://lesscss.org/#docs
|
||||
|
||||
Copyright (c)
|
||||
See LICENSE for details
|
||||
<jtm@robot.is>
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
import argparse
|
||||
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
|
||||
from lesscpy.lessc import parser
|
||||
from lesscpy.lessc import lexer
|
||||
from lesscpy.lessc import formatter
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
def run():
|
||||
aparse = argparse.ArgumentParser(description='LessCss Compiler', epilog='<< jtm@robot.is @_o >>')
|
||||
aparse.add_argument('-I', '--include', action="store", type=str,
|
||||
help="Included less-files (comma separated)")
|
||||
aparse.add_argument('-x', '--minify', action="store_true",
|
||||
default=False, help="Minify output")
|
||||
aparse.add_argument('-X', '--xminify', action="store_true",
|
||||
default=False, help="Minify output, no end of block newlines")
|
||||
aparse.add_argument('-m', '--min-ending', action="store_true",
|
||||
default=False, help="Add '.min' into output filename. eg, name.min.css")
|
||||
aparse.add_argument('-D', '--dry-run', action="store_true",
|
||||
default=False, help="Dry run, do not write files")
|
||||
aparse.add_argument('-v', '--verbose', action="store_true",
|
||||
default=False, help="Verbose mode")
|
||||
aparse.add_argument('-o', '--out', action="store", help="Output directory")
|
||||
group = aparse.add_argument_group('Debugging')
|
||||
group.add_argument('-S', '--scopemap', action="store_true",
|
||||
default=False, help="Scopemap")
|
||||
group.add_argument('-V', '--debug', action="store_true",
|
||||
default=False, help="Debug mode")
|
||||
group.add_argument('-L', '--lex-only', action="store_true",
|
||||
default=False, help="Run lexer on target")
|
||||
group.add_argument('-N', '--no-css', action="store_true",
|
||||
default=False, help="No css output")
|
||||
aparse.add_argument('target')
|
||||
args = aparse.parse_args()
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
if args.lex_only:
|
||||
lex = lexer.LessLexer()
|
||||
ll = lex.file(args.target)
|
||||
while True:
|
||||
tok = ll.token()
|
||||
if not tok: break
|
||||
print(tok)
|
||||
print('EOF')
|
||||
sys.exit()
|
||||
#
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
yacctab = 'yacctab' if args.debug else None
|
||||
scope = None
|
||||
if args.include:
|
||||
for u in args.include.split(','):
|
||||
if args.debug: print("compiling include: %s" % u)
|
||||
p = parser.LessParser(
|
||||
yacc_debug=False,
|
||||
lex_optimize=True,
|
||||
yacc_optimize=True,
|
||||
yacctab=yacctab)
|
||||
p.parse(filename=u, debuglevel=0)
|
||||
if not scope:
|
||||
scope = p.scope
|
||||
else:
|
||||
scope[0].update(p.scope[0])
|
||||
else:
|
||||
scope = None
|
||||
p = None
|
||||
f = formatter.Formatter()
|
||||
if not os.path.exists(args.target):
|
||||
sys.exit("Target not found '%s' ..." % args.target)
|
||||
if os.path.isdir(args.target):
|
||||
if not args.out:
|
||||
sys.exit("Compile directory option needs -o ...")
|
||||
elif os.path.isdir(args.out) and not os.listdir(args.out) == []:
|
||||
sys.exit("Output directory not empty...")
|
||||
else:
|
||||
if not os.path.isdir(args.out):
|
||||
if args.verbose:
|
||||
print("Creating '%s'" % args.out)
|
||||
if not args.dry_run:
|
||||
os.mkdir(args.out)
|
||||
less = glob.glob(os.path.join(args.target, '*.less'))
|
||||
for lf in less:
|
||||
outf = os.path.splitext(os.path.basename(lf))
|
||||
min = '.min' if args.min_ending else ''
|
||||
outf = "%s/%s%s.css" % (args.out, outf[0], min)
|
||||
if args.verbose: print("%s -> %s" % (lf, outf))
|
||||
|
||||
p = parser.LessParser(yacc_debug=False,
|
||||
lex_optimize=True,
|
||||
yacc_optimize=True,
|
||||
scope=scope,
|
||||
yacctab=yacctab)
|
||||
p.parse(filename=lf, debuglevel=0)
|
||||
css = f.format(p, args.minify, args.xminify)
|
||||
if not args.dry_run:
|
||||
with open(outf, 'w') as outfile:
|
||||
outfile.write(css)
|
||||
if args.dry_run:
|
||||
print('Dry run, nothing done.')
|
||||
|
||||
else:
|
||||
if args.verbose: print("compiling target: %s" % args.target)
|
||||
p = parser.LessParser(yacc_debug=(args.debug),
|
||||
lex_optimize=True,
|
||||
yacc_optimize=(not args.debug),
|
||||
scope=scope)
|
||||
p.parse(filename=args.target, debuglevel=0)
|
||||
if args.scopemap:
|
||||
args.no_css = True
|
||||
p.scopemap()
|
||||
if not args.no_css and p:
|
||||
out = f.format(p, args.minify, args.xminify)
|
||||
print(out)
|
||||
|
||||
0
lesscpy/test/__init__.py
Normal file
0
lesscpy/test/__init__.py
Normal file
33
lesscpy/test/__main__.py
Normal file
33
lesscpy/test/__main__.py
Normal file
@@ -0,0 +1,33 @@
|
||||
"""
|
||||
LessCss tests
|
||||
"""
|
||||
import unittest
|
||||
import sys, os
|
||||
import re
|
||||
|
||||
here = os.path.dirname(__file__)
|
||||
path = os.path.abspath(here)
|
||||
while os.path.dirname(path) != path:
|
||||
if os.path.exists(os.path.join(path, 'lesscpy', '__init__.py')):
|
||||
sys.path.insert(0, path)
|
||||
break
|
||||
path = os.path.dirname(path)
|
||||
|
||||
def find():
|
||||
svn = re.compile('\.svn')
|
||||
test = re.compile('test.+\.py$')
|
||||
skip = re.compile('testissues.*')
|
||||
alltests = unittest.TestSuite()
|
||||
for path, dirs, files in os.walk(here):
|
||||
if svn.search(path):
|
||||
continue
|
||||
for f in files:
|
||||
if skip.search(f):
|
||||
continue
|
||||
if test.search(f):
|
||||
module = __import__(f.split('.')[0])
|
||||
alltests.addTest(unittest.findTestCases(module))
|
||||
return alltests
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(defaultTest='find')
|
||||
13
lesscpy/test/css/color-functions.css
Normal file
13
lesscpy/test/css/color-functions.css
Normal file
@@ -0,0 +1,13 @@
|
||||
.color_functions {
|
||||
lighten: #ffcccc;
|
||||
darken: #330000;
|
||||
saturate: #203c31;
|
||||
desaturate: #29332f;
|
||||
greyscale: #4e0e27;
|
||||
spin-p: #bf6b40;
|
||||
spin-n: #bf4055;
|
||||
hue: 100.0;
|
||||
saturation: 12;
|
||||
lightness: 95;
|
||||
}
|
||||
|
||||
1
lesscpy/test/css/color-functions.min.css
vendored
Normal file
1
lesscpy/test/css/color-functions.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.color_functions{lighten:#ffcccc;darken:#330000;saturate:#203c31;desaturate:#29332f;greyscale:#4e0e27;spin-p:#bf6b40;spin-n:#bf4055;hue:100.0;saturation:12;lightness:95;}
|
||||
48
lesscpy/test/css/colors.css
Normal file
48
lesscpy/test/css/colors.css
Normal file
@@ -0,0 +1,48 @@
|
||||
#yelow #short {
|
||||
color: #ffeeaa;
|
||||
}
|
||||
#yelow #long {
|
||||
color: #ffeeaa;
|
||||
}
|
||||
#yelow #rgba {
|
||||
color: rgba(255, 238, 170, 0.1);
|
||||
}
|
||||
#yelow #argb {
|
||||
color: argb(rgba(255, 238, 170, 0.1));
|
||||
}
|
||||
#blue #short {
|
||||
color: #0000ff;
|
||||
}
|
||||
#blue #long {
|
||||
color: #0000ff;
|
||||
}
|
||||
#blue #rgba {
|
||||
color: rgba(0, 0, 255, 0.1);
|
||||
}
|
||||
#blue #argb {
|
||||
color: argb(rgba(0, 0, 255, 0.1));
|
||||
}
|
||||
#alpha #hsla {
|
||||
color: hsla(11, 20%, 20%, 0.6);
|
||||
}
|
||||
#overflow .a {
|
||||
color: #000000;
|
||||
}
|
||||
#overflow .b {
|
||||
color: #ffffff;
|
||||
}
|
||||
#overflow .c {
|
||||
color: #ffffff;
|
||||
}
|
||||
#overflow .d {
|
||||
color: #00ff00;
|
||||
}
|
||||
#grey {
|
||||
color: #c8c8c8;
|
||||
}
|
||||
#808080 {
|
||||
color: #808080;
|
||||
}
|
||||
#00ff00 {
|
||||
color: #00ff00;
|
||||
}
|
||||
16
lesscpy/test/css/colors.min.css
vendored
Normal file
16
lesscpy/test/css/colors.min.css
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
#yelow #short{color:#ffeeaa;}
|
||||
#yelow #long{color:#ffeeaa;}
|
||||
#yelow #rgba{color:rgba(255, 238, 170, 0.1);}
|
||||
#yelow #argb{color:argb(rgba(255, 238, 170, 0.1));}
|
||||
#blue #short{color:#0000ff;}
|
||||
#blue #long{color:#0000ff;}
|
||||
#blue #rgba{color:rgba(0, 0, 255, 0.1);}
|
||||
#blue #argb{color:argb(rgba(0, 0, 255, 0.1));}
|
||||
#alpha #hsla{color:hsla(11, 20%, 20%, 0.6);}
|
||||
#overflow .a{color:#000000;}
|
||||
#overflow .b{color:#ffffff;}
|
||||
#overflow .c{color:#ffffff;}
|
||||
#overflow .d{color:#00ff00;}
|
||||
#grey{color:#c8c8c8;}
|
||||
#808080{color:#808080;}
|
||||
#00ff00{color:#00ff00;}
|
||||
17
lesscpy/test/css/comments.css
Normal file
17
lesscpy/test/css/comments.css
Normal file
@@ -0,0 +1,17 @@
|
||||
#comments {
|
||||
color: red;
|
||||
background-color: orange;
|
||||
font-size: 12px;
|
||||
content: "content";
|
||||
border: 1px solid black;
|
||||
padding: 0;
|
||||
margin: 2em;
|
||||
}
|
||||
.selector, .lots, .comments {
|
||||
color: grey, orange;
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 8px;
|
||||
}
|
||||
#last {
|
||||
color: blue;
|
||||
}
|
||||
3
lesscpy/test/css/comments.min.css
vendored
Normal file
3
lesscpy/test/css/comments.min.css
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
#comments{color:red;background-color:orange;font-size:12px;content:"content";border:1px solid black;padding:0;margin:2em;}
|
||||
.selector, .lots, .comments{color:grey, orange;-webkit-border-radius:2px;-moz-border-radius:8px;}
|
||||
#last{color:blue;}
|
||||
46
lesscpy/test/css/css-3.css
Normal file
46
lesscpy/test/css/css-3.css
Normal file
@@ -0,0 +1,46 @@
|
||||
.comma-delimited {
|
||||
background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg);
|
||||
text-shadow: -1px -1px 1px red, 6px 5px 5px yellow;
|
||||
-moz-box-shadow: 0pt 0pt 2px rgba(255, 255, 255, 0.4) inset, 0pt 4px 6px rgba(255, 255, 255, 0.4) inset;
|
||||
}
|
||||
@font-face {
|
||||
font-family: Headline;
|
||||
src: local(Futura-Medium), url(fonts.svg#MyGeometricModern) format("svg");
|
||||
}
|
||||
.other {
|
||||
-moz-transform: translate(0, 11em) rotate(-90deg);
|
||||
}
|
||||
p:not([class*="lead"]) {
|
||||
color: black;
|
||||
}
|
||||
input[type="text"].class#id[attr=32]:not(1) {
|
||||
color: white;
|
||||
}
|
||||
div#id.class[a=1][b=2].class:not(1) {
|
||||
color: white;
|
||||
}
|
||||
ul.comma > li:not(:only-child)::after {
|
||||
color: white;
|
||||
}
|
||||
ol.comma > li:nth-last-child(2)::after {
|
||||
color: white;
|
||||
}
|
||||
li:nth-child(4n+1), li:nth-child(-5n), li:nth-child(-n+2) {
|
||||
color: white;
|
||||
}
|
||||
a[href^="http://"] {
|
||||
color: black;
|
||||
}
|
||||
a[href$="http://"] {
|
||||
color: black;
|
||||
}
|
||||
form[data-disabled] {
|
||||
color: black;
|
||||
}
|
||||
p::before {
|
||||
color: black;
|
||||
}
|
||||
#issue322 {
|
||||
-webkit-animation: anim2 7s infinite ease-in-out;
|
||||
}
|
||||
|
||||
14
lesscpy/test/css/css-3.min.css
vendored
Normal file
14
lesscpy/test/css/css-3.min.css
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
.comma-delimited{background:url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg);text-shadow:-1px -1px 1px red, 6px 5px 5px yellow;-moz-box-shadow:0pt 0pt 2px rgba(255, 255, 255, 0.4) inset, 0pt 4px 6px rgba(255, 255, 255, 0.4) inset;}
|
||||
@font-face{font-family:Headline;src:local(Futura-Medium), url(fonts.svg#MyGeometricModern) format("svg");}
|
||||
.other{-moz-transform:translate(0, 11em) rotate(-90deg);}
|
||||
p:not([class*="lead"]){color:black;}
|
||||
input[type="text"].class#id[attr=32]:not(1){color:white;}
|
||||
div#id.class[a=1][b=2].class:not(1){color:white;}
|
||||
ul.comma > li:not(:only-child)::after{color:white;}
|
||||
ol.comma > li:nth-last-child(2)::after{color:white;}
|
||||
li:nth-child(4n+1), li:nth-child(-5n), li:nth-child(-n+2){color:white;}
|
||||
a[href^="http://"]{color:black;}
|
||||
a[href$="http://"]{color:black;}
|
||||
form[data-disabled]{color:black;}
|
||||
p::before{color:black;}
|
||||
#issue322{-webkit-animation:anim2 7s infinite ease-in-out;}
|
||||
16
lesscpy/test/css/css-escapes.css
Normal file
16
lesscpy/test/css/css-escapes.css
Normal file
@@ -0,0 +1,16 @@
|
||||
.escape\|random\|char {
|
||||
color: red;
|
||||
}
|
||||
.mixin\!tUp {
|
||||
font-weight: bold;
|
||||
}
|
||||
.\34 04 {
|
||||
background: red;
|
||||
}
|
||||
.\34 04 strong {
|
||||
color: fuchsia;
|
||||
font-weight: bold;
|
||||
}
|
||||
.trailingTest\+ {
|
||||
color: red;
|
||||
}
|
||||
5
lesscpy/test/css/css-escapes.min.css
vendored
Normal file
5
lesscpy/test/css/css-escapes.min.css
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
.escape\|random\|char{color:red;}
|
||||
.mixin\!tUp{font-weight:bold;}
|
||||
.\34 04{background:red;}
|
||||
.\34 04 strong{color:fuchsia;font-weight:bold;}
|
||||
.trailingTest\+{color:red;}
|
||||
89
lesscpy/test/css/css.css
Normal file
89
lesscpy/test/css/css.css
Normal file
@@ -0,0 +1,89 @@
|
||||
@charset "utf-8";
|
||||
div {
|
||||
color: black;
|
||||
}
|
||||
div {
|
||||
width: 99%;
|
||||
}
|
||||
* {
|
||||
min-width: 45em;
|
||||
}
|
||||
h1, h2 > a > p, h3 {
|
||||
color: none;
|
||||
}
|
||||
div.class {
|
||||
color: blue;
|
||||
}
|
||||
div#id {
|
||||
color: green;
|
||||
}
|
||||
.class#id {
|
||||
color: purple;
|
||||
}
|
||||
.one.two.three {
|
||||
color: grey;
|
||||
}
|
||||
.one .two .three {
|
||||
color: grey;
|
||||
}
|
||||
@media print {
|
||||
font-size: 3em;
|
||||
}
|
||||
@media screen {
|
||||
font-size: 10px;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Garamond Pro';
|
||||
src: url("/fonts/garamond-pro.ttf");
|
||||
}
|
||||
a:hover, a:link {
|
||||
color: #999999;
|
||||
}
|
||||
p, p:first-child {
|
||||
text-transform: none;
|
||||
}
|
||||
q:lang(no) {
|
||||
quotes: none;
|
||||
}
|
||||
p + h1 {
|
||||
font-size: 2.2em;
|
||||
}
|
||||
#shorthands {
|
||||
border: 1px solid #000000;
|
||||
font: 12px/16px Arial;
|
||||
margin: 1px 0;
|
||||
padding: 0 auto;
|
||||
background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px;
|
||||
}
|
||||
#shorthands {
|
||||
font: 100%/16px Arial;
|
||||
}
|
||||
#more-shorthands {
|
||||
margin: 0;
|
||||
padding: 1px 0 2px 0;
|
||||
font: normal small/20px 'Trebuchet MS', Verdana, sans-serif;
|
||||
}
|
||||
.misc {
|
||||
-moz-border-radius: 2px;
|
||||
display: -moz-inline-stack;
|
||||
width: .1em;
|
||||
background-color: #009998;
|
||||
background-image: url(images/image.jpg);
|
||||
background: -webkit-gradient(linear, left top, left bottom, from(red), to(blue));
|
||||
filter: alpha(opacity=100);
|
||||
}
|
||||
#important {
|
||||
color: red !important;
|
||||
width: 100% !important;
|
||||
height: 20px !important;
|
||||
}
|
||||
#data-uri {
|
||||
background: url(data:image/png;charset=utf-8;base64,
|
||||
kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
|
||||
k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
|
||||
kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);
|
||||
background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
|
||||
}
|
||||
#svg-data-uri {
|
||||
background: transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');
|
||||
}
|
||||
27
lesscpy/test/css/css.min.css
vendored
Normal file
27
lesscpy/test/css/css.min.css
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
@charset"utf-8";div{color:black;}
|
||||
div{width:99%;}
|
||||
*{min-width:45em;}
|
||||
h1, h2 > a > p, h3{color:none;}
|
||||
div.class{color:blue;}
|
||||
div#id{color:green;}
|
||||
.class#id{color:purple;}
|
||||
.one.two.three{color:grey;}
|
||||
.one .two .three{color:grey;}
|
||||
@media print{font-size:3em;}
|
||||
@media screen{font-size:10px;}
|
||||
@font-face{font-family:'Garamond Pro';src:url("/fonts/garamond-pro.ttf");}
|
||||
a:hover, a:link{color:#999999;}
|
||||
p, p:first-child{text-transform:none;}
|
||||
q:lang(no){quotes:none;}
|
||||
p + h1{font-size:2.2em;}
|
||||
#shorthands{border:1px solid #000000;font:12px/16px Arial;margin:1px 0;padding:0 auto;background:url("http://www.lesscss.org/spec.html") no-repeat 0 4px;}
|
||||
#shorthands{font:100%/16px Arial;}
|
||||
#more-shorthands{margin:0;padding:1px 0 2px 0;font:normal small/20px 'Trebuchet MS', Verdana, sans-serif;}
|
||||
.misc{-moz-border-radius:2px;display:-moz-inline-stack;width:.1em;background-color:#009998;background-image:url(images/image.jpg);background:-webkit-gradient(linear, left top, left bottom, from(red), to(blue));filter:alpha(opacity=100);}
|
||||
#important{color:red !important;width:100% !important;height:20px !important;}
|
||||
#data-uri{background:url(data:image/png;charset=utf-8;base64,
|
||||
kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
|
||||
k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
|
||||
kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);background-image:url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);}
|
||||
#svg-data-uri{background:transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');}
|
||||
.uri_test{background-image:url(fonts.svg#MyGeometricModern);behavior:url(border-radius.htc);}
|
||||
18
lesscpy/test/css/functions.css
Normal file
18
lesscpy/test/css/functions.css
Normal file
@@ -0,0 +1,18 @@
|
||||
#functions {
|
||||
width: 16;
|
||||
height: undefined("self");
|
||||
border-width: 5;
|
||||
variable: 11;
|
||||
decrement: 9;
|
||||
}
|
||||
#built-in {
|
||||
escaped: -Some::weird(#thing, y);
|
||||
escaped1: -Some::weird(#thing, z);
|
||||
format: "rgb(32, 128, 64)";
|
||||
format-string: "hello world";
|
||||
format-multiple: "hello earth 2";
|
||||
format-url-encode: 'red is %23ff0000';
|
||||
eformat: rgb(32, 128, 64);
|
||||
rounded: 10;
|
||||
roundedpx: 3px;
|
||||
}
|
||||
2
lesscpy/test/css/functions.min.css
vendored
Normal file
2
lesscpy/test/css/functions.min.css
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#functions{width:16;height:undefined("self");border-width:5;variable:11;decrement:9;}
|
||||
#built-in{escaped:-Some::weird(#thing, y);escaped1:-Some::weird(#thing, z);format:"rgb(32, 128, 64)";format-string:"hello world";format-multiple:"hello earth 2";format-url-encode:'red is %23ff0000';eformat:rgb(32, 128, 64);rounded:10;roundedpx:3px;}
|
||||
9
lesscpy/test/css/ie-filters.css
Normal file
9
lesscpy/test/css/ie-filters.css
Normal file
@@ -0,0 +1,9 @@
|
||||
.nav {
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#333333", endColorstr="#000000", GradientType=0);
|
||||
}
|
||||
.nav {
|
||||
filter: progid:DXImageTransform.Microsoft.Alpha(opacity=0);
|
||||
}
|
||||
.nav {
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#333333", endColorstr="#000000", GradientType=0);
|
||||
}
|
||||
3
lesscpy/test/css/ie-filters.min.css
vendored
Normal file
3
lesscpy/test/css/ie-filters.min.css
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.nav{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#333333", endColorstr="#000000", GradientType=0);}
|
||||
.nav{filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);}
|
||||
.nav{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#333333", endColorstr="#000000", GradientType=0);}
|
||||
7
lesscpy/test/css/import.css
vendored
Normal file
7
lesscpy/test/css/import.css
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
@import 'some.css.file.css';
|
||||
@import 'some/other.css.file.CSS';
|
||||
.import {
|
||||
color: red;
|
||||
width: 6px;
|
||||
height: 9px;
|
||||
}
|
||||
1
lesscpy/test/css/import.min.css
vendored
Normal file
1
lesscpy/test/css/import.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
@import'some.css.file.css';@import'some/other.css.file.CSS';.import{color:red;width:6px;height:9px;}
|
||||
3
lesscpy/test/css/issues/inherit_scope.css
Normal file
3
lesscpy/test/css/issues/inherit_scope.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.tiny-scope {
|
||||
color: #998899;
|
||||
}
|
||||
9
lesscpy/test/css/issues/mixins-closure.css
Normal file
9
lesscpy/test/css/issues/mixins-closure.css
Normal file
@@ -0,0 +1,9 @@
|
||||
.class {
|
||||
width: 99px;
|
||||
}
|
||||
.overwrite {
|
||||
width: 99px;
|
||||
}
|
||||
.nested .class {
|
||||
width: 5px;
|
||||
}
|
||||
14
lesscpy/test/css/issues/mixins-nested.css
Normal file
14
lesscpy/test/css/issues/mixins-nested.css
Normal file
@@ -0,0 +1,14 @@
|
||||
.class .inner {
|
||||
height: 300;
|
||||
}
|
||||
.class .inner .innest {
|
||||
width: 30;
|
||||
border-width: 60;
|
||||
}
|
||||
.class2 .inner {
|
||||
height: 600;
|
||||
}
|
||||
.class2 .inner .innest {
|
||||
width: 60;
|
||||
border-width: 120;
|
||||
}
|
||||
49
lesscpy/test/css/issues/mixins-pattern.css
Normal file
49
lesscpy/test/css/issues/mixins-pattern.css
Normal file
@@ -0,0 +1,49 @@
|
||||
.zero {
|
||||
zero: 0;
|
||||
one: 1;
|
||||
two: 2;
|
||||
three: 3;
|
||||
}
|
||||
.one {
|
||||
zero: 0;
|
||||
one: 1;
|
||||
one-req: 1;
|
||||
two: 2;
|
||||
three: 3;
|
||||
}
|
||||
.two {
|
||||
zero: 0;
|
||||
one: 1;
|
||||
two: 2;
|
||||
three: 3;
|
||||
}
|
||||
.three {
|
||||
zero: 0;
|
||||
one: 1;
|
||||
two: 2;
|
||||
three-req: 3;
|
||||
three: 3;
|
||||
}
|
||||
.left {
|
||||
left: 1;
|
||||
}
|
||||
.right {
|
||||
right: 1;
|
||||
}
|
||||
.border-right {
|
||||
color: black;
|
||||
border-right: 4px;
|
||||
}
|
||||
.border-left {
|
||||
color: black;
|
||||
border-left: 4px;
|
||||
}
|
||||
.only-right {
|
||||
right: 33;
|
||||
}
|
||||
.only-left {
|
||||
left: 33;
|
||||
}
|
||||
.left-right {
|
||||
both: 330;
|
||||
}
|
||||
0
lesscpy/test/css/issues/plus_identifier.css
Normal file
0
lesscpy/test/css/issues/plus_identifier.css
Normal file
21
lesscpy/test/css/media.css
Normal file
21
lesscpy/test/css/media.css
Normal file
@@ -0,0 +1,21 @@
|
||||
@media print {
|
||||
.class {
|
||||
color: blue;
|
||||
}
|
||||
.class .sub {
|
||||
width: 42;
|
||||
}
|
||||
.top, header > h1 {
|
||||
color: #444444;
|
||||
}
|
||||
}
|
||||
@media screen {
|
||||
body {
|
||||
max-width: 480;
|
||||
}
|
||||
}
|
||||
@media all and (orientation:portrait) {
|
||||
aside {
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
8
lesscpy/test/css/media.min.css
vendored
Normal file
8
lesscpy/test/css/media.min.css
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
@media print{.class{color:blue;}
|
||||
.class .sub{width:42;}
|
||||
.top, header > h1{color:#444444;}
|
||||
}
|
||||
@media screen{body{max-width:480;}
|
||||
}
|
||||
@media all and (orientation:portrait){aside{float:none;}
|
||||
}
|
||||
50
lesscpy/test/css/mixins-args.css
Normal file
50
lesscpy/test/css/mixins-args.css
Normal file
@@ -0,0 +1,50 @@
|
||||
#hidden {
|
||||
color: transparent;
|
||||
}
|
||||
.two-args {
|
||||
color: blue;
|
||||
width: 10px;
|
||||
height: 99%;
|
||||
border: 2px dotted black;
|
||||
}
|
||||
.one-arg {
|
||||
width: 15px;
|
||||
height: 49%;
|
||||
}
|
||||
.no-parens {
|
||||
width: 5px;
|
||||
height: 49%;
|
||||
}
|
||||
.no-args {
|
||||
width: 5px;
|
||||
height: 49%;
|
||||
}
|
||||
.var-args {
|
||||
width: 45;
|
||||
height: 17%;
|
||||
}
|
||||
.multi-mix {
|
||||
width: 10px;
|
||||
height: 29%;
|
||||
margin: 4;
|
||||
padding: 5;
|
||||
}
|
||||
body {
|
||||
padding: 30px;
|
||||
color: #f00;
|
||||
}
|
||||
.scope-mix {
|
||||
width: 8;
|
||||
}
|
||||
#same-var-name {
|
||||
radius: 5px;
|
||||
}
|
||||
#var-inside {
|
||||
width: 10px;
|
||||
}
|
||||
.arguments {
|
||||
border: 1px solid black;
|
||||
}
|
||||
.arguments2 {
|
||||
border: 0px;
|
||||
}
|
||||
13
lesscpy/test/css/mixins-args.min.css
vendored
Normal file
13
lesscpy/test/css/mixins-args.min.css
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
#hidden{color:transparent;}
|
||||
.two-args{color:blue;width:10px;height:99%;border:2px dotted black;}
|
||||
.one-arg{width:15px;height:49%;}
|
||||
.no-parens{width:5px;height:49%;}
|
||||
.no-args{width:5px;height:49%;}
|
||||
.var-args{width:45;height:17%;}
|
||||
.multi-mix{width:10px;height:29%;margin:4;padding:5;}
|
||||
body{padding:30px;color:#f00;}
|
||||
.scope-mix{width:8;}
|
||||
#same-var-name{radius:5px;}
|
||||
#var-inside{width:10px;}
|
||||
.arguments{border:1px solid black;}
|
||||
.arguments2{border:0px;}
|
||||
25
lesscpy/test/css/mixins-redundant.css
Normal file
25
lesscpy/test/css/mixins-redundant.css
Normal file
@@ -0,0 +1,25 @@
|
||||
.ext {
|
||||
color: red;
|
||||
}
|
||||
.ext {
|
||||
color: blue;
|
||||
}
|
||||
.ext {
|
||||
color: red;
|
||||
}
|
||||
.ext {
|
||||
color: black;
|
||||
}
|
||||
.ext {
|
||||
color: red;
|
||||
}
|
||||
.ext {
|
||||
color: green;
|
||||
}
|
||||
p {
|
||||
width: 1px;
|
||||
color: green;
|
||||
}
|
||||
a {
|
||||
color: green;
|
||||
}
|
||||
8
lesscpy/test/css/mixins-redundant.min.css
vendored
Normal file
8
lesscpy/test/css/mixins-redundant.min.css
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
.ext{color:red;}
|
||||
.ext{color:blue;}
|
||||
.ext{color:red;}
|
||||
.ext{color:black;}
|
||||
.ext{color:red;}
|
||||
.ext{color:green;}
|
||||
p{width:1px;color:green;}
|
||||
a{color:green;}
|
||||
74
lesscpy/test/css/mixins.css
Normal file
74
lesscpy/test/css/mixins.css
Normal file
@@ -0,0 +1,74 @@
|
||||
.mixin {
|
||||
border: 1px solid black;
|
||||
}
|
||||
.mixout {
|
||||
border-color: orange;
|
||||
}
|
||||
.borders {
|
||||
border-style: dashed;
|
||||
}
|
||||
#namespace .borders {
|
||||
border-style: dotted;
|
||||
}
|
||||
#namespace .biohazard {
|
||||
content: "death";
|
||||
}
|
||||
#namespace .biohazard .man {
|
||||
color: transparent;
|
||||
}
|
||||
#theme > .mixin {
|
||||
background-color: grey;
|
||||
}
|
||||
#container {
|
||||
color: black;
|
||||
border: 1px solid black;
|
||||
border-color: orange;
|
||||
background-color: grey;
|
||||
}
|
||||
#header .milk {
|
||||
color: white;
|
||||
border: 1px solid black;
|
||||
background-color: grey;
|
||||
}
|
||||
#header #cookie {
|
||||
border-style: dashed;
|
||||
}
|
||||
#header #cookie .chips {
|
||||
border-style: dotted;
|
||||
}
|
||||
#header #cookie .chips .calories {
|
||||
color: black;
|
||||
border: 1px solid black;
|
||||
border-color: orange;
|
||||
background-color: grey;
|
||||
}
|
||||
.secure-zone {
|
||||
color: transparent;
|
||||
}
|
||||
.direct {
|
||||
border-style: dotted;
|
||||
}
|
||||
.bo, .bar {
|
||||
width: 100%;
|
||||
}
|
||||
.bo {
|
||||
border: 1px;
|
||||
}
|
||||
.bo {
|
||||
color: red;
|
||||
}
|
||||
.ar.bo.ca {
|
||||
color: black;
|
||||
}
|
||||
.jo.ki {
|
||||
background: none;
|
||||
}
|
||||
.extended {
|
||||
width: 100%;
|
||||
border: 1px;
|
||||
color: red;
|
||||
background: none;
|
||||
}
|
||||
.foo .bar {
|
||||
width: 100%;
|
||||
}
|
||||
21
lesscpy/test/css/mixins.min.css
vendored
Normal file
21
lesscpy/test/css/mixins.min.css
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
.mixin{border:1px solid black;}
|
||||
.mixout{border-color:orange;}
|
||||
.borders{border-style:dashed;}
|
||||
#namespace .borders{border-style:dotted;}
|
||||
#namespace .biohazard{content:"death";}
|
||||
#namespace .biohazard .man{color:transparent;}
|
||||
#theme > .mixin{background-color:grey;}
|
||||
#container{color:black;border:1px solid black;border-color:orange;background-color:grey;}
|
||||
#header .milk{color:white;border:1px solid black;background-color:grey;}
|
||||
#header #cookie{border-style:dashed;}
|
||||
#header #cookie .chips{border-style:dotted;}
|
||||
#header #cookie .chips .calories{color:black;border:1px solid black;border-color:orange;background-color:grey;}
|
||||
.secure-zone{color:transparent;}
|
||||
.direct{border-style:dotted;}
|
||||
.bo, .bar{width:100%;}
|
||||
.bo{border:1px;}
|
||||
.bo{color:red;}
|
||||
.ar.bo.ca{color:black;}
|
||||
.jo.ki{background:none;}
|
||||
.extended{width:100%;border:1px;color:red;background:none;}
|
||||
.foo .bar{width:100%;}
|
||||
42
lesscpy/test/css/operations.css
Normal file
42
lesscpy/test/css/operations.css
Normal file
@@ -0,0 +1,42 @@
|
||||
#operations {
|
||||
color: #111111;
|
||||
height: 9px;
|
||||
width: 3em;
|
||||
text-shadow: -1px -1px 1px red, 6px 5px 5px yellow;
|
||||
substraction: 0;
|
||||
division: 1;
|
||||
}
|
||||
#operations .spacing {
|
||||
height: 9px;
|
||||
width: 3em;
|
||||
}
|
||||
.with-variables {
|
||||
height: 16em;
|
||||
width: 24em;
|
||||
size: 1cm;
|
||||
}
|
||||
.negative {
|
||||
height: 0;
|
||||
width: 4px;
|
||||
}
|
||||
.shorthands {
|
||||
padding: -1px 2px 0 -4px;
|
||||
}
|
||||
.colors {
|
||||
color: #112233;
|
||||
border-color: #334455;
|
||||
background-color: #000000;
|
||||
}
|
||||
.colors .other {
|
||||
color: #222222;
|
||||
border-color: #222222;
|
||||
}
|
||||
.negations {
|
||||
variable: -4px;
|
||||
variable1: 0;
|
||||
variable2: 0;
|
||||
variable3: 8px;
|
||||
variable4: 0;
|
||||
paren: -4px;
|
||||
paren2: 16px;
|
||||
}
|
||||
8
lesscpy/test/css/operations.min.css
vendored
Normal file
8
lesscpy/test/css/operations.min.css
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
#operations{color:#111111;height:9px;width:3em;text-shadow:-1px -1px 1px red, 6px 5px 5px yellow;substraction:0;division:1;}
|
||||
#operations .spacing{height:9px;width:3em;}
|
||||
.with-variables{height:16em;width:24em;size:1cm;}
|
||||
.negative{height:0;width:4px;}
|
||||
.shorthands{padding:-1px 2px 0 -4px;}
|
||||
.colors{color:#112233;border-color:#334455;background-color:#000000;}
|
||||
.colors .other{color:#222222;border-color:#222222;}
|
||||
.negations{variable:-4px;variable1:0;variable2:0;variable3:8px;variable4:0;paren:-4px;paren2:16px;}
|
||||
20
lesscpy/test/css/parens.css
Normal file
20
lesscpy/test/css/parens.css
Normal file
@@ -0,0 +1,20 @@
|
||||
.parens {
|
||||
border: 2px solid black;
|
||||
margin: 1px 3px 16 3;
|
||||
width: 36;
|
||||
padding: 2px 36px;
|
||||
}
|
||||
.more-parens {
|
||||
padding: 8 4 4 4px;
|
||||
width: 96;
|
||||
height: 113;
|
||||
margin: 12;
|
||||
}
|
||||
.nested-parens {
|
||||
width: 71;
|
||||
height: 6;
|
||||
}
|
||||
.mixed-units {
|
||||
margin: 2px 4em 1 5pc;
|
||||
padding: 6px 1em 2px 2;
|
||||
}
|
||||
4
lesscpy/test/css/parens.min.css
vendored
Normal file
4
lesscpy/test/css/parens.min.css
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.parens{border:2px solid black;margin:1px 3px 16 3;width:36;padding:2px 36px;}
|
||||
.more-parens{padding:8 4 4 4px;width:96;height:113;margin:12;}
|
||||
.nested-parens{width:71;height:6;}
|
||||
.mixed-units{margin:2px 4em 1 5pc;padding:6px 1em 2px 2;}
|
||||
29
lesscpy/test/css/rulesets.css
Normal file
29
lesscpy/test/css/rulesets.css
Normal file
@@ -0,0 +1,29 @@
|
||||
#first > .one {
|
||||
font-size: 2em;
|
||||
}
|
||||
#first > .one > #second .two > #deux {
|
||||
width: 50%;
|
||||
}
|
||||
#first > .one > #second .two > #deux #third {
|
||||
height: 100%;
|
||||
}
|
||||
#first > .one > #second .two > #deux #third:focus {
|
||||
color: black;
|
||||
}
|
||||
#first > .one > #second .two > #deux #third:focus #fifth > #sixth .seventh #eighth {
|
||||
color: purple;
|
||||
}
|
||||
#first > .one > #second .two > #deux #fourth, #first > .one > #second .two > #deux #five, #first > .one > #second .two > #deux #six {
|
||||
color: #110000;
|
||||
}
|
||||
#first > .one > #second .two > #deux #fourth .seven,
|
||||
#first > .one > #second .two > #deux #five .seven,
|
||||
#first > .one > #second .two > #deux #six .seven,
|
||||
#first > .one > #second .two > #deux #fourth .eight > #nine,
|
||||
#first > .one > #second .two > #deux #five .eight > #nine,
|
||||
#first > .one > #second .two > #deux #six .eight > #nine {
|
||||
border: 1px solid black;
|
||||
}
|
||||
#first > .one > #second .two > #deux #fourth #ten, #first > .one > #second .two > #deux #five #ten, #first > .one > #second .two > #deux #six #ten {
|
||||
color: red;
|
||||
}
|
||||
13
lesscpy/test/css/rulesets.min.css
vendored
Normal file
13
lesscpy/test/css/rulesets.min.css
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
#first > .one{font-size:2em;}
|
||||
#first > .one > #second .two > #deux{width:50%;}
|
||||
#first > .one > #second .two > #deux #third{height:100%;}
|
||||
#first > .one > #second .two > #deux #third:focus{color:black;}
|
||||
#first > .one > #second .two > #deux #third:focus #fifth > #sixth .seventh #eighth{color:purple;}
|
||||
#first > .one > #second .two > #deux #fourth, #first > .one > #second .two > #deux #five, #first > .one > #second .two > #deux #six{color:#110000;}
|
||||
#first > .one > #second .two > #deux #fourth .seven,
|
||||
#first > .one > #second .two > #deux #five .seven,
|
||||
#first > .one > #second .two > #deux #six .seven,
|
||||
#first > .one > #second .two > #deux #fourth .eight > #nine,
|
||||
#first > .one > #second .two > #deux #five .eight > #nine,
|
||||
#first > .one > #second .two > #deux #six .eight > #nine{border:1px solid black;}
|
||||
#first > .one > #second .two > #deux #fourth #ten, #first > .one > #second .two > #deux #five #ten, #first > .one > #second .two > #deux #six #ten{color:red;}
|
||||
12
lesscpy/test/css/scope.css
Normal file
12
lesscpy/test/css/scope.css
Normal file
@@ -0,0 +1,12 @@
|
||||
.scope1 {
|
||||
color: blue;
|
||||
border-color: black;
|
||||
}
|
||||
.scope1 .scope2 {
|
||||
color: blue;
|
||||
}
|
||||
.scope1 .scope2 .scope3 {
|
||||
color: red;
|
||||
border-color: black;
|
||||
background-color: white;
|
||||
}
|
||||
3
lesscpy/test/css/scope.min.css
vendored
Normal file
3
lesscpy/test/css/scope.min.css
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.scope1{color:blue;border-color:black;}
|
||||
.scope1 .scope2{color:blue;}
|
||||
.scope1 .scope2 .scope3{color:red;border-color:black;background-color:white;}
|
||||
53
lesscpy/test/css/selectors.css
Normal file
53
lesscpy/test/css/selectors.css
Normal file
@@ -0,0 +1,53 @@
|
||||
h1 a:hover,
|
||||
h2 a:hover,
|
||||
h3 a:hover,
|
||||
h1 p:hover,
|
||||
h2 p:hover,
|
||||
h3 p:hover {
|
||||
color: red;
|
||||
}
|
||||
#all {
|
||||
color: blue;
|
||||
}
|
||||
#the {
|
||||
color: blue;
|
||||
}
|
||||
#same {
|
||||
color: blue;
|
||||
}
|
||||
ul,
|
||||
li,
|
||||
div,
|
||||
q,
|
||||
blockquote,
|
||||
textarea {
|
||||
margin: 0;
|
||||
}
|
||||
td {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
td, input {
|
||||
line-height: 1em;
|
||||
}
|
||||
a {
|
||||
color: red;
|
||||
}
|
||||
a:hover {
|
||||
color: blue;
|
||||
}
|
||||
div a {
|
||||
color: green;
|
||||
}
|
||||
p a span {
|
||||
color: yellow;
|
||||
}
|
||||
.foo .bar .qux, .foo .baz .qux {
|
||||
display: block;
|
||||
}
|
||||
.foo .qux .bar, .foo .qux .baz {
|
||||
display: inline;
|
||||
}
|
||||
.foo .qux .bar .biz, .foo .qux .baz .biz {
|
||||
display: none;
|
||||
}
|
||||
24
lesscpy/test/css/selectors.min.css
vendored
Normal file
24
lesscpy/test/css/selectors.min.css
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
h1 a:hover,
|
||||
h2 a:hover,
|
||||
h3 a:hover,
|
||||
h1 p:hover,
|
||||
h2 p:hover,
|
||||
h3 p:hover{color:red;}
|
||||
#all{color:blue;}
|
||||
#the{color:blue;}
|
||||
#same{color:blue;}
|
||||
ul,
|
||||
li,
|
||||
div,
|
||||
q,
|
||||
blockquote,
|
||||
textarea{margin:0;}
|
||||
td{margin:0;padding:0;}
|
||||
td, input{line-height:1em;}
|
||||
a{color:red;}
|
||||
a:hover{color:blue;}
|
||||
div a{color:green;}
|
||||
p a span{color:yellow;}
|
||||
.foo .bar .qux, .foo .baz .qux{display:block;}
|
||||
.foo .qux .bar, .foo .qux .baz{display:inline;}
|
||||
.foo .qux .bar .biz, .foo .qux .baz .biz{display:none;}
|
||||
32
lesscpy/test/css/strings.css
Normal file
32
lesscpy/test/css/strings.css
Normal file
@@ -0,0 +1,32 @@
|
||||
#strings {
|
||||
background-image: url("http://son-of-a-banana.com");
|
||||
quotes: "~" "~";
|
||||
content: "#*%:&^,)!.(~*})";
|
||||
empty: "";
|
||||
brackets: "{" "}";
|
||||
}
|
||||
#comments {
|
||||
content: "/* hello */ // not-so-secret";
|
||||
}
|
||||
#single-quote {
|
||||
quotes: "'" "'";
|
||||
content: '""#!&""';
|
||||
empty: '';
|
||||
semi-colon: ';';
|
||||
}
|
||||
#escaped {
|
||||
filter: DX.Transform.MS.BS.filter(opacity=50);
|
||||
}
|
||||
#one-line {
|
||||
image: url(http://tooks.com);
|
||||
}
|
||||
#interpolation {
|
||||
url: "http://lesscss.org/dev/image.jpg";
|
||||
url2: "http://lesscss.org/image-256.jpg";
|
||||
url3: "http://lesscss.org#445566";
|
||||
url4: "http://lesscss.org/hello";
|
||||
url5: "http://lesscss.org/54.4px";
|
||||
}
|
||||
.mix-mul-class {
|
||||
color: orange;
|
||||
}
|
||||
7
lesscpy/test/css/strings.min.css
vendored
Normal file
7
lesscpy/test/css/strings.min.css
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
#strings{background-image:url("http://son-of-a-banana.com");quotes:"~" "~";content:"#*%:&^,)!.(~*})";empty:"";brackets:"{" "}";}
|
||||
#comments{content:"/* hello */ // not-so-secret";}
|
||||
#single-quote{quotes:"'" "'";content:'""#!&""';empty:'';semi-colon:';';}
|
||||
#escaped{filter:DX.Transform.MS.BS.filter(opacity=50);}
|
||||
#one-line{image:url(http://tooks.com);}
|
||||
#interpolation{url:"http://lesscss.org/dev/image.jpg";url2:"http://lesscss.org/image-256.jpg";url3:"http://lesscss.org#445566";url4:"http://lesscss.org/hello";url5:"http://lesscss.org/54.4px";}
|
||||
.mix-mul-class{color:orange;}
|
||||
27
lesscpy/test/css/variables.css
Normal file
27
lesscpy/test/css/variables.css
Normal file
@@ -0,0 +1,27 @@
|
||||
.variables {
|
||||
width: 14cm;
|
||||
}
|
||||
.variables {
|
||||
height: 24px;
|
||||
color: #888888;
|
||||
font-family: "Trebuchet MS",Verdana,sans-serif;
|
||||
quotes: "~""~";
|
||||
}
|
||||
.redefinition {
|
||||
three: 3;
|
||||
}
|
||||
.values {
|
||||
font-family: 'Trebuchet', 'Trebuchet', 'Trebuchet';
|
||||
color: #888888 !important;
|
||||
url: url('Trebuchet');
|
||||
multi: something 'A',B,C, 'Trebuchet';
|
||||
}
|
||||
.variable-names {
|
||||
name: 'hello';
|
||||
}
|
||||
.alpha {
|
||||
filter: alpha(opacity=42);
|
||||
}
|
||||
.lazy-eval {
|
||||
width: 100%;
|
||||
}
|
||||
7
lesscpy/test/css/variables.min.css
vendored
Normal file
7
lesscpy/test/css/variables.min.css
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
.variables{width:14cm;}
|
||||
.variables{height:24px;color:#888888;font-family:"Trebuchet MS",Verdana,sans-serif;quotes:"~""~";}
|
||||
.redefinition{three:3;}
|
||||
.values{font-family:'Trebuchet', 'Trebuchet', 'Trebuchet';color:#888888 !important;url:url('Trebuchet');multi:something 'A',B,C, 'Trebuchet';}
|
||||
.variable-names{name:'hello';}
|
||||
.alpha{filter:alpha(opacity=42);}
|
||||
.lazy-eval{width:100%;}
|
||||
32
lesscpy/test/css/whitespace.css
Normal file
32
lesscpy/test/css/whitespace.css
Normal file
@@ -0,0 +1,32 @@
|
||||
.whitespace {
|
||||
color: white;
|
||||
}
|
||||
.whitespace {
|
||||
color: white;
|
||||
}
|
||||
.whitespace {
|
||||
color: white;
|
||||
}
|
||||
.whitespace {
|
||||
color: white;
|
||||
}
|
||||
.whitespace {
|
||||
color: white;
|
||||
}
|
||||
.white, .space, .mania {
|
||||
color: white;
|
||||
}
|
||||
.no-semi-column {
|
||||
color: white;
|
||||
}
|
||||
.no-semi-column {
|
||||
color: white;
|
||||
white-space: pre;
|
||||
}
|
||||
.no-semi-column {
|
||||
border: 2px solid white;
|
||||
}
|
||||
.newlines {
|
||||
background: the, great, wall;
|
||||
border: 2px solid black;
|
||||
}
|
||||
10
lesscpy/test/css/whitespace.min.css
vendored
Normal file
10
lesscpy/test/css/whitespace.min.css
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
.whitespace{color:white;}
|
||||
.whitespace{color:white;}
|
||||
.whitespace{color:white;}
|
||||
.whitespace{color:white;}
|
||||
.whitespace{color:white;}
|
||||
.white, .space, .mania{color:white;}
|
||||
.no-semi-column{color:white;}
|
||||
.no-semi-column{color:white;white-space:pre;}
|
||||
.no-semi-column{border:2px solid white;}
|
||||
.newlines{background:the, great, wall;border:2px solid black;}
|
||||
16
lesscpy/test/genast.py
Normal file
16
lesscpy/test/genast.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import sys, re
|
||||
sys.path.append('..')
|
||||
import lesscpy.lessc.parser
|
||||
|
||||
m = []
|
||||
|
||||
with open('../lessc/parser.py') as f:
|
||||
for l in f.readlines():
|
||||
r = re.findall('def[ \t]+(p_[a-z_0-9]+)([^\(]*)', l)
|
||||
if r: m.append(r[0][0])
|
||||
|
||||
p = lesscpy.lessc.parser.LessParser
|
||||
|
||||
for u in m:
|
||||
if u == 'p_error': continue
|
||||
print(getattr(p, u).__doc__)
|
||||
12
lesscpy/test/less/color-functions.less
Normal file
12
lesscpy/test/less/color-functions.less
Normal file
@@ -0,0 +1,12 @@
|
||||
.color_functions {
|
||||
lighten: lighten(#ff0000, 40%);
|
||||
darken: darken(#ff0000, 40%);
|
||||
saturate: saturate(#29332f, 20%);
|
||||
desaturate: desaturate(#203c31, 20%);
|
||||
greyscale: greyscale(#203c31);
|
||||
spin-p: spin(hsl(340, 50%, 50%), 40);
|
||||
spin-n: spin(hsl(30, 50%, 50%), -40);
|
||||
hue: hue(hsl(98, 12%, 95%));
|
||||
saturation: saturation(hsl(98, 12%, 95%));
|
||||
lightness: lightness(hsl(98, 12%, 95%));
|
||||
}
|
||||
52
lesscpy/test/less/colors.less
Normal file
52
lesscpy/test/less/colors.less
Normal file
@@ -0,0 +1,52 @@
|
||||
#yelow {
|
||||
#short {
|
||||
color: #fea;
|
||||
}
|
||||
#long {
|
||||
color: #ffeeaa;
|
||||
}
|
||||
#rgba {
|
||||
color: rgba(255, 238, 170, 0.1);
|
||||
}
|
||||
#argb {
|
||||
color: argb(rgba(255, 238, 170, 0.1));
|
||||
}
|
||||
}
|
||||
|
||||
#blue {
|
||||
#short {
|
||||
color: #00f;
|
||||
}
|
||||
#long {
|
||||
color: #0000ff;
|
||||
}
|
||||
#rgba {
|
||||
color: rgba(0, 0, 255, 0.1);
|
||||
}
|
||||
#argb {
|
||||
color: argb(rgba(0, 0, 255, 0.1));
|
||||
}
|
||||
}
|
||||
|
||||
#alpha #hsla {
|
||||
color: hsla(11, 20%, 20%, 0.6);
|
||||
}
|
||||
|
||||
#overflow {
|
||||
.a { color: #111111 - #444444; } // #000000
|
||||
.b { color: #eee + #fff; } // #ffffff
|
||||
.c { color: #aaa * 3; } // #ffffff
|
||||
.d { color: #00ee00 + #009900; } // #00ff00
|
||||
}
|
||||
|
||||
#grey {
|
||||
color: rgb(200, 200, 200);
|
||||
}
|
||||
|
||||
#808080 {
|
||||
color: hsl(50, 0%, 50%);
|
||||
}
|
||||
|
||||
#00ff00 {
|
||||
color: hsl(120, 100%, 50%);
|
||||
}
|
||||
65
lesscpy/test/less/comments.less
Normal file
65
lesscpy/test/less/comments.less
Normal file
@@ -0,0 +1,65 @@
|
||||
/******************\
|
||||
* *
|
||||
* Comment Header *
|
||||
* *
|
||||
\******************/
|
||||
|
||||
/*
|
||||
|
||||
Comment
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* Comment Test
|
||||
*
|
||||
* - cloudhead (http://cloudhead.net)
|
||||
*
|
||||
*/
|
||||
|
||||
////////////////
|
||||
@var: "content";
|
||||
////////////////
|
||||
|
||||
/* Colors
|
||||
* ------
|
||||
* #EDF8FC (background blue)
|
||||
* #166C89 (darkest blue)
|
||||
*
|
||||
* Text:
|
||||
* #333 (standard text) // A comment within a comment!
|
||||
* #1F9EC9 (standard link)
|
||||
*
|
||||
*/
|
||||
|
||||
/* @group Variables
|
||||
------------------- */
|
||||
#comments /* boo */ {
|
||||
/**/ // An empty comment
|
||||
color: red; /* A C-style comment */
|
||||
background-color: orange; // A little comment
|
||||
font-size: 12px;
|
||||
|
||||
/* lost comment */ content: @var;
|
||||
|
||||
border: 1px solid black;
|
||||
|
||||
// padding & margin //
|
||||
padding: 0; // }{ '"
|
||||
margin: 2em;
|
||||
} //
|
||||
|
||||
/* commented out
|
||||
#more-comments {
|
||||
color: grey;
|
||||
}
|
||||
*/
|
||||
|
||||
.selector /* .with */, .lots, /* of */ .comments {
|
||||
color: grey, /* blue */ orange;
|
||||
-webkit-border-radius: 2px /* webkit only */;
|
||||
-moz-border-radius: 2px * 4 /* moz only with operation */;
|
||||
}
|
||||
|
||||
#last { color: blue }
|
||||
//
|
||||
66
lesscpy/test/less/css-3.less
Normal file
66
lesscpy/test/less/css-3.less
Normal file
@@ -0,0 +1,66 @@
|
||||
.comma-delimited {
|
||||
background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg);
|
||||
text-shadow: -1px -1px 1px red, 6px 5px 5px yellow;
|
||||
-moz-box-shadow: 0pt 0pt 2px rgba(255, 255, 255, 0.4) inset,
|
||||
0pt 4px 6px rgba(255, 255, 255, 0.4) inset;
|
||||
}
|
||||
@font-face {
|
||||
font-family: Headline;
|
||||
src: local(Futura-Medium),
|
||||
url(fonts.svg#MyGeometricModern) format("svg");
|
||||
}
|
||||
.other {
|
||||
-moz-transform: translate(0, 11em) rotate(-90deg);
|
||||
}
|
||||
p:not([class*="lead"]) {
|
||||
color: black;
|
||||
}
|
||||
|
||||
input[type="text"].class#id[attr=32]:not(1) {
|
||||
color: white;
|
||||
}
|
||||
|
||||
div#id.class[a=1][b=2].class:not(1) {
|
||||
color: white;
|
||||
}
|
||||
|
||||
ul.comma > li:not(:only-child)::after {
|
||||
color: white;
|
||||
}
|
||||
|
||||
ol.comma > li:nth-last-child(2)::after {
|
||||
color: white;
|
||||
}
|
||||
|
||||
li:nth-child(4n+1),
|
||||
li:nth-child(-5n),
|
||||
li:nth-child(-n+2) {
|
||||
color: white;
|
||||
}
|
||||
|
||||
a[href^="http://"] {
|
||||
color: black;
|
||||
}
|
||||
|
||||
a[href$="http://"] {
|
||||
color: black;
|
||||
}
|
||||
|
||||
form[data-disabled] {
|
||||
color: black;
|
||||
}
|
||||
|
||||
p::before {
|
||||
color: black;
|
||||
}
|
||||
|
||||
#issue322 {
|
||||
-webkit-animation: anim2 7s infinite ease-in-out;
|
||||
}
|
||||
/*
|
||||
@-webkit-keyframes frames {
|
||||
0% { border: 1px }
|
||||
5.5% { border: 2px }
|
||||
100% { border: 3px }
|
||||
}
|
||||
*/
|
||||
23
lesscpy/test/less/css-escapes.less
Normal file
23
lesscpy/test/less/css-escapes.less
Normal file
@@ -0,0 +1,23 @@
|
||||
@ugly: fuchsia;
|
||||
|
||||
.escape\|random\|char {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.mixin\!tUp {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
// class="404"
|
||||
.\34 04 {
|
||||
background: red;
|
||||
|
||||
strong {
|
||||
color: @ugly;
|
||||
.mixin\!tUp;
|
||||
}
|
||||
}
|
||||
|
||||
.trailingTest\+ {
|
||||
color: red;
|
||||
}
|
||||
118
lesscpy/test/less/css.less
Normal file
118
lesscpy/test/less/css.less
Normal file
@@ -0,0 +1,118 @@
|
||||
@charset "utf-8";
|
||||
div { color: black; }
|
||||
div { width: 99%; }
|
||||
|
||||
* {
|
||||
min-width: 45em;
|
||||
}
|
||||
|
||||
h1, h2 > a > p, h3 {
|
||||
color: none;
|
||||
}
|
||||
|
||||
div.class {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
div#id {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.class#id {
|
||||
color: purple;
|
||||
}
|
||||
|
||||
.one.two.three {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.one .two .three {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
@media print {
|
||||
font-size: 3em;
|
||||
}
|
||||
|
||||
@media screen {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Garamond Pro';
|
||||
src: url("/fonts/garamond-pro.ttf");
|
||||
}
|
||||
|
||||
a:hover, a:link {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
p, p:first-child {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
q:lang(no) {
|
||||
quotes: none;
|
||||
}
|
||||
|
||||
p + h1 {
|
||||
font-size: 2.2em;
|
||||
}
|
||||
|
||||
#shorthands {
|
||||
border: 1px solid #000;
|
||||
font : 12px/16px Arial;
|
||||
margin: 1px 0;
|
||||
padding: 0 auto;
|
||||
background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px;
|
||||
}
|
||||
|
||||
#shorthands {
|
||||
font: 100%/16px Arial;
|
||||
}
|
||||
|
||||
#more-shorthands {
|
||||
margin: 0;
|
||||
padding: 1px 0 2px 0;
|
||||
font: normal small/20px 'Trebuchet MS', Verdana, sans-serif;
|
||||
}
|
||||
|
||||
.misc {
|
||||
-moz-border-radius: 2px;
|
||||
display: -moz-inline-stack;
|
||||
width: .1em;
|
||||
background-color: #009998;
|
||||
background-image: url(images/image.jpg);
|
||||
background: -webkit-gradient(linear, left top, left bottom, from(red), to(blue));
|
||||
margin: ;
|
||||
filter: alpha(opacity=100);
|
||||
}
|
||||
|
||||
#important {
|
||||
color: red !important;
|
||||
width: 100%!important;
|
||||
height: 20px ! important;
|
||||
}
|
||||
|
||||
#data-uri {
|
||||
background: url(data:image/png;charset=utf-8;base64,
|
||||
kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/
|
||||
k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U
|
||||
kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC);
|
||||
background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==);
|
||||
}
|
||||
|
||||
#svg-data-uri {
|
||||
background: transparent url('data:image/svg+xml, <svg version="1.1"><g></g></svg>');
|
||||
}
|
||||
|
||||
.uri_test {
|
||||
background-image: url(images/image.jpg);
|
||||
background-image: url(../some/path);
|
||||
background-image: url(./../some/path);
|
||||
background-image: url(./images/image.jpg);
|
||||
background-image: url(http://some/path/img.jpeg);
|
||||
background-image: url(https://some.server.com:9696/path/img.jpeg);
|
||||
behavior:url(border-radius.htc);
|
||||
background-image: url(fonts.svg#MyGeometricModern);
|
||||
}
|
||||
22
lesscpy/test/less/functions.less
Normal file
22
lesscpy/test/less/functions.less
Normal file
@@ -0,0 +1,22 @@
|
||||
#functions {
|
||||
@var: 10;
|
||||
width: increment(15);
|
||||
height: undefined("self");
|
||||
border-width: add(2, 3);
|
||||
variable: increment(@var);
|
||||
decrement: decrement(@var);
|
||||
}
|
||||
|
||||
#built-in {
|
||||
@r: 32;
|
||||
escaped: e("-Some::weird(#thing, y)");
|
||||
escaped1: ~"-Some::weird(#thing, z)";
|
||||
format: %("rgb(%d, %d, %d)", @r, 128, 64);
|
||||
format-string: %("hello %s", "world");
|
||||
format-multiple: %("hello %s %d", "earth", 2);
|
||||
format-url-encode: %('red is %A', #ff0000);
|
||||
eformat: e(%("rgb(%d, %d, %d)", @r, 128, 64));
|
||||
rounded: round(@r/3);
|
||||
roundedpx: round(10px / 3);
|
||||
}
|
||||
|
||||
12
lesscpy/test/less/ie-filters.less
Normal file
12
lesscpy/test/less/ie-filters.less
Normal file
@@ -0,0 +1,12 @@
|
||||
@fat: 0;
|
||||
@cloudhead: "#000000";
|
||||
|
||||
.nav {
|
||||
filter: ~'progid:DXImageTransform.Microsoft.gradient(startColorstr="#333333", endColorstr="@{cloudhead}", GradientType=@{fat})';
|
||||
}
|
||||
.nav {
|
||||
filter: ~"progid:DXImageTransform.Microsoft.Alpha(opacity=@{fat})";
|
||||
}
|
||||
.nav {
|
||||
filter: ~'progid:DXImageTransform.Microsoft.gradient(startColorstr="#333333", endColorstr="@{cloudhead}", GradientType=@{fat})';
|
||||
}
|
||||
10
lesscpy/test/less/import.less
vendored
Normal file
10
lesscpy/test/less/import.less
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
@import './imports/import.less';
|
||||
@import 'some.css.file.css';
|
||||
@import './imports/import.less'; // redundant
|
||||
@import './imports/import_f'; // No ext
|
||||
@import 'some/other.css.file.CSS';
|
||||
.import {
|
||||
.mixin;
|
||||
.mixf(6px);
|
||||
height: @imported;
|
||||
}
|
||||
1
lesscpy/test/less/imports/circular.less
Normal file
1
lesscpy/test/less/imports/circular.less
Normal file
@@ -0,0 +1 @@
|
||||
@import 'circular.less';
|
||||
5
lesscpy/test/less/imports/import.less
vendored
Normal file
5
lesscpy/test/less/imports/import.less
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
@imported: 9px;
|
||||
|
||||
.mixin {
|
||||
color: red;
|
||||
}
|
||||
6
lesscpy/test/less/imports/import_f.less
Normal file
6
lesscpy/test/less/imports/import_f.less
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
|
||||
*/
|
||||
.mixf (@var: 7px) {
|
||||
width: @var;
|
||||
}
|
||||
8
lesscpy/test/less/issues/inherit_scope.less
Normal file
8
lesscpy/test/less/issues/inherit_scope.less
Normal file
@@ -0,0 +1,8 @@
|
||||
@mix: none;
|
||||
.mixin {
|
||||
@mix: #989;
|
||||
}
|
||||
.tiny-scope {
|
||||
color: @mix; // #989
|
||||
.mixin;
|
||||
}
|
||||
26
lesscpy/test/less/issues/mixins-closure.less
Normal file
26
lesscpy/test/less/issues/mixins-closure.less
Normal file
@@ -0,0 +1,26 @@
|
||||
.scope {
|
||||
@var: 99px;
|
||||
.mixin () {
|
||||
width: @var;
|
||||
}
|
||||
}
|
||||
|
||||
.class {
|
||||
.scope > .mixin;
|
||||
}
|
||||
|
||||
.overwrite {
|
||||
@var: 0px;
|
||||
.scope > .mixin;
|
||||
}
|
||||
|
||||
.nested {
|
||||
@var: 5px;
|
||||
.mixin () {
|
||||
width: @var;
|
||||
}
|
||||
.class {
|
||||
@var: 10px;
|
||||
.mixin;
|
||||
}
|
||||
}
|
||||
30
lesscpy/test/less/issues/mixins-nested.less
Normal file
30
lesscpy/test/less/issues/mixins-nested.less
Normal file
@@ -0,0 +1,30 @@
|
||||
.nested-ruleset (@width: 200px) {
|
||||
width: @width;
|
||||
.column { margin: @width; }
|
||||
}
|
||||
.content {
|
||||
.nested-ruleset(600px);
|
||||
}
|
||||
|
||||
.mix-inner (@var) {
|
||||
border-width: @var;
|
||||
}
|
||||
|
||||
.mix (@a: 10) {
|
||||
.inner {
|
||||
height: @a * 10;
|
||||
|
||||
.innest {
|
||||
width: @a;
|
||||
.mix-inner(@a * 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.class {
|
||||
.mix(30);
|
||||
}
|
||||
|
||||
.class2 {
|
||||
.mix(60);
|
||||
}
|
||||
96
lesscpy/test/less/issues/mixins-pattern.less
Normal file
96
lesscpy/test/less/issues/mixins-pattern.less
Normal file
@@ -0,0 +1,96 @@
|
||||
.mixin () {
|
||||
zero: 0;
|
||||
}
|
||||
.mixin (@a: 1px) {
|
||||
one: 1;
|
||||
}
|
||||
.mixin (@a) {
|
||||
one-req: 1;
|
||||
}
|
||||
.mixin (@a: 1px, @b: 2px) {
|
||||
two: 2;
|
||||
}
|
||||
|
||||
.mixin (@a, @b, @c) {
|
||||
three-req: 3;
|
||||
}
|
||||
|
||||
.mixin (@a: 1px, @b: 2px, @c: 3px) {
|
||||
three: 3;
|
||||
}
|
||||
|
||||
.zero {
|
||||
.mixin();
|
||||
}
|
||||
|
||||
.one {
|
||||
.mixin(1);
|
||||
}
|
||||
|
||||
.two {
|
||||
.mixin(1, 2);
|
||||
}
|
||||
|
||||
.three {
|
||||
.mixin(1, 2, 3);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
.mixout ('left') {
|
||||
left: 1;
|
||||
}
|
||||
|
||||
.mixout ('right') {
|
||||
right: 1;
|
||||
}
|
||||
|
||||
.left {
|
||||
.mixout('left');
|
||||
}
|
||||
.right {
|
||||
.mixout('right');
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
.border (@side, @width) {
|
||||
color: black;
|
||||
.border-side(@side, @width);
|
||||
}
|
||||
.border-side (left, @w) {
|
||||
border-left: @w;
|
||||
}
|
||||
.border-side (right, @w) {
|
||||
border-right: @w;
|
||||
}
|
||||
|
||||
.border-right {
|
||||
.border(right, 4px);
|
||||
}
|
||||
.border-left {
|
||||
.border(left, 4px);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
|
||||
.border-radius (@r) {
|
||||
both: @r * 10;
|
||||
}
|
||||
.border-radius (@r, left) {
|
||||
left: @r;
|
||||
}
|
||||
.border-radius (@r, right) {
|
||||
right: @r;
|
||||
}
|
||||
|
||||
.only-right {
|
||||
.border-radius(33, right);
|
||||
}
|
||||
.only-left {
|
||||
.border-radius(33, left);
|
||||
}
|
||||
.left-right {
|
||||
.border-radius(33);
|
||||
}
|
||||
5
lesscpy/test/less/issues/plus_identifier.less
Normal file
5
lesscpy/test/less/issues/plus_identifier.less
Normal file
@@ -0,0 +1,5 @@
|
||||
#foo {
|
||||
+ .one {
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
25
lesscpy/test/less/media.less
Normal file
25
lesscpy/test/less/media.less
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
// For now, variables can't be declared inside @media blocks.
|
||||
|
||||
@var: 42;
|
||||
|
||||
@media print {
|
||||
.class {
|
||||
color: blue;
|
||||
.sub {
|
||||
width: @var;
|
||||
}
|
||||
}
|
||||
.top, header > h1 {
|
||||
color: #222 * 2;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen {
|
||||
@base: 8;
|
||||
body { max-width: @base * 60; }
|
||||
}
|
||||
|
||||
@media all and (orientation:portrait) {
|
||||
aside { float: none; }
|
||||
}
|
||||
110
lesscpy/test/less/mixins-args.less
Normal file
110
lesscpy/test/less/mixins-args.less
Normal file
@@ -0,0 +1,110 @@
|
||||
.mixin (@a: 1px, @b: 50%) {
|
||||
width: @a * 5;
|
||||
height: @b - 1%;
|
||||
}
|
||||
|
||||
.mixina (@style, @width, @color: black) {
|
||||
border: @width @style @color;
|
||||
}
|
||||
|
||||
.mixiny
|
||||
(@a: 0, @b: 0) {
|
||||
margin: @a;
|
||||
padding: @b;
|
||||
}
|
||||
|
||||
.hidden() {
|
||||
color: transparent; // asd
|
||||
}
|
||||
|
||||
#hidden {
|
||||
.hidden;
|
||||
.hidden();
|
||||
}
|
||||
|
||||
.two-args {
|
||||
color: blue;
|
||||
.mixin(2px, 100%);
|
||||
.mixina(dotted, 2px);
|
||||
}
|
||||
|
||||
.one-arg {
|
||||
.mixin(3px);
|
||||
}
|
||||
|
||||
.no-parens {
|
||||
.mixin;
|
||||
}
|
||||
|
||||
.no-args {
|
||||
.mixin();
|
||||
}
|
||||
|
||||
.var-args {
|
||||
@var: 9;
|
||||
.mixin(@var, @var * 2);
|
||||
}
|
||||
|
||||
.multi-mix {
|
||||
.mixin(2px, 30%);
|
||||
.mixiny(4, 5);
|
||||
}
|
||||
|
||||
.maxa(@arg1: 10, @arg2: #f00) {
|
||||
padding: @arg1 * 2px;
|
||||
color: @arg2;
|
||||
}
|
||||
|
||||
body {
|
||||
.maxa(15);
|
||||
}
|
||||
|
||||
@glob: 5;
|
||||
.global-mixin(@a:2) {
|
||||
width: @glob + @a;
|
||||
}
|
||||
|
||||
.scope-mix {
|
||||
.global-mixin(3);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
.same-var-name2(@radius) {
|
||||
radius: @radius;
|
||||
}
|
||||
.same-var-name(@radius) {
|
||||
.same-var-name2(@radius);
|
||||
}
|
||||
#same-var-name {
|
||||
.same-var-name(5px);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
.var-inside () {
|
||||
@var: 10px;
|
||||
width: @var;
|
||||
}
|
||||
#var-inside { .var-inside; }
|
||||
|
||||
// # mixins
|
||||
|
||||
/* #id-mixin () {
|
||||
color: red;
|
||||
} */
|
||||
.id-class {
|
||||
#id-mixin();
|
||||
#id-mixin;
|
||||
}
|
||||
|
||||
.mixin-arguments (@width: 0px) {
|
||||
border: @arguments;
|
||||
}
|
||||
|
||||
.arguments {
|
||||
.mixin-arguments(1px, solid, black);
|
||||
}
|
||||
.arguments2 {
|
||||
.mixin-arguments();
|
||||
}
|
||||
29
lesscpy/test/less/mixins-redundant.less
Normal file
29
lesscpy/test/less/mixins-redundant.less
Normal file
@@ -0,0 +1,29 @@
|
||||
.ext {
|
||||
color: red;
|
||||
}
|
||||
.ext {
|
||||
color: blue;
|
||||
}
|
||||
.ext {
|
||||
color: red;
|
||||
}
|
||||
.ext {
|
||||
color: black;
|
||||
}
|
||||
.ext {
|
||||
color: red;
|
||||
}
|
||||
.ext {
|
||||
color: green;
|
||||
}
|
||||
p {
|
||||
width: 1px;
|
||||
.ext;
|
||||
}
|
||||
a {
|
||||
.ext;
|
||||
.ext;
|
||||
.ext;
|
||||
.ext;
|
||||
.ext;
|
||||
}
|
||||
72
lesscpy/test/less/mixins.less
Normal file
72
lesscpy/test/less/mixins.less
Normal file
@@ -0,0 +1,72 @@
|
||||
.mixin { border: 1px solid black; }
|
||||
.mixout { border-color: orange; }
|
||||
.borders { border-style: dashed; }
|
||||
|
||||
#namespace {
|
||||
.borders {
|
||||
border-style: dotted;
|
||||
}
|
||||
.biohazard {
|
||||
content: "death";
|
||||
.man {
|
||||
color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#theme {
|
||||
> .mixin {
|
||||
background-color: grey;
|
||||
}
|
||||
}
|
||||
|
||||
#container {
|
||||
color: black;
|
||||
.mixin;
|
||||
.mixout;
|
||||
#theme > .mixin;
|
||||
}
|
||||
|
||||
#header {
|
||||
.milk {
|
||||
color: white;
|
||||
.mixin;
|
||||
#theme > .mixin;
|
||||
}
|
||||
#cookie {
|
||||
.chips {
|
||||
#namespace .borders;
|
||||
.calories {
|
||||
#container;
|
||||
}
|
||||
}
|
||||
.borders;
|
||||
}
|
||||
}
|
||||
.secure-zone { #namespace .biohazard .man; }
|
||||
|
||||
.direct {
|
||||
#namespace > .borders;
|
||||
}
|
||||
.bo, .bar {
|
||||
width: 100%;
|
||||
}
|
||||
.bo {
|
||||
border: 1px;
|
||||
}
|
||||
.bo {
|
||||
color: red;
|
||||
}
|
||||
.ar.bo.ca {
|
||||
color: black;
|
||||
}
|
||||
.jo.ki {
|
||||
background: none;
|
||||
}
|
||||
.extended {
|
||||
.bo;
|
||||
.jo.ki;
|
||||
}
|
||||
.foo .bar {
|
||||
.bar;
|
||||
}
|
||||
53
lesscpy/test/less/operations.less
Normal file
53
lesscpy/test/less/operations.less
Normal file
@@ -0,0 +1,53 @@
|
||||
#operations {
|
||||
color: #110000 + #000011 + #001100; // #111111
|
||||
height: 10px / 2px + 6px - 1px * 2; // 9px
|
||||
width: 2 * 4 - 5em; // 3em
|
||||
text-shadow: -1px -1px 1px red, 6px 5px 5px yellow;
|
||||
.spacing {
|
||||
height: 10px / 2px+6px- 1px*2;
|
||||
width: 2 * 4 - 5em;
|
||||
}
|
||||
substraction: 20 - 10 - 5 - 5; // 0
|
||||
division: 20 / 5 / 4; // 1
|
||||
}
|
||||
|
||||
@x: 4;
|
||||
@y: 12em;
|
||||
|
||||
.with-variables {
|
||||
height: @x + @y; // 16em
|
||||
width: 12 + @y; // 24em
|
||||
size: 5cm - @x; // 1cm
|
||||
}
|
||||
|
||||
@z: -2;
|
||||
|
||||
.negative {
|
||||
height: 2px + @z; // 0px
|
||||
width: 2px - @z; // 4px
|
||||
}
|
||||
|
||||
.shorthands {
|
||||
padding: -1px 2px 0 -4px; //
|
||||
}
|
||||
|
||||
.colors {
|
||||
color: #123; // #112233
|
||||
border-color: #234 + #111111; // #334455
|
||||
background-color: #222222 - #fff; // #000000
|
||||
.other {
|
||||
color: 2 * #111; // #222222
|
||||
border-color: #333333 / 3 + #111; // #222222
|
||||
}
|
||||
}
|
||||
|
||||
.negations {
|
||||
@var: 4px;
|
||||
variable: -@var; // 4
|
||||
variable1: -@var + @var; // 0
|
||||
variable2: @var + -@var; // 0
|
||||
variable3: @var - -@var; // 8
|
||||
variable4: -@var - -@var; // 0
|
||||
paren: -(@var); // -4px
|
||||
paren2: -(2 + 2) * -@var; // 16
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user