├── ckstyle
├── __init__.py
├── browsers
│ ├── __init__.py
│ ├── Detector.py
│ ├── BinaryRule.py
│ └── Analyser.py
├── command
│ ├── __init__.py
│ ├── index.py
│ ├── args.py
│ └── PluginManager.py
├── entity
│ ├── __init__.py
│ ├── NestedStatement.py
│ ├── ExtraStatement.py
│ ├── Rule.py
│ ├── EntityUtil.py
│ └── StyleSheet.py
├── plugins
│ ├── __init__.py
│ ├── combiners
│ │ ├── __init__.py
│ │ ├── Combiner.py
│ │ ├── helper.py
│ │ └── CombinerFactory.py
│ ├── validators
│ │ ├── __init__.py
│ │ ├── MarginValidator.py
│ │ └── ValidatorFactory.py
│ ├── FEDUnknownCssNameChecker.py
│ ├── FEDDistinguishBrowserRule.py
│ ├── FEDDistinguishBrowserRuleSet.py
│ ├── FEDSemicolonAfterValue.py
│ ├── FEDFixOutlineZero.py
│ ├── FEDShouldNotUseImportant.py
│ ├── FEDSelectorNoUnderLine.py
│ ├── FEDDistinguishBrowserExtra.py
│ ├── FEDNoEmptyRuleSet.py
│ ├── FEDNoAlphaImageLoader.py
│ ├── FEDCommentLengthLessThan80.py
│ ├── FEDUseSingleQuotation.py
│ ├── FEDUseValidValues.py
│ ├── FEDNoCommentInValues.py
│ ├── FEDTransChnFontFamilyNameIntoEng.py
│ ├── FEDDoNotSetStyleForTagOnly.py
│ ├── FEDCanNotSetFontFamily.py
│ ├── FEDUseLowerCaseSelector.py
│ ├── FEDHackAttributeInCorrectWay.py
│ ├── FEDZIndexShouldInRange.py
│ ├── FEDFixCommentInValue.py
│ ├── FEDMustContainAuthorInfo.py
│ ├── FEDReplaceBorderZeroWithBorderNone.py
│ ├── FEDNoAppearanceNameInSelector.py
│ ├── FEDNoStarInSelector.py
│ ├── FEDNoSimpleNumberInSelector.py
│ ├── Base.py
│ ├── FEDFixNestedStatement.py
│ ├── FEDSingleLineSelector.py
│ ├── FEDDoNotSetStyleForSimpleSelector.py
│ ├── FEDNoZeroBeforeDot.py
│ ├── FEDFontSizeShouldBePtOrPx.py
│ ├── FEDHackRuleSetInCorrectWay.py
│ ├── FEDNoExpression.py
│ ├── FEDSingleLineSpaces.py
│ ├── FEDSingleLineBraces.py
│ ├── FEDMultiLineBraces.py
│ ├── FEDUseLowerCaseProp.py
│ ├── FEDRemoveDuplicatedAttr.py
│ ├── FEDStyleShouldInOrder.py
│ ├── FEDUnknownHTMLTagName.py
│ ├── FEDNoUnitAfterZero.py
│ └── FEDMultiLineSpaces.py
├── reporter
│ ├── __init__.py
│ ├── Reporter.py
│ ├── XMLReporter.py
│ ├── ReporterUtil.py
│ ├── JsonReporter.py
│ ├── TextReporter.py
│ └── helper.py
├── cmdconsole
│ ├── __init__.py
│ └── ConsoleClass.py
├── cssparser
│ ├── __init__.py
│ └── helper.py
└── userplugins
│ ├── __init__.py
│ ├── commands
│ └── __init__.py
│ └── plugins
│ └── __init__.py
├── tests
├── unit
│ ├── __init__.py
│ ├── check
│ │ ├── __init__.py
│ │ ├── FEDNoEmptyFile.css
│ │ ├── FEDNoAlphaImageLoader.css
│ │ ├── FEDSelectorNoUnderLine.css
│ │ ├── FEDUseSingleQuotation.css
│ │ ├── FEDNoZeroBeforeDot.css
│ │ ├── FEDReplaceBorderZeroWithBorderNone.css
│ │ ├── FEDMustContainAuthorInfoAnotherOK.css
│ │ ├── FEDNoEmptyRuleSet.css
│ │ ├── FEDNoStarInSelector.css
│ │ ├── FEDSemicolonAfterValue.css
│ │ ├── helper.py
│ │ ├── FEDUnknownCssNameChecker.css
│ │ ├── demo.py
│ │ ├── FEDMustContainAuthorInfo.css
│ │ ├── FEDMustContainAuthorInfoAnother.css
│ │ ├── FEDShouldNotUseImportant.css
│ │ ├── FEDNoAppearanceNameInSelector.css
│ │ ├── FEDNoEmptyFile.py
│ │ ├── FEDTransChnFontFamilyNameIntoEng.css
│ │ ├── FED16ColorShouldUpper.css
│ │ ├── FEDDoNotSetStyleForTagOnly.css
│ │ ├── FEDUseLowerCaseSelector.css
│ │ ├── FEDCanNotSetFontFamily.css
│ │ ├── FEDDoNotSetStyleForSimpleSelector.css
│ │ ├── FEDHackAttributeInCorrectWay.css
│ │ ├── FEDSingleLineSelector.css
│ │ ├── FEDZIndexShouldInRange.css
│ │ ├── FEDNoUnitAfterZero.css
│ │ ├── FEDFontSizeShouldBePtOrPx.css
│ │ ├── FEDHackRuleSetInCorrectWay.css
│ │ ├── FEDNoSimpleNumberInSelector.css
│ │ ├── FEDUseLowerCaseProp.css
│ │ ├── FEDMultiLineBraces.css
│ │ ├── FEDSingleLineSpaces.css
│ │ ├── FEDCombineInToOne.css
│ │ ├── FEDCommentLengthLessThan80.css
│ │ ├── FEDNoExpression.css
│ │ ├── FEDMultiLineSpaces.css
│ │ ├── FEDMultiLineSelectors.css
│ │ ├── FEDStyleShouldInOrder.css
│ │ ├── FEDSingleLineBraces.css
│ │ └── FEDHighPerformanceSelector.css
│ ├── fix
│ │ ├── __init__.py
│ │ ├── FixFEDOutlineNone.py
│ │ ├── FixFEDCss3PropSpaces.py
│ │ ├── FixFEDNoEmptyRuleSet.py
│ │ ├── helper.py
│ │ ├── FixPropStartWithSharp.py
│ │ ├── Try.py
│ │ ├── FixRuleSetsFormat.py
│ │ ├── FixFEDCombineInToOne.py
│ │ ├── CommentForExtraStatement.py
│ │ ├── FixAllInSafeMode.py
│ │ ├── FixFEDUseLowerCaseSelector.py
│ │ ├── ExtraCss3Prefix.py
│ │ ├── FixFEDNoUnitAfterZero.py
│ │ ├── SoManyColors.py
│ │ ├── FEDCombineSameRuleSets.py
│ │ └── FixComplicatedStatement.py
│ ├── hacks
│ │ ├── __init__.py
│ │ ├── helper.py
│ │ ├── HackRuleSets.py
│ │ ├── HackExtras.py
│ │ └── HackRules.py
│ ├── order
│ │ ├── __init__.py
│ │ ├── helper.py
│ │ ├── Try.py
│ │ └── InAMessOrder.py
│ ├── todos
│ │ ├── __init__.py
│ │ ├── TryParseKeyFrames.py
│ │ └── helper.py
│ ├── combiner
│ │ ├── __init__.py
│ │ ├── _just_margin.css
│ │ ├── _just_padding.css
│ │ ├── _test.css
│ │ ├── helper.py
│ │ ├── _test_different_order.css
│ │ ├── _with_margin.css
│ │ ├── _with_padding.css
│ │ └── Try.py
│ ├── compress
│ │ ├── __init__.py
│ │ ├── _browsers_importanter_hacks.css
│ │ ├── _expression.css
│ │ ├── _one_line_file.css
│ │ ├── _extra_statement.css
│ │ ├── _browsers.css
│ │ ├── _compress_special_hack_chars.css
│ │ ├── _with_extra.css
│ │ ├── _browsers_combine_ruleset.css
│ │ ├── _file.css
│ │ ├── CompressNestedStatement.py
│ │ ├── CompressCssCompiler.py
│ │ ├── helper.py
│ │ ├── _selectors_from_kimblim.css
│ │ ├── CompressWithOrder.py
│ │ └── CompressFile.py
│ ├── config
│ │ ├── __init__.py
│ │ ├── ckstyle_missing.ini
│ │ ├── ckstyle_with_plugin.ini
│ │ ├── ckstyle_configed.ini
│ │ ├── ckstyle.ini
│ │ ├── helper.py
│ │ └── WithPlugin.py
│ ├── entity
│ │ ├── __init__.py
│ │ ├── helper.py
│ │ ├── StyleSheet.py
│ │ ├── Rule.py
│ │ ├── ExtraStatement.py
│ │ └── RuleSet.py
│ ├── parser
│ │ ├── __init__.py
│ │ ├── helper.py
│ │ ├── ParserIgnoreSpeicalStatement.css
│ │ ├── NestedStatement.py
│ │ └── ExtraStatement.py
│ ├── tellme
│ │ └── __init__.py
│ ├── commandline
│ │ ├── __init__.py
│ │ ├── _test.css
│ │ ├── _test_browsers.css
│ │ ├── helper.py
│ │ └── Try.py
│ └── asserts.py
├── experiment
│ ├── test.css
│ ├── outer
│ │ ├── outer.css
│ │ └── inner
│ │ │ ├── inner.css
│ │ │ └── inner2.css
│ ├── go.bat
│ ├── python.py
│ ├── xxx.ini
│ └── test.py
├── go.bat
├── demo
│ ├── demo-compress.min.css
│ ├── demo-ckstyle-step1.css
│ ├── demo-ckstyle-step2.css
│ ├── demo-ckstyle-step3.css
│ └── demo-compress.css
└── test.css
├── go.bat
├── utils
├── go.bat
├── python_gflags-2.0-py3.1.egg.rar
├── test.py
├── combineRules.py
└── exportRules.py
├── bin
├── ckstyle-admin.py
├── compress-admin.py
├── fixstyle-admin.py
├── ckstyle.bat
├── compress.bat
└── fixstyle.bat
├── editor-plugins
├── fixstyle for EditPlus.mkd
├── fixstyle for notepad++.mkd
├── fixstyle_for_sublime_text_2.zip
├── fixstyle_for_sublime_text_2
│ ├── Default (OSX).sublime-keymap
│ ├── Default (Linux).sublime-keymap
│ ├── Default (Windows).sublime-keymap
│ ├── Preferences.sublime-settings
│ ├── helper.py
│ ├── Context.sublime-menu
│ ├── README.md
│ ├── CkStylePlugin.py
│ ├── Main.sublime-menu
│ ├── FixStylePlugin.py
│ ├── CssCompressPlugin.py
│ ├── FixStyleSafePlugin.py
│ └── FixStyleSingleLinePlugin.py
└── fixstyle.vim
├── doc
└── index.html
├── .gitignore
├── ckstyle.ini
└── LICENSE
/ckstyle/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ckstyle/browsers/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ckstyle/command/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ckstyle/entity/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ckstyle/plugins/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ckstyle/reporter/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/experiment/test.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/check/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/fix/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/hacks/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/order/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/todos/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ckstyle/cmdconsole/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ckstyle/cssparser/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ckstyle/userplugins/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/combiner/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/compress/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/config/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/entity/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/parser/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/tellme/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ckstyle/plugins/combiners/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ckstyle/plugins/validators/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/commandline/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ckstyle/userplugins/commands/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ckstyle/userplugins/plugins/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/go.bat:
--------------------------------------------------------------------------------
1 | python setup.py install
2 | python tests/runUnitTests.py
3 |
--------------------------------------------------------------------------------
/tests/unit/commandline/_test.css:
--------------------------------------------------------------------------------
1 | .test {
2 | width: 100px;
3 | }
--------------------------------------------------------------------------------
/tests/experiment/outer/outer.css:
--------------------------------------------------------------------------------
1 | fdsa {
2 | width:100px;
3 | }
4 |
--------------------------------------------------------------------------------
/tests/go.bat:
--------------------------------------------------------------------------------
1 | python ../setup.py install
2 | python runUnitTests.py
3 |
--------------------------------------------------------------------------------
/tests/experiment/outer/inner/inner.css:
--------------------------------------------------------------------------------
1 | width {
2 | width: 100px;
3 | }
4 |
--------------------------------------------------------------------------------
/tests/experiment/outer/inner/inner2.css:
--------------------------------------------------------------------------------
1 | width {
2 | width: 100px;
3 | }
4 |
--------------------------------------------------------------------------------
/tests/experiment/go.bat:
--------------------------------------------------------------------------------
1 | python ../../setup.py install
2 | python ../runUnitTests.py
3 |
--------------------------------------------------------------------------------
/tests/unit/todos/TryParseKeyFrames.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | pass
5 |
--------------------------------------------------------------------------------
/utils/go.bat:
--------------------------------------------------------------------------------
1 | python ../setup.py install
2 | python ../tests/runUnitTests.py
3 | python .\exportRules.py
4 |
--------------------------------------------------------------------------------
/ckstyle/plugins/combiners/Combiner.py:
--------------------------------------------------------------------------------
1 | class Combiner():
2 | def combine(self, name, attrs):
3 | pass
4 |
--------------------------------------------------------------------------------
/bin/ckstyle-admin.py:
--------------------------------------------------------------------------------
1 | from ckstyle.command.index import ckstyle
2 |
3 | if __name__ == '__main__':
4 | ckstyle()
5 |
--------------------------------------------------------------------------------
/bin/compress-admin.py:
--------------------------------------------------------------------------------
1 | from ckstyle.command.index import compress
2 |
3 | if __name__ == '__main__':
4 | compress()
5 |
--------------------------------------------------------------------------------
/bin/fixstyle-admin.py:
--------------------------------------------------------------------------------
1 | from ckstyle.command.index import fixstyle
2 |
3 | if __name__ == '__main__':
4 | fixstyle()
5 |
--------------------------------------------------------------------------------
/tests/unit/commandline/_test_browsers.css:
--------------------------------------------------------------------------------
1 | .test {
2 | width: 100px;
3 | }
4 |
5 |
6 | .test[prop] {
7 | width: 100px;
8 | }
--------------------------------------------------------------------------------
/utils/python_gflags-2.0-py3.1.egg.rar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjeaf/CSSCheckStyle/HEAD/utils/python_gflags-2.0-py3.1.egg.rar
--------------------------------------------------------------------------------
/editor-plugins/fixstyle for EditPlus.mkd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjeaf/CSSCheckStyle/HEAD/editor-plugins/fixstyle for EditPlus.mkd
--------------------------------------------------------------------------------
/doc/index.html:
--------------------------------------------------------------------------------
1 | 请查看README,或移步 https://github.com/wangjeaf/CSSCheckStyle-docs
2 |
--------------------------------------------------------------------------------
/editor-plugins/fixstyle for notepad++.mkd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjeaf/CSSCheckStyle/HEAD/editor-plugins/fixstyle for notepad++.mkd
--------------------------------------------------------------------------------
/ckstyle/plugins/combiners/helper.py:
--------------------------------------------------------------------------------
1 | def containsHack(name, strippedName, value):
2 | return name != strippedName or value.find('\9') != -1
3 |
--------------------------------------------------------------------------------
/editor-plugins/fixstyle_for_sublime_text_2.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjeaf/CSSCheckStyle/HEAD/editor-plugins/fixstyle_for_sublime_text_2.zip
--------------------------------------------------------------------------------
/tests/unit/compress/_browsers_importanter_hacks.css:
--------------------------------------------------------------------------------
1 | .test[fd*=df], .test:not(xxx) {
2 | width:100px;
3 | }
4 |
5 | .test[fd~=df] {
6 | width:100px;
7 | }
--------------------------------------------------------------------------------
/editor-plugins/fixstyle_for_sublime_text_2/Default (OSX).sublime-keymap:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "keys": ["ctrl+f1"], "command": "fixstyle"
4 | }
5 | ]
6 |
--------------------------------------------------------------------------------
/editor-plugins/fixstyle_for_sublime_text_2/Default (Linux).sublime-keymap:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "keys": ["ctrl+f1"], "command": "fixstyle"
4 | }
5 | ]
6 |
--------------------------------------------------------------------------------
/editor-plugins/fixstyle_for_sublime_text_2/Default (Windows).sublime-keymap:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "keys": ["ctrl+f1"], "command": "fixstyle"
4 | }
5 | ]
6 |
--------------------------------------------------------------------------------
/tests/experiment/python.py:
--------------------------------------------------------------------------------
1 | import os
2 | homedir = os.getenv('USERPROFILE') or os.getenv('HOME')
3 | print os.path.realpath(os.path.join(homedir, 'ckstyle.ini'))
4 |
--------------------------------------------------------------------------------
/tests/demo/demo-compress.min.css:
--------------------------------------------------------------------------------
1 | .test1,.test2,.test3,.test4,.test5{*display:none;_display:inline-block;width:100px;height:200px;margin:20px 10px 10px;border:1px solid #FFF}
2 |
--------------------------------------------------------------------------------
/tests/unit/config/ckstyle_missing.ini:
--------------------------------------------------------------------------------
1 | [ckstyle]
2 | tab-spaces = 2
3 | standard = standard3.css
4 | fixed-extension = .fixed2.css
5 |
6 | [compress]
7 | extension = .lala.min.css
8 |
--------------------------------------------------------------------------------
/utils/test.py:
--------------------------------------------------------------------------------
1 | plugin = __import__("ckstyle.plugins.AllRules", fromlist = ['AllRules'])
2 | props = dir(plugin)
3 | for prop in props:
4 | if prop.startswith('FED'):
5 | print prop
--------------------------------------------------------------------------------
/tests/test.css:
--------------------------------------------------------------------------------
1 | .test {
margin-left: 1px;
margin-right: 1px;
margin-top: 0px;
margin-bottom: 0;
padding-top: 0px;
padding-bottom: 0;
padding-right: 0;
padding-left: 0;
}
--------------------------------------------------------------------------------
/tests/demo/demo-ckstyle-step1.css:
--------------------------------------------------------------------------------
1 | /* @author : zhifu.wang */
2 |
3 | .test1 ul li a {
4 | width:10px;
5 | color:#ffffff;
6 | -webkit-border-radius:3px;
7 | -moz-border-radius : 3px;
8 | border-radius:3px
9 | }
10 |
--------------------------------------------------------------------------------
/editor-plugins/fixstyle_for_sublime_text_2/Preferences.sublime-settings:
--------------------------------------------------------------------------------
1 | // Settings in here override those in "Default/Preferences.sublime-settings", and
2 | // are overridden in turn by file type specific settings.
3 | {
4 | }
5 |
--------------------------------------------------------------------------------
/ckstyle/reporter/Reporter.py:
--------------------------------------------------------------------------------
1 | class Reporter():
2 | def __init__(self, checker):
3 | pass
4 | def doReport(self):
5 | pass
6 | def appendMsg(self):
7 | pass
8 | def export(self):
9 | pass
10 |
--------------------------------------------------------------------------------
/tests/unit/compress/_expression.css:
--------------------------------------------------------------------------------
1 | *html .feed-comment textarea {
2 | behavior: expression(function(ele){
3 | ele.runtimeStyle.behavior='none';
4 | Expressions.pseudo.hover(ele, 'textarea_hover');
5 | }(this));
6 | }
7 |
--------------------------------------------------------------------------------
/tests/unit/compress/_one_line_file.css:
--------------------------------------------------------------------------------
1 | /**
2 | * @author zhifu.wang
3 | */
4 |
5 | .test { width: 100px; height: 200px; _z-index: 111; } @keyframes 'name' { 10% {width: 100px;} } .another { *width: 100px; color: #DDDDDD; background-color: #aabbcc; }
6 |
--------------------------------------------------------------------------------
/tests/unit/fix/FixFEDOutlineNone.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _try()
5 |
6 | def _try():
7 | fixer, msg = doFix('.test {outline:none;}', '')
8 | equal(msg, '''.test {
9 | outline: 0;
10 | }''', 'outline fix ok')
11 |
--------------------------------------------------------------------------------
/bin/ckstyle.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | rem Use a full path to Python (relative to this script) as the standard Python
4 | rem install does not put python.exe on the PATH...
5 | rem %~dp0 is the directory of this script
6 |
7 | "%~dp0..\python" "%~dp0ckstyle-admin.py" %*
8 |
--------------------------------------------------------------------------------
/bin/compress.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | rem Use a full path to Python (relative to this script) as the standard Python
4 | rem install does not put python.exe on the PATH...
5 | rem %~dp0 is the directory of this script
6 |
7 | "%~dp0..\python" "%~dp0compress-admin.py" %*
8 |
--------------------------------------------------------------------------------
/bin/fixstyle.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | rem Use a full path to Python (relative to this script) as the standard Python
4 | rem install does not put python.exe on the PATH...
5 | rem %~dp0 is the directory of this script
6 |
7 | "%~dp0..\python" "%~dp0fixstyle-admin.py" %*
8 |
--------------------------------------------------------------------------------
/tests/experiment/xxx.ini:
--------------------------------------------------------------------------------
1 | [config]
2 | error-level = 0
3 | include = all
4 | exclude = none
5 | recursive = false
6 | print-flag = false
7 | extension = .ckstyle2.txt
8 | tab-spaces = 4
9 | standard = standard.css
10 | ignore-rule-sets = @unit-test-expecteds
11 |
--------------------------------------------------------------------------------
/tests/unit/config/ckstyle_with_plugin.ini:
--------------------------------------------------------------------------------
1 | [ckstyle]
2 | tab-spaces = 2
3 | standard = standard3.css
4 | fixed-extension = .fixed2.css
5 |
6 | [compress]
7 | extension = .lala.min.css
8 |
9 | [plugin]
10 | plugin-a-config = 1
11 | plugin-b-config = 2
12 | pluginCConfig = 3
--------------------------------------------------------------------------------
/tests/unit/order/helper.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | sys.path.insert(0, os.path.realpath(os.path.join(__file__, '../../')))
4 | for p in os.environ.get('PYTHONPATH', '').split(';'):
5 | sys.path.append(p)
6 |
7 | from asserts import *
8 | from ckstyle.doCssFix import doFix
9 |
--------------------------------------------------------------------------------
/tests/unit/todos/helper.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | sys.path.insert(0, os.path.realpath(os.path.join(__file__, '../../')))
4 | for p in os.environ.get('PYTHONPATH', '').split(';'):
5 | sys.path.append(p)
6 |
7 | from asserts import *
8 | from ckstyle.doCssFix import doFix
9 |
--------------------------------------------------------------------------------
/tests/unit/compress/_extra_statement.css:
--------------------------------------------------------------------------------
1 | @-css-compiler {
2 | selector-compile: no-combinator;
3 | rule-compile: all;
4 | }
5 |
6 | @charset utf-8;
7 |
8 | @-css-compiler-xxx fdasfdas;
9 |
10 | @import url(fdafdas/fdafdas.css)
11 |
--------------------------------------------------------------------------------
/tests/demo/demo-ckstyle-step2.css:
--------------------------------------------------------------------------------
1 | /* @author : zhifu.wang */
2 |
3 | .test-special-word a.a-class {
4 | width:10px;
5 | color:#FFFFFF;
6 | -webkit-border-radius: 3px;
7 | -moz-border-radius: 3px;
8 | -o-border-radius: 3px;
9 | border-radius: 3px;
10 | }
11 |
--------------------------------------------------------------------------------
/tests/demo/demo-ckstyle-step3.css:
--------------------------------------------------------------------------------
1 | /* @author : zhifu.wang */
2 |
3 | .test-special-word .a-class {
4 | width: 10px;
5 | color: #FFF;
6 | -webkit-border-radius: 3px;
7 | -moz-border-radius: 3px;
8 | -o-border-radius: 3px;
9 | border-radius: 3px;
10 | }
11 |
--------------------------------------------------------------------------------
/tests/unit/parser/helper.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | sys.path.insert(0, os.path.realpath(os.path.join(__file__, '../../')))
4 | for p in os.environ.get('PYTHONPATH', '').split(';'):
5 | sys.path.append(p)
6 |
7 | from asserts import *
8 | from ckstyle.cssparser.CssFileParser import CssParser
9 |
--------------------------------------------------------------------------------
/tests/unit/compress/_browsers.css:
--------------------------------------------------------------------------------
1 | *html a {
2 | width: 100px;
3 | }
4 |
5 | *+html a {
6 | width: 200px;
7 | }
8 |
9 | a {
10 | -webkit-transform: 1s;
11 | -moz-transform: 1s;
12 | -o-transform: 1s;
13 | }
14 |
15 | b {
16 | -moz-transform: 1s;
17 | width: 300px;
18 | _width: 400px;
19 | }
--------------------------------------------------------------------------------
/tests/unit/compress/_compress_special_hack_chars.css:
--------------------------------------------------------------------------------
1 | li:nth-child(even) {
2 | background:gray;
3 | }
4 | * html li.even {
5 | background:gray;
6 | }
7 | .test[^=aaa] {
8 | background:gray;
9 | }
10 |
11 | .test1 {
12 | width: 100px;
13 | }
14 | .test2 {
15 | width: 100px;
16 | }
17 |
--------------------------------------------------------------------------------
/ckstyle/reporter/XMLReporter.py:
--------------------------------------------------------------------------------
1 | from .Reporter import Reporter
2 |
3 | class XMLReporter(Reporter):
4 | def __init__(self, checker):
5 | pass
6 |
7 | def doReport(self):
8 | pass
9 |
10 | def appendMsg(self, msg):
11 | pass
12 |
13 | def export(self):
14 | pass
15 |
--------------------------------------------------------------------------------
/tests/unit/compress/_with_extra.css:
--------------------------------------------------------------------------------
1 | /**
2 | * @author zhifu.wang
3 | */
4 |
5 | @charset utf-8;
6 |
7 | @import url('xxxxx');
8 | @namespace lalala;
9 |
10 | .test {
11 | width: 100px;
12 | height: 200px;
13 | _z-index: 111;
14 | }
15 |
16 | @import url('xxx2');
17 |
18 | @import url('xxx3');
19 |
--------------------------------------------------------------------------------
/tests/unit/compress/_browsers_combine_ruleset.css:
--------------------------------------------------------------------------------
1 | a {
2 | -webkit-transform: 1s;
3 | -moz-transform: 1s;
4 | -o-transform: 1s;
5 | width: 300px;
6 | }
7 |
8 | b {
9 | -moz-transform: 1s;
10 | width: 300px;
11 | _width: 400px;
12 | }
13 |
14 | d {
15 | -moz-transform: 1s;
16 | width: 300px;
17 | _width: 400px;
18 | }
--------------------------------------------------------------------------------
/tests/unit/hacks/helper.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | sys.path.insert(0, os.path.realpath(os.path.join(__file__, '../../')))
4 | for p in os.environ.get('PYTHONPATH', '').split(';'):
5 | sys.path.append(p)
6 |
7 | from asserts import *
8 | from ckstyle.browsers.BinaryRule import *
9 | from ckstyle.browsers.Hacks import *
10 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDNoEmptyFile.css:
--------------------------------------------------------------------------------
1 | /**
2 | * @author : zhifu.wang@renren-inc.com
3 | */
4 |
5 | /* because this is an ruleSet, so the error will not occur..., by python! */
6 | @unit-test-expecteds {
7 | }
8 |
9 | /* comment 1 */
10 | /* comment 2 */
11 | /*
12 | .test-b { width: 100px;
13 | }
14 |
15 | .test-d {
16 | width: 100px;
17 | }*/
18 |
19 |
--------------------------------------------------------------------------------
/tests/unit/compress/_file.css:
--------------------------------------------------------------------------------
1 | /**
2 | * @author zhifu.wang
3 | */
4 |
5 | .test {
6 | width: 100px;
7 | height: 200px;
8 | _z-index: 111;
9 | }
10 |
11 | @keyframes 'name' {
12 | 10% {
13 | width: 100px;
14 | }
15 | }
16 |
17 | .another {
18 | *width: 100px;
19 | color: #DDDDDD;
20 | background-color: #aabbcc;
21 | }
22 |
--------------------------------------------------------------------------------
/tests/unit/compress/CompressNestedStatement.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _go()
5 |
6 | def _go():
7 | msg = doCssCompress('@media print{/* Hide the cursor when printing */.CodeMirror div.CodeMirror-cursor{visibility:hidden}}')
8 | equal(msg, '@media print{.CodeMirror div.CodeMirror-cursor{visibility:hidden}}', 'nested statement compress is ok')
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.py[co]
2 | *.swp
3 | *.tmp
4 |
5 | # Packages
6 | *.egg
7 | *.egg-info
8 | dist
9 | build
10 | eggs
11 | parts
12 | var
13 | sdist
14 | develop-eggs
15 | .installed.cfg
16 |
17 | # Installer logs
18 | pip-log.txt
19 |
20 | # Unit test / coverage reports
21 | .coverage
22 | .tox
23 |
24 | #Translations
25 | *.mo
26 |
27 | #Mr Developer
28 | .mr.developer.cfg
29 |
--------------------------------------------------------------------------------
/editor-plugins/fixstyle_for_sublime_text_2/helper.py:
--------------------------------------------------------------------------------
1 |
2 | import os
3 | import sys
4 |
5 | def getCkstylePath():
6 | cmdPath = 'ckstyle'
7 | if sys.platform == 'linux2' or sys.platform == 'darwin':
8 | returnValue = os.popen3('which ckstyle')
9 | returnValue = returnValue[1].read() + returnValue[2].read()
10 | cmdPath = returnValue
11 | return cmdPath
--------------------------------------------------------------------------------
/tests/unit/parser/ParserIgnoreSpeicalStatement.css:
--------------------------------------------------------------------------------
1 | @unit-test-expecteds {
2 | 0: should add @author in the head of "ParserIgnoreSpeicalStatement.css"
3 | }
4 |
5 | /* should not affect the parser */
6 | @import url(lfjdalkfda);
7 | @charset fjdaslkfjd;
8 | @-css-compiler {
9 | fdas: fdas;
10 | }
11 | @-css-compiler-fdas fdkal;
12 | /* test */
13 | .fda {
14 | width: 100px;
15 | }
16 |
--------------------------------------------------------------------------------
/ckstyle/plugins/validators/MarginValidator.py:
--------------------------------------------------------------------------------
1 | class MarginValidator():
2 | def __init__(self, name, value):
3 | self.name = name
4 | self.value = value
5 |
6 | def validate(self):
7 | realValues = [x for x in self.value.split(' ') if x != '']
8 | if len(realValues) > 4:
9 | return False, 'value of margin is too much(items > 4)'
10 | return True, ''
11 |
--------------------------------------------------------------------------------
/ckstyle/reporter/ReporterUtil.py:
--------------------------------------------------------------------------------
1 | from .TextReporter import TextReporter
2 | from .JsonReporter import JsonReporter
3 | from .XMLReporter import XMLReporter
4 |
5 | class ReporterUtil():
6 | @staticmethod
7 | def getReporter(reporterType, checker):
8 | if reporterType == 'text':
9 | return TextReporter(checker)
10 | elif reporterType == 'json':
11 | return JsonReporter(checker)
12 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDNoAlphaImageLoader.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 1: should not use AlphaImageLoader in ".test"
6 | 1: value of "background-image" should use lower case, in ".test"
7 | }
8 |
9 | .test {
10 | background-image: filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='http://a.xnimg.cn/img/pop_dialog_top_left.png',sizingMethod='crop');
11 | }
12 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDSelectorNoUnderLine.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 1: should not use _ in selector ".selector-with-_"
6 |
7 | 1: ".selector-with-no-underscore, .selector-with-_" contains the same rules in "FEDSelectorNoUnderLine.css"
8 | }
9 |
10 |
11 | .selector-with-no-underscore {
12 | width: 100px;
13 | }
14 |
15 | .selector-with-_ {
16 | width: 100px;
17 | }
18 |
--------------------------------------------------------------------------------
/tests/unit/fix/FixFEDCss3PropSpaces.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | fixer, msg = doFix('.test {-webkit-border-radius: 3px;-moz-border-radius:3px;border-radius:3px;}', '')
5 | equal(msg, '.test {\n -webkit-border-radius: 3px;\n -moz-border-radius: 3px;\n border-radius: 3px;\n}', 'ok')
6 |
7 | fixer, msg = doFix('.test {border-radius:3px;}', '')
8 | equal(msg, '.test {\n border-radius: 3px;\n}', 'ok')
9 |
--------------------------------------------------------------------------------
/tests/unit/entity/helper.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | sys.path.insert(0, os.path.realpath(os.path.join(__file__, '../../')))
4 | for p in os.environ.get('PYTHONPATH', '').split(';'):
5 | sys.path.append(p)
6 |
7 | from asserts import *
8 |
9 |
10 | from ckstyle.entity.Rule import Rule
11 | from ckstyle.entity.RuleSet import RuleSet
12 | from ckstyle.entity.StyleSheet import StyleSheet
13 | from ckstyle.entity.ExtraStatement import ExtraStatement
14 |
--------------------------------------------------------------------------------
/tests/unit/combiner/_just_margin.css:
--------------------------------------------------------------------------------
1 | .test {
2 | margin-left: 10px;
3 | margin-bottom: 10px;
4 | margin-right: 10px;
5 | margin-top: 20px;
6 | }
7 |
8 | .test2 {
9 | margin: 0 1px 2px 3px;
10 | margin-left: 10px;
11 | margin-bottom: 10px;
12 | margin-right: 10px;
13 | margin-top: 20px;
14 | }
15 |
16 | .test3 {
17 | margin: 20px 10px 10px;
18 | }
19 | .test4 {
20 | margin: 10px;
21 | margin-top: 20px;
22 | }
23 |
--------------------------------------------------------------------------------
/tests/unit/combiner/_just_padding.css:
--------------------------------------------------------------------------------
1 | .test {
2 | padding-left: 10px;
3 | padding-bottom: 10px;
4 | padding-right: 10px;
5 | padding-top: 20px;
6 | }
7 |
8 | .test2 {
9 | padding: 0 1px 2px 3px;
10 | padding-left: 10px;
11 | padding-bottom: 10px;
12 | padding-right: 10px;
13 | padding-top: 20px;
14 | }
15 |
16 | .test3 {
17 | padding: 20px 10px 10px;
18 | }
19 | .test4 {
20 | padding: 10px;
21 | padding-top: 20px;
22 | }
23 |
--------------------------------------------------------------------------------
/tests/unit/config/ckstyle_configed.ini:
--------------------------------------------------------------------------------
1 | [ckstyle]
2 | error-level = 2
3 | include = abc
4 | exclude = ddd
5 | recursive = true
6 | print-flag = true
7 | extension = .ckstyle2.txt
8 | tab-spaces = 2
9 | standard = standard2.css
10 | ignore-rule-sets = @unit-test-expecteds,@unit-tests-fda
11 | fix-to-single-line = true
12 | safe-mode = true
13 | no-bak = true
14 |
15 | [compress]
16 | extension = .min3.css
17 | combine-file = true
18 | browsers = ie6,ie7,std
19 | no-bak = true
20 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDUseSingleQuotation.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 1: replace " with ' in ".with-double-quotation"
6 | 0: can not set font-family for ".with-double-quotation"
7 | 0: can not set font-family for ".with-single-quotation"
8 | }
9 |
10 |
11 | .with-double-quotation {
12 | font-family: "Microsoft Yahei";
13 | }
14 |
15 | .with-single-quotation {
16 | font-family: 'microsoft yahei';
17 | }
18 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDNoZeroBeforeDot.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 1: zero should be removed when meet 0.xxx in ".test"
6 | 1: zero should be removed when meet 0.xxx in ".test-another"
7 | 1: zero should be removed when meet 0.xxx in ".test-padding"
8 | }
9 |
10 | .test {
11 | width: 0.1px;
12 | }
13 |
14 | .test-another {
15 | width: 0.001px;
16 | }
17 |
18 | .test-padding {
19 | padding: 1px 0.2px;
20 | }
21 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDReplaceBorderZeroWithBorderNone.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 1: replace "border: 0" with "border: none" in ".test-border-zero"
6 | 1: replace "border-width: 0" with "border-width: none" in ".test-border-width-zero"
7 | }
8 |
9 | .test-border-none {
10 | border: none;
11 | }
12 |
13 | .test-border-zero {
14 | border: 0;
15 | }
16 |
17 | .test-border-width-zero {
18 | border-width: 0;
19 | }
20 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDMustContainAuthorInfoAnotherOK.css:
--------------------------------------------------------------------------------
1 | /** zhifu.wang@renren-inc.com
2 | */
3 | @unit-test-expecteds {
4 | 1: ".test, .test-b, .test-c, .test-d" contains the same rules in "FEDMustContainAuthorInfoAnotherOK.css"
5 | }
6 |
7 | .test {
8 | width: 100px;
9 | }
10 |
11 | /* comment 1*/
12 | /* comment 2*/
13 | .test-b {
14 | width: 100px;
15 | }
16 |
17 | /* fa */ /* fda */
18 | .test-c {
19 | width: 100px;
20 | }
21 |
22 | .test-d {
23 | width: 100px;
24 | }
25 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDNoEmptyRuleSet.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 2: find css comment (/* */) in ".testb"
6 | 0: empty ruleset found ".test"
7 | 0: empty ruleset found ".testa"
8 | 0: empty ruleset found ".testb"
9 | 1: ".test, .testa, .testb" contains the same rules in "FEDNoEmptyRuleSet.css"
10 | }
11 |
12 | .test {
13 | }
14 |
15 | .testa {
16 |
17 |
18 | }
19 |
20 | .testb {
21 |
22 | /***/
23 | }
24 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDNoStarInSelector.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 0: please remove low performance selector "*" from "* > div"
6 | 0: please remove low performance selector "*" from "div *"
7 | 0: do not use low performance selector ">" in "* > div"
8 | 1: "* > div, div *" contains the same rules in "FEDNoStarInSelector.css"
9 | }
10 |
11 | * > div {
12 | width: 100px;
13 | }
14 |
15 | div * {
16 | width: 100px;
17 | }
18 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDSemicolonAfterValue.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 1: each rule in ".abcd" need semicolon in the end, "border" has not
6 | 1: each rule in ".abcd" need semicolon in the end, "width" has not
7 | 1: each rule in ".abcd:hover" need semicolon in the end, "height" has not
8 | }
9 |
10 | .abcd {
11 | width: 0
12 | border: 1px solid red
13 | }
14 |
15 | .abcd:hover {
16 | width: 0;
17 | height: 200px
18 | }
19 |
--------------------------------------------------------------------------------
/ckstyle.ini:
--------------------------------------------------------------------------------
1 | [ckstyle]
2 | error-level = 2
3 | include = all
4 | exclude = none
5 | recursive = false
6 | print-flag = false
7 | extension = .ckstyle.txt
8 | standard = standard.css
9 | ignore-rule-sets = @unit-test-expecteds
10 | fixed-extension = .fixed.css
11 |
12 | [compress]
13 | extension = .min.css
14 | combine-file = false
15 | browsers = false
16 |
17 | # TODOs
18 | [css-format]
19 | tab-spaces = 4
20 |
21 | [global-selectors]
22 | .nav, sidebar2 = home-frame2.css
23 |
24 | [plugin]
25 | demo = 111
26 |
--------------------------------------------------------------------------------
/tests/unit/config/ckstyle.ini:
--------------------------------------------------------------------------------
1 | [ckstyle]
2 | error-level = 0
3 | include = all
4 | exclude = none
5 | recursive = false
6 | print-flag = false
7 | extension = .ckstyle.txt
8 | fixed-extension = .fixed.css
9 | standard = standard.css
10 | ignore-rule-sets = @unit-test-expecteds
11 | fix-to-single-line = false
12 | safe-mode = false
13 | no-bak = false
14 |
15 | [compress]
16 | extension = .min.css
17 | combine-file = false
18 | browsers = none
19 | no-bak = false
20 |
21 | [global-files]
22 | .nav, sidebar2 = home-frame2.css
23 |
--------------------------------------------------------------------------------
/tests/unit/check/helper.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | sys.path.insert(0, os.path.realpath(os.path.join(__file__, '../../')))
4 | for p in os.environ.get('PYTHONPATH', '').split(';'):
5 | sys.path.append(p)
6 |
7 | from asserts import *
8 | from ckstyle.doCssCheck import doCheck
9 |
10 | def doCssCheck(fileContent, level = 2):
11 | checker = doCheck(fileContent)
12 | return checker.errors()
13 |
14 | def doCssTextCheck(text, fileName = ''):
15 | checker = doCheck(text, fileName)
16 | return checker.errors()
17 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDUnknownCssNameChecker.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 0: unknown attribute name "a" found in ".has-a"
6 | }
7 |
8 | .has-a {
9 | a: 1px solid red;
10 | }
11 |
12 | .behavior {
13 | behavior: test;
14 | }
15 |
16 | .khtml {
17 | -webkit-border-radius: 3px;
18 | -khtml-border-radius: 3px;
19 | -moz-border-radius: 3px;
20 | -ms-border-radius: 3px;
21 | -o-border-radius: 3px;
22 | border-radius: 3px;
23 | }
24 |
--------------------------------------------------------------------------------
/tests/unit/combiner/_test.css:
--------------------------------------------------------------------------------
1 | /* @author wangjeaf */
2 |
3 | @import (url-here);
4 |
5 | .test {
6 | _width: 100px;
7 | *height: 100px;
8 | }
9 |
10 | .test2 {
11 | _width: 100px;
12 | *height: 100px;
13 | }
14 | .test3 {
15 | _width: 100px;
16 | *height: 100px;
17 | }
18 |
19 | .test4 {
20 | _width: 100px;
21 | *height: 100px;
22 | }
23 |
24 | .test5 {
25 | *height: 100px;
26 | _width: 100px;
27 | }
28 |
29 | .test6 {
30 | _width: 100px;
31 | *height: 100px;
32 | display:none;
33 | }
34 |
--------------------------------------------------------------------------------
/tests/unit/fix/FixFEDNoEmptyRuleSet.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | fixer, msg = doFix('.test {} .test2 {} .test3 {width:100px;} .test4 {}', '')
5 |
6 | styleSheet = fixer.getStyleSheet()
7 | equal(len(styleSheet.getRuleSets()), 1, 'one ruleset')
8 | ruleSet = styleSheet.getRuleSets()[0]
9 | equal(ruleSet.selector, '.test3', 'it is test3')
10 | width = ruleSet.getRuleByName('width')
11 | equal(width.fixedValue, "100px", 'width is fixed')
12 | equal(width.value, '100px', 'value of width is origin')
13 |
--------------------------------------------------------------------------------
/tests/unit/check/demo.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 | from ckstyle.reporter.helper import fill
3 |
4 | def doTest():
5 | text = 'body {width: 1px}'
6 | logs, warns, errors = doCssCheck(text)
7 | equal(len(logs), 2, 'two logs')
8 | equal(len(warns), 1, 'one warn happened')
9 | equal(len(errors), 1, 'one error happened')
10 | equal(fill(warns[0]), r'each rule in "body" need semicolon in the end, "width" has not', 'warn rule text is ok')
11 | equal(fill(errors[0]), r'should not set style for html tag in "body"', 'error rule text is ok')
12 |
--------------------------------------------------------------------------------
/editor-plugins/fixstyle_for_sublime_text_2/Context.sublime-menu:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "caption": "Fixstyle",
4 | "command": "fixstyle"
5 | },
6 | {
7 | "caption": "FixstyleSafe",
8 | "command": "fixstylesafe"
9 | },
10 | {
11 | "caption": "FixstyleSingleLine",
12 | "command": "fixstylesingleline"
13 | },
14 | {
15 | "caption": "CkStyle",
16 | "command": "ckstyle"
17 | },
18 | {
19 | "caption": "CssCompress",
20 | "command": "csscompress"
21 | }
22 | ]
23 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDMustContainAuthorInfo.css:
--------------------------------------------------------------------------------
1 | @unit-test-expecteds {
2 | 0: should add @author in the head of "FEDMustContainAuthorInfo.css"
3 | 1: ".test, .test-b, .test-c, .test-d" contains the same rules in "FEDMustContainAuthorInfo.css"
4 | }
5 |
6 | .test {
7 | width: 100px;
8 | }
9 |
10 | /** * @author : zhifu.wang@renren-inc.com
11 | */
12 | /* comment 1*/
13 | /* comment 2*/
14 | .test-b {
15 | width: 100px;
16 | }
17 |
18 | /* fa */ /* fda */
19 | .test-c {
20 | width: 100px;
21 | }
22 |
23 | .test-d {
24 | width: 100px;
25 | }
26 |
--------------------------------------------------------------------------------
/tests/unit/fix/helper.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | sys.path.insert(0, os.path.realpath(os.path.join(__file__, '../../')))
4 | for p in os.environ.get('PYTHONPATH', '').split(';'):
5 | sys.path.append(p)
6 |
7 | from asserts import *
8 | from ckstyle.doCssFix import doFix
9 | import ckstyle.command.args as args
10 | defaultConfig = args.CommandArgs()
11 |
12 | def getFixed(css, name):
13 | fixer, msg = doFix(css, '')
14 |
15 | ruleSet = fixer.getStyleSheet().getRuleSets()[0]
16 | rule = ruleSet.getRuleByName(name)
17 | return rule.fixedValue
18 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDMustContainAuthorInfoAnother.css:
--------------------------------------------------------------------------------
1 | /** zhifu.wang@renren-inc.co
2 | */
3 | @unit-test-expecteds {
4 | 0: should add @author in the head of "FEDMustContainAuthorInfoAnother.css"
5 | 1: ".test, .test-b, .test-c, .test-d" contains the same rules in "FEDMustContainAuthorInfoAnother.css"
6 | }
7 |
8 | .test {
9 | width: 100px;
10 | }
11 |
12 | /* comment 1*/
13 | /* comment 2*/
14 | .test-b {
15 | width: 100px;
16 | }
17 |
18 | /* fa */ /* fda */
19 | .test-c {
20 | width: 100px;
21 | }
22 |
23 | .test-d {
24 | width: 100px;
25 | }
26 |
--------------------------------------------------------------------------------
/ckstyle/browsers/Detector.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 | from .BinaryRule import *
4 | from .Hacks import doRuleDetect, doRuleSetDetect, doExtraDetect
5 |
6 | class Browser():
7 |
8 | @staticmethod
9 | def handleRule(rule):
10 | rule.browser = doRuleDetect(rule.fixedName, rule.fixedValue)
11 |
12 | @staticmethod
13 | def handleRuleSet(ruleSet):
14 | ruleSet.browser = doRuleSetDetect(ruleSet.selector)
15 |
16 | @staticmethod
17 | def handleNestedStatement(statement):
18 | statement.browser = doExtraDetect(statement.selector)
--------------------------------------------------------------------------------
/tests/unit/check/FEDShouldNotUseImportant.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 0: Should not use !important in "width" of ".test"
6 | 0: Should not use !important in "width" of ".testa"
7 | 0: Should not use !important in "width" of ".testb"
8 |
9 | 1: ".test, .testa" contains the same rules in "FEDShouldNotUseImportant.css"
10 | }
11 |
12 | .test {
13 | width: 100px !important;
14 | }
15 |
16 | .testa {
17 | width: 100px !important;
18 | }
19 |
20 | .testb {
21 | width: 100px ! important;
22 | }
23 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDNoAppearanceNameInSelector.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 1: should not use appearance word "red" in ".color-red"
6 | 1: should not use appearance word "float" in ".float-left"
7 | 1: ".float-left, .topic" contains the same rules in "FEDNoAppearanceNameInSelector.css"
8 | }
9 |
10 | .is-correct-name {
11 | width: 100px;
12 | height: 200px;
13 | }
14 |
15 | .color-red {
16 | color: red;
17 | }
18 |
19 | .float-left {
20 | float: left;
21 | }
22 |
23 | .topic {
24 | float: left;
25 | }
26 |
--------------------------------------------------------------------------------
/utils/combineRules.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 | import os
4 | import json
5 |
6 | def loadPlugins(pluginDir):
7 | ids = []
8 | content = ''
9 | '''从plugins目录动态载入检查类'''
10 | for filename in os.listdir(pluginDir):
11 | if not filename.endswith('.py') or filename.startswith('_'):
12 | continue
13 | if filename == 'Base.py' or filename == 'helper.py':
14 | continue
15 | content = content + open(pluginDir + '/' + filename, 'r').read()
16 |
17 | open('AllRules.py', 'w').write(content);
18 |
19 | loadPlugins('../ckstyle/plugins')
--------------------------------------------------------------------------------
/tests/unit/check/FEDNoEmptyFile.py:
--------------------------------------------------------------------------------
1 | from asserts import ok, equal, notEqual, getResults
2 | from helper import doCssCheck, doCssTextCheck
3 | from ckstyle.reporter.helper import fill
4 |
5 | def doTest():
6 | logs, warns, errors = doCssTextCheck('/* @author: zhifu.wang**/ /* .test {width: 100px;}*/', 'test.css')
7 | equal(len(errors), 1, 'one error occur')
8 | equal(fill(errors[0]), 'empty css file "test.css"')
9 |
10 | logs, warns, errors = doCssTextCheck('/* @author: zhifu.wang**/ /* .test {width: 100px;}*/ \n.test { width: 100px; }', 'test.css')
11 | equal(len(errors), 0, 'no error now')
12 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDUnknownCssNameChecker.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | from .helper import isCssProp
6 |
7 | class FEDUnknownCssNameChecker(RuleChecker):
8 |
9 | '''{
10 | "summary":"错误的css属性",
11 | "desc":"本工具会帮您查找错误的CSS属性,如果写错了,即可收到错误提示"
12 | }'''
13 |
14 | def __init__(self):
15 | self.id = 'unknown-css-prop'
16 | self.errorLevel = ERROR_LEVEL.ERROR
17 | self.errorMsg = 'unknown attribute name "${name}" found in "${selector}"'
18 |
19 | def check(self, rule, config):
20 | return isCssProp(rule.name.lower())
21 |
--------------------------------------------------------------------------------
/tests/unit/asserts.py:
--------------------------------------------------------------------------------
1 | results = []
2 |
3 | def ok(expected, msg = 'ok'):
4 | results.append([expected, msg])
5 |
6 | def equal(expected, actual, msg = 'ok'):
7 | if expected == actual:
8 | ok(True, msg)
9 | else:
10 | ok(False, msg + ' (expect: %s, actual: %s)' % (expected, actual))
11 |
12 | def notEqual(expected, actual, msg = 'ok'):
13 | if expected != actual:
14 | ok(True, msg)
15 | else:
16 | ok(False, msg + ' (%s is equal to %s)' % (expected, actual))
17 |
18 | def getResults():
19 | global results
20 | final = results
21 | results = []
22 | return final
23 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDTransChnFontFamilyNameIntoEng.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 0: can not set font-family for ".test-chn"
6 | 0: can not set font-family for ".test-chn-font"
7 | 0: can not set font-family for ".test-eng"
8 | 0: should not use chinese font family name in ".test-chn"
9 | 0: should not use chinese font family name in ".test-chn-font"
10 | }
11 |
12 | .test-chn {
13 | font-family: '宋体';
14 | }
15 |
16 | .test-chn-font {
17 | font: italic bold 12px/30px '宋体', '黑体';
18 | }
19 |
20 | .test-eng {
21 | font-family: sarif;
22 | }
23 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDDistinguishBrowserRule.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | import string
6 | from ckstyle.browsers.Detector import Browser
7 |
8 | class FEDDistinguishBrowserRule(RuleChecker):
9 |
10 | '''{
11 | "summary":"在属性级别区分浏览器",
12 | "desc":"目的是针对不同的浏览器,生成不同的CSS"
13 | }'''
14 |
15 | def __init__(self):
16 | self.id = 'rule-for-browsers'
17 | self.errorLevel = ERROR_LEVEL.ERROR
18 | self.errorMsg = ''
19 |
20 | def check(self, rule, config):
21 | return True
22 |
23 | def fix(self, rule, config):
24 | Browser.handleRule(rule)
--------------------------------------------------------------------------------
/editor-plugins/fixstyle_for_sublime_text_2/README.md:
--------------------------------------------------------------------------------
1 | ##ckstyle plugin for Sublime Text 2
2 |
3 | ### 安装说明
4 | * 利用Sublime Text菜单 `references --> Browse Packages...` 找到Sublime Text 2的插件目录,例如:`C:\Documents and Settings\Administrator\Application Data\Sublime Text 2\Packages`
5 | * 将 `fixstyle_for_sublime_text_2.zip` 内的内容解压缩到插件目录下即可
6 |
7 | ### 命令说明
8 |
9 | * `Fixstyle` 为样式修复
10 | * `FixstyleSingleLine` 修复成单行模式
11 | * `FixstyleSafe` 安全模式修复
12 | * `CkStyle` 检查代码问题
13 | * `CssCompress` 为代码压缩
14 |
15 | ### 使用方法
16 |
17 | * Fixstyle快捷键:`Ctrl + F1` ,其他功能无快捷键
18 |
19 | * 菜单项:`tools->Fixstyle` `tools->CssCompress` 等
20 |
21 | * 右键菜单 `Fixstyle` `CssCompress` 等
22 |
--------------------------------------------------------------------------------
/tests/unit/check/FED16ColorShouldUpper.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 1: replace "#DDDDDD" with "#DDD" in ".test"
6 | 1: replace "#AABBCC" with "#ABC" in ".test-c"
7 | 1: wrong color length(should be 3 or 6) in ".test-wrong-len"
8 | 1: color should in upper case in ".test-lower"
9 | }
10 |
11 | .test-lower {
12 | color: #fff;
13 | }
14 |
15 | .test {
16 | color: #DDDDDD;
17 | }
18 |
19 | .test-a {
20 | color: #DDD;
21 | }
22 |
23 | .test-b {
24 | color: #ABC;
25 | }
26 |
27 | .test-c {
28 | color: #AABBCC;
29 | }
30 |
31 | .test-wrong-len {
32 | color: #DDFDD;
33 | }
34 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDDistinguishBrowserRuleSet.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | import string
6 | from ckstyle.browsers.Detector import Browser
7 |
8 | class FEDDistinguishBrowserRuleSet(RuleSetChecker):
9 |
10 | '''{
11 | "summary":"在规则集级别区分浏览器",
12 | "desc":"目的是针对不同的浏览器,生成不同的CSS规则集"
13 | }'''
14 |
15 | def __init__(self):
16 | self.id = 'ruleset-for-browsers'
17 | self.errorLevel = ERROR_LEVEL.ERROR
18 | self.errorMsg = ''
19 |
20 | def check(self, ruleSet, config):
21 | return True
22 |
23 | def fix(self, ruleSet, config):
24 | Browser.handleRuleSet(ruleSet)
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDSemicolonAfterValue.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDSemicolonAfterValue(RuleChecker):
7 |
8 | '''{
9 | "summary":"为每一个属性后添加分号",
10 | "desc":"按照CSS编码规范,每一个规则后面都必须加上分号 ;"
11 | }'''
12 |
13 | def __init__(self):
14 | self.id = 'add-semicolon'
15 | self.errorLevel = ERROR_LEVEL.WARNING
16 | self.errorMsg = 'each rule in "${selector}" need semicolon in the end, "${name}" has not'
17 |
18 | def check(self, rule, config):
19 | if not rule.roughValue.strip().endswith(';'):
20 | return False
21 | return True
22 |
--------------------------------------------------------------------------------
/tests/experiment/test.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from ckstyle.CssCheckerWrapper import doCheck
3 | from ckstyle.reporter.ReporterUtil import ReporterUtil
4 | from ckstyle.cssparser.CssFileParser import CssParser
5 | from ckstyle.entity.StyleSheet import StyleSheet
6 |
7 | def checkCssFileByOpm(filePath):
8 | fileContent = open(filePath).read()
9 | checker = doCheck(fileContent, filePath)
10 | if checker.hasError():
11 | reporter = ReporterUtil.getReporter('text', checker)
12 | reporter.doReport()
13 | print reporter.export()
14 | return False
15 | return True
16 |
17 |
18 | if __name__ == '__main__':
19 | checkCssFileByOpm('test.css')
20 |
--------------------------------------------------------------------------------
/tests/unit/order/Try.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | fixer, msg = doFix('.test {width:100px; display:none;}', '')
5 |
6 | styleSheet = fixer.getStyleSheet()
7 | equal(len(styleSheet.getRuleSets()), 1, 'one ruleset')
8 | ruleSet = styleSheet.getRuleSets()[0]
9 | equal(ruleSet.selector, '.test', 'it is the selector that i need')
10 |
11 | rules = ruleSet.getRules()
12 | equal(rules[0].name, 'display', 'first element is display now')
13 | equal(rules[1].name, 'width', 'second element is width')
14 |
15 | equal(rules[0].value, 'none', 'first element value is ok')
16 | equal(rules[1].value, '100px', 'second element value is ok')
17 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDFixOutlineZero.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDFixOutlineZero(RuleChecker):
7 |
8 | '''{
9 | "summary":"修复outline:none",
10 | "desc":"outline:none 和 outline:0 实现了相同的功能,但是后者的代码更简洁,便于压缩。"
11 | }'''
12 |
13 | def __init__(self):
14 | self.id = 'outline-zero'
15 | self.errorLevel = ERROR_LEVEL.WARNING
16 | self.errorMsg = ''
17 |
18 | def check(self, rule, config):
19 | return True
20 |
21 | def fix(self, rule, config):
22 | if rule.name == 'outline' and rule.fixedValue == 'none':
23 | rule.fixedValue = '0'
--------------------------------------------------------------------------------
/tests/unit/fix/FixPropStartWithSharp.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | go()
5 |
6 | def go():
7 | css = '''.theme-hot li {
8 | float: left;
9 | padding: 0 10px 0 2px;
10 | #padding: 1px 10px 0 2px;
11 | padding: 1px 10px 0 2px\\0;
12 | _padding: 3px 10px 0 2px;
13 | }'''
14 |
15 | expectedFixed = '''.theme-hot li {
16 | float: left;
17 | padding: 0 10px 0 2px;
18 | #padding: 1px 10px 0 2px;
19 | padding: 1px 10px 0 2px\\0;
20 | _padding: 3px 10px 0 2px;
21 | }'''
22 |
23 | fixer, msg = doFix(css, '')
24 | equal(msg.strip(), expectedFixed.strip(), '#padding is ok, do not combine attr which in hack')
25 |
26 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDDoNotSetStyleForTagOnly.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 0: should not set style for html tag in "div"
6 | 0: should not set style for html tag in "div, .class"
7 | 0: should not set style for html tag in "div, html"
8 | 1: "div, div, .class, div, html, .test div, div:hover" contains the same rules in "FEDDoNotSetStyleForTagOnly.css"
9 | }
10 |
11 | div {
12 | width: 100px;
13 | }
14 |
15 | div,
16 | .class {
17 | width: 100px;
18 | }
19 |
20 | div,
21 | html {
22 | width: 100px;
23 | }
24 |
25 | .test div {
26 | width: 100px;
27 | }
28 |
29 | div:hover {
30 | width: 100px;
31 | }
32 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDShouldNotUseImportant.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDShouldNotUseImportant(RuleChecker):
7 |
8 | '''{
9 | "summary":"不要使用!important",
10 | "desc":"CSS中不要使用!important"
11 | }'''
12 |
13 | def __init__(self):
14 | self.id = 'do-not-use-important'
15 | self.errorLevel = ERROR_LEVEL.ERROR
16 | self.errorMsg = 'Should not use !important in "${name}" of "${selector}"'
17 |
18 | def check(self, rule, config):
19 | value = rule.value
20 | if value.replace(' ', '').find('!important') != -1:
21 | return False
22 | return True
23 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDSelectorNoUnderLine.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDSelectorNoUnderLine(RuleSetChecker):
7 |
8 | '''{
9 | "summary":"不要在选择器中使用下划线",
10 | "desc":"在selector中不要使用下划线 _ ,可以使用中划线 -"
11 | }'''
12 |
13 | def __init__(self):
14 | self.id = 'no-underline-in-selector'
15 | self.errorLevel = ERROR_LEVEL.WARNING
16 | self.errorMsg = 'should not use _ in selector "${selector}"'
17 |
18 | def check(self, ruleSet, config):
19 | selector = ruleSet.selector
20 | if selector.find('_') != -1:
21 | return False
22 | return True
23 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDUseLowerCaseSelector.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 1: selector should use lower case, in ".IS-UPPER"
6 | 1: should not use _ in selector ".is_uppeR"
7 | 1: selector should use lower case, in ".is_uppeR"
8 | 1: selector should use lower case, in ".AAA, .BBB"
9 |
10 | 1: ".IS-UPPER, .is_uppeR, .AAA, .BBB" contains the same rules in "FEDUseLowerCaseSelector.css"
11 | }
12 |
13 | .is-correct-lower-selector {
14 | width: 100px;
15 | height: 200px;
16 | }
17 |
18 | .IS-UPPER {
19 | width: 100px;
20 | }
21 |
22 | .is_uppeR {
23 | width: 100px;
24 | }
25 |
26 | .AAA,
27 | .BBB {
28 | width: 100px;
29 | }
30 |
--------------------------------------------------------------------------------
/tests/unit/fix/Try.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | fixer, msg = doFix('.test {width:"100px";color:#DDDDDD;}', '')
5 |
6 | styleSheet = fixer.getStyleSheet()
7 | equal(len(styleSheet.getRuleSets()), 1, 'one ruleset')
8 | ruleSet = styleSheet.getRuleSets()[0]
9 | equal(ruleSet.selector, '.test', 'it is the selector that i need')
10 | width = ruleSet.getRuleByName('width')
11 | equal(width.fixedValue, "'100px'", 'width is fixed')
12 | equal(width.value, '"100px"', 'value of width is origin')
13 |
14 | color = ruleSet.getRuleByName('color')
15 | equal(color.fixedValue, '#DDD', 'color is fixed')
16 | equal(color.value, '#DDDDDD', 'value of color is origin')
17 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDCanNotSetFontFamily.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 0: can not set font-family for ".set-font-family"
6 | 0: can not set font-family for ".set-by-font"
7 | 0: can not set font-family for ".set-by-font-but-no-comma"
8 | 0: can not set font-family for ".set-by-font-but-no-comma-another"
9 | }
10 |
11 | .set-font-family {
12 | font-family: 'arial,sans-serif';
13 | }
14 |
15 | .set-by-font {
16 | font: italic bold 12px/30px Georgia, serif;
17 | }
18 |
19 | .set-by-font-but-no-comma {
20 | font: italic bold 12px/30px Georgia;
21 | }
22 |
23 | .set-by-font-but-no-comma-another {
24 | font: italic bold Georgia;
25 | }
26 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDDistinguishBrowserExtra.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | import string
6 | from ckstyle.browsers.Detector import Browser
7 |
8 | class FEDDistinguishBrowserExtra(ExtraChecker):
9 |
10 | '''{
11 | "summary":"嵌套规则区分浏览器",
12 | "desc":"目的是针对不同的浏览器,生成不同的CSS规则集"
13 | }'''
14 |
15 | def __init__(self):
16 | self.id = 'extra-for-browsers'
17 | self.errorLevel = ERROR_LEVEL.ERROR
18 | self.errorMsg = ''
19 |
20 | def check(self, ruleSet, config):
21 | return True
22 |
23 | def fix(self, ruleSet, config):
24 | if not ruleSet.nested:
25 | return
26 | Browser.handleNestedStatement(ruleSet)
--------------------------------------------------------------------------------
/tests/unit/compress/CompressCssCompiler.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _no_space()
5 | _has_space()
6 | _just_prefix()
7 |
8 | def _no_space():
9 | msg = doCssCompress('@-css-compiler{selector-compile:no-combinator;rule-compile:all}html{width:100px;}')
10 | equal(msg, 'html{width:100px}', '@css-compiler compressed')
11 |
12 | def _has_space():
13 | msg = doCssCompress('@-css-compiler {selector-compile:no-combinator;rule-compile:all}html{width:100px;}')
14 | equal(msg, 'html{width:100px}', '@css-compiler compressed')
15 |
16 | def _just_prefix():
17 | msg = doCssCompress('@-css-compiler-prefix fdsafdsafdsa;html{width:100px;}')
18 | equal(msg, 'html{width:100px}', '@css-compiler compressed')
--------------------------------------------------------------------------------
/tests/unit/combiner/helper.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | sys.path.insert(0, os.path.realpath(os.path.join(__file__, '../../')))
4 | for p in os.environ.get('PYTHONPATH', '').split(';'):
5 | sys.path.append(p)
6 |
7 | from asserts import *
8 | from ckstyle.doCssCompress import doCompress
9 |
10 | def doCssCompress(fileContent, fileName = ''):
11 | checker, compressed = doCompress(fileContent, fileName)
12 | return compressed
13 |
14 | def realpath(filepath):
15 | dirpath = os.path.realpath(os.path.join(__file__, '../'))
16 | path = os.path.join(dirpath, filepath)
17 | return path
18 |
19 | def doCssFileCompress(path):
20 | fileContent = open(realpath(path), 'r').read()
21 | return doCssCompress(fileContent, path)
22 |
--------------------------------------------------------------------------------
/tests/unit/config/helper.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | sys.path.insert(0, os.path.realpath(os.path.join(__file__, '../../')))
4 | for p in os.environ.get('PYTHONPATH', '').split(';'):
5 | sys.path.append(p)
6 |
7 | from asserts import *
8 | import ckstyle.command.CommandFileParser as CommandFileParser
9 | from ckstyle.command.ConsoleCommandParser import parseCkStyleCmdArgs, parseCompressCmdArgs, parseFixStyleCmdArgs
10 |
11 | def realpath(filepath):
12 | dirpath = os.path.realpath(os.path.join(__file__, '../'))
13 | path = os.path.join(dirpath, filepath)
14 | return path
15 |
16 | def parseConfigFile(path):
17 | parser = CommandFileParser.CommandFileParser(realpath(path), True)
18 | config = parser.args
19 | return config
20 |
--------------------------------------------------------------------------------
/tests/unit/fix/FixRuleSetsFormat.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _singleLine()
5 | _multiLine()
6 |
7 | def _singleLine():
8 | defaultConfig.fixToSingleLine = True
9 | fixer, msg = doFix('.test {width:"100px";color:#DDDDDD;} .test2 {width:"100px";color:#DDDDDD;}', fileName = '', config = defaultConfig)
10 | defaultConfig.fixToSingleLine = False
11 | equal(msg, '''.test,
12 | .test2 { width: '100px'; color: #DDD; }''', 'fix to single line is ok')
13 |
14 | def _multiLine():
15 | fixer, msg = doFix('.test {width:"100px";color:#DDDDDD;} .test2 {width:"100px";color:#DDDDDD;}', '')
16 | equal(msg, '''.test,
17 | .test2 {
18 | width: '100px';
19 | color: #DDD;
20 | }''', 'fix to multi line is ok')
21 |
--------------------------------------------------------------------------------
/tests/unit/hacks/HackRuleSets.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | equal(doRuleSetDetect('* html .test'), IE6, '* html')
5 | equal(doRuleSetDetect('* + html .test'), IE7, '* + html')
6 | equal(doRuleSetDetect('*:first-child+html .test'), IE7, '*:first-child+html .test')
7 | equal(doRuleSetDetect('html > body .test'), IE7 | IE8 | IE9PLUS, 'html > body')
8 | equal(doRuleSetDetect('html>/**/body .test'), IE8 | IE9PLUS, 'html>/**/body')
9 | equal(doRuleSetDetect('::-webkit-selection {}'), WEBKIT, '::-webkit-selection')
10 | equal(doRuleSetDetect('::-moz-selection {}'), FIREFOX, '::-moz-selection')
11 | equal(doRuleSetDetect('::-ms-selection {}'), IE9PLUS, '::-ms-selection')
12 | equal(doRuleSetDetect('::-o-selection {}'), OPERA, '::-o-selection')
--------------------------------------------------------------------------------
/tests/unit/config/WithPlugin.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _default()
5 |
6 | def _default():
7 | config = parseCkStyleCmdArgs(realpath('ckstyle_with_plugin.ini'), [], [], True)
8 | ok(config.pluginConfig is not None, 'plugin config is not none')
9 |
10 | options = config.pluginConfig
11 | ok(options.has_key('plugin-a-config'), 'plugin config a')
12 | ok(options.has_key('plugin-b-config'), 'plugin config b')
13 | # config in lower case
14 | ok(options.has_key('pluginCConfig'), 'plugin c')
15 |
16 | equal(options.get('plugin-a-config'), '1', 'value of plugin config a')
17 | equal(options.get('plugin-b-config'), '2', 'value of plugin config b')
18 | equal(options.get('pluginCConfig'), '3', 'value of plugin config c')
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDNoEmptyRuleSet.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDNoEmptyRuleSet(RuleSetChecker):
7 |
8 | '''{
9 | "summary":"删除空的规则",
10 | "desc":"空的CSS规则集是没有任何意义的,应该直接删除掉"
11 | }'''
12 |
13 | def __init__(self):
14 | self.id = 'no-empty-ruleset'
15 | self.errorLevel = ERROR_LEVEL.ERROR
16 | self.errorMsg = 'empty ruleset found "${selector}"'
17 |
18 | def check(self, ruleSet, config):
19 | if len(ruleSet.getRules()) == 0:
20 | return False
21 | return True
22 |
23 | def fix(self, ruleSet, config):
24 | if len(ruleSet.getRules()) == 0:
25 | styleSheet = ruleSet.getStyleSheet()
26 | styleSheet.removeRuleSet(ruleSet)
27 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDNoAlphaImageLoader.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDNoAlphaImageLoader(RuleChecker):
7 |
8 | '''{
9 | "summary":"不要使用AlphaImageLoader",
10 | "desc":"AlphaImageLoader 主要用于在IE6下显示半透明图片,此举实际上费力不讨好,
11 | 对IE的性能影响极大,为了更好地实现网页的 渐进增强
12 | ,建议不要使用 AlphaImageLoader"
13 | }'''
14 |
15 | def __init__(self):
16 | self.id = 'no-alpha-image-loader'
17 | self.errorLevel = ERROR_LEVEL.WARNING
18 | self.errorMsg = 'should not use AlphaImageLoader in "${selector}"'
19 |
20 | def check(self, rule, config):
21 | if rule.value.find('AlphaImageLoader') != -1:
22 | return False
23 | return True
24 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDDoNotSetStyleForSimpleSelector.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 0: should not set style for ".nav" in ".nav"
6 | 0: should not set style for "#sidebar" in "#sidebar"
7 | 0: should not set style for ".nav" in ".nav-b, .nav"
8 | 1: ".nav-b, .nav, .nav:hover" contains the same rules in "FEDDoNotSetStyleForSimpleSelector.css"
9 | 1: "#side-bar, #sidebar" contains the same rules in "FEDDoNotSetStyleForSimpleSelector.css"
10 | }
11 |
12 | .nav {
13 | width: 100px;
14 | height: 200px;
15 | }
16 |
17 | #side-bar {
18 | float: left;
19 | }
20 |
21 | #sidebar {
22 | float: left;
23 | }
24 |
25 | .nav-b,
26 | .nav {
27 | width: 100px;
28 | }
29 |
30 | .nav:hover {
31 | width: 100px;
32 | }
33 |
--------------------------------------------------------------------------------
/ckstyle/cmdconsole/ConsoleClass.py:
--------------------------------------------------------------------------------
1 | DEBUG = False
2 | PREFIX = '[CKstyle %s] '
3 |
4 | class console():
5 |
6 | @staticmethod
7 | def show(msg, t=""):
8 | if t != "":
9 | print(PREFIX % t + msg)
10 | else:
11 | print(msg)
12 |
13 | @staticmethod
14 | def showError(msg):
15 | console.show(msg, "ERROR")
16 |
17 | @staticmethod
18 | def showOk(msg):
19 | console.show(msg, "OK")
20 |
21 | @staticmethod
22 | def log(msg):
23 | if DEBUG:
24 | consle.show(msg, "LOG")
25 |
26 | @staticmethod
27 | def warn(msg):
28 | if DEBUG:
29 | console.show(msg, "WARN")
30 |
31 | @staticmethod
32 | def error(msg):
33 | if DEBUG:
34 | console.show(msg, "ERROR")
35 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDHackAttributeInCorrectWay.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 0: unknown attribute name "^width" found in ".hack-by-extra-char"
6 | 0: "width" is not in correct hacking way in ".error-hack-ie"
7 | 1: ".no-hack, .hack-ie-six, .hack-ie-seven, .hack-ie-six-and-seven" contains the same rules in "FEDHackAttributeInCorrectWay.css"
8 | }
9 |
10 | .hack-by-extra-char {
11 | ^width: 100px;
12 | }
13 |
14 | .no-hack {
15 | width: 200px;
16 | }
17 |
18 | .hack-ie-six {
19 | _width: 200px;
20 | }
21 |
22 | .hack-ie-seven {
23 | +width: 200px;
24 | }
25 |
26 | .hack-ie-six-and-seven {
27 | *width: 200px;
28 | }
29 |
30 | .hack-ies {
31 | width: 200px\9;
32 | }
33 |
34 | .error-hack-ie {
35 | width: 200px\0;
36 | }
37 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDSingleLineSelector.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 2: should not "enter" at the end of ".single-has-enter"
6 | 2: should start with "space" in ".single-extra-space"
7 | 2: should have "only one space" before the opening brace in ".many-spaces-end"
8 | 2: selector should end with only one space ".single-has-enter"
9 | 2: selector should end with only one space ".many-spaces-end"
10 |
11 | 1: ".single-has-enter, .single-extra-space, .many-spaces-end" contains the same rules in "FEDSingleLineSelector.css"
12 | }
13 |
14 | .single-ok { width: 100px; height: 100px; }
15 |
16 | .single-has-enter
17 | {
18 | width: 100px;
19 | }
20 |
21 | .single-extra-space { width: 100px; }
22 |
23 | .many-spaces-end { width: 100px; }
24 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDCommentLengthLessThan80.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDCommentLengthLessThan80(RuleSetChecker):
7 |
8 | '''{
9 | "summary":"注释不能超过80个字符",
10 | "desc":"注释长度不能超过80个字符,40个汉字,如果超出,则应该要换行~"
11 | }'''
12 |
13 | def __init__(self):
14 | self.id = 'comment-length'
15 | self.errorLevel = ERROR_LEVEL.LOG
16 | self.errorMsg = 'comment for "${selector}" length should less than 80 per line'
17 |
18 | def check(self, ruleSet, config):
19 | comment = ruleSet.roughComment
20 | if len(comment) == 0:
21 | return True
22 |
23 | cs = comment.split('\n')
24 | for c in cs:
25 | if len(c.strip()) > 80:
26 | return False
27 | return True
28 |
--------------------------------------------------------------------------------
/tests/unit/combiner/_test_different_order.css:
--------------------------------------------------------------------------------
1 | .test1 {
2 | width: 100px;
3 | height: 200px;
4 | *display: none;
5 | border: 1px solid #FFFFFF;
6 | _display: inline-block;
7 | }
8 |
9 | .test2 {
10 | *display: none;
11 | width: 100px;
12 | border: 1px solid #FFF;
13 | height: 200px;
14 | _display: inline-block;
15 | }
16 |
17 | .test3 {
18 | border: 1px solid #fff;
19 | width: 100px;
20 | height: 200px;
21 | *display: none;
22 | _display: inline-block;
23 | }
24 |
25 | .test4 {
26 | border: 1px solid #ffffff;
27 | *display: none;
28 | width: 100px;
29 | height: 200px;
30 | _display: inline-block;
31 | }
32 |
33 | .test5 {
34 | width: 100px; *display: none; height: 200px;
35 | border: 1px solid #ffffff;
36 | _display: inline-block;
37 | }
38 |
--------------------------------------------------------------------------------
/editor-plugins/fixstyle_for_sublime_text_2/CkStylePlugin.py:
--------------------------------------------------------------------------------
1 | #encoding=utf-8
2 | import sublime, sublime_plugin
3 | import os
4 | from helper import getCkstylePath
5 |
6 | class CkstyleCommand(sublime_plugin.TextCommand):
7 |
8 | def run(self, edit):
9 | path = os.path.realpath(self.view.file_name().decode('utf-8'))
10 | if os.path.splitext(path)[1] != '.css':
11 | sublime.error_message('Not a CSS file!')
12 | return
13 |
14 | cmd = getCkstylePath() + ' check "' + path + '"'
15 | os.popen3(cmd)
16 | resultFile = self.view.file_name() + '.ckstyle.txt'
17 | if os.path.exists(resultFile):
18 | self.view.window().open_file(self.view.file_name() + '.ckstyle.txt')
19 | else:
20 | sublime.message_dialog('No mistake found in this CSS, NB!')
21 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDUseSingleQuotation.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDUseSingleQuotation(RuleChecker):
7 |
8 | '''{
9 | "summary":"使用单引号",
10 | "desc":"CSS的属性取值一律使用单引号', 不允许使用双引号"
11 | }'''
12 |
13 | def __init__(self):
14 | self.id = 'single-quotation'
15 | self.errorLevel = ERROR_LEVEL.WARNING
16 | self.errorMsg = 'replace " with \' in "${selector}"'
17 |
18 | def check(self, rule, config):
19 | if self._findDouble(rule.value):
20 | return False
21 |
22 | return True
23 |
24 | def fix(self, rule, config):
25 | if self._findDouble(rule.value):
26 | rule.fixedValue = rule.value.replace('"', "'")
27 |
28 | def _findDouble(self, value):
29 | return value.find('"') != -1
30 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDUseValidValues.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | from .validators.ValidatorFactory import doValidate
6 |
7 | class FEDUseValidValues(RuleChecker):
8 |
9 | '''{
10 | "summary":"不正确的属性取值",
11 | "desc":"检查不正确的属性取值,比如: width: underline; 等"
12 | }'''
13 |
14 | def __init__(self):
15 | self.id = 'valid-values'
16 | self.errorLevel = ERROR_LEVEL.ERROR
17 | self.errorMsg_rough = '%s in "${selector}"'
18 | self.errorMsg = ''
19 |
20 | def check(self, rule, config):
21 | flag, msg = doValidate(rule.name, rule.strippedValue)
22 | if flag is True:
23 | return True
24 |
25 | self.errorMsg = self.errorMsg_rough % msg
26 | return False
27 |
28 | def fix(self, rule, config):
29 | pass
30 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDZIndexShouldInRange.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 0: value of "z-index" is not correct in ".z-index-minus"
6 | 0: value of "z-index" is not correct in ".z-index-exceed"
7 | 0: value of "z-index" is not correct in ".z-index-exceed-more"
8 | }
9 |
10 | .z-index-minus {
11 | z-index: -1000;
12 | }
13 |
14 | .z-index-minus-one {
15 | z-index: -1;
16 | }
17 |
18 | .z-index-zero {
19 | z-index: 0;
20 | }
21 |
22 | .z-index-one {
23 | z-index: 1;
24 | }
25 |
26 | .z-index-little-less {
27 | z-index: 1999;
28 | }
29 |
30 | .z-index-little-less-beta {
31 | z-index: 2099;
32 | }
33 |
34 | .z-index-max {
35 | z-index: 2100;
36 | }
37 |
38 | .z-index-exceed {
39 | z-index: 2101;
40 | }
41 |
42 | .z-index-exceed-more {
43 | z-index: 100000;
44 | }
45 |
--------------------------------------------------------------------------------
/tests/unit/combiner/_with_margin.css:
--------------------------------------------------------------------------------
1 | /* @author wangjeaf */
2 |
3 | .test {
4 | _width: 100px;
5 | *height: 100px;
6 | margin: 10px;
7 | margin-top: 20px;
8 | }
9 |
10 | .test2 {
11 | _width: 100px;
12 | *height: 100px;
13 | margin: 20px 10px 10px;
14 | }
15 | .test3 {
16 | _width: 100px;
17 | *height: 100px;
18 | margin: 0 10px 0px;
19 | margin-top: 20px;
20 | margin-left: 10px;
21 | margin-right: 10px;
22 | margin-bottom: 10px;
23 | }
24 |
25 | .test4 {
26 | margin: 10px;
27 | _width: 100px;
28 | margin-top: 20px;
29 | *height: 100px;
30 | }
31 |
32 | .test5 {
33 | margin: 10px;
34 | margin-top: 20px;
35 | margin-left: 10px;
36 | *height: 100px;
37 | _width: 100px;
38 | }
39 |
40 | .test6 {
41 | _width: 100px;
42 | *height: 100px;
43 | display:none;
44 | }
45 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDNoCommentInValues.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDNoCommentInValues(RuleSetChecker):
7 |
8 | '''{
9 | "summary":"不要在css属性中添加注释",
10 | "desc":"CSS的注释应该写在 selector 前面,属性中不允许添加css注释,例如:
11 | .selector {
12 | width: 100px;/*comment here*/
13 | }
14 | "
15 | }'''
16 |
17 | def __init__(self):
18 | self.id = 'no-comment-in-value'
19 | self.errorLevel = ERROR_LEVEL.LOG
20 | self.errorMsg = 'find css comment (/* */) in "${selector}"'
21 |
22 | def check(self, ruleSet, config):
23 | if ruleSet.roughValue.find('/*') != -1 or ruleSet.roughValue.find('*/') != -1:
24 | return False
25 | return True
26 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDTransChnFontFamilyNameIntoEng.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | from .helper import containsChnChar
6 |
7 | class FEDTransChnFontFamilyNameIntoEng(RuleChecker):
8 |
9 | '''{
10 | "summary":"字体设置时使用英文",
11 | "desc":"有的字体设置可以通过中文和英文两者方式来声明,比如
12 | 微软雅黑 和 Microsoft Yahei ,我们推荐用英文的方式来实现"
13 | }'''
14 |
15 | def __init__(self):
16 | self.id = 'no-chn-font-family'
17 | self.errorLevel = ERROR_LEVEL.ERROR
18 | self.errorMsg = 'should not use chinese font family name in "${selector}"'
19 |
20 | def check(self, rule, config):
21 | if rule.name != 'font' and rule.name != 'font-family':
22 | return True
23 |
24 | if containsChnChar(rule.value):
25 | return False
26 |
27 | return True
28 |
--------------------------------------------------------------------------------
/tests/unit/combiner/_with_padding.css:
--------------------------------------------------------------------------------
1 | /* @author wangjeaf */
2 |
3 | .test {
4 | _width: 100px;
5 | *height: 100px;
6 | padding: 10px;
7 | padding-top: 20px;
8 | }
9 |
10 | .test2 {
11 | _width: 100px;
12 | *height: 100px;
13 | padding: 20px 10px 10px;
14 | }
15 | .test3 {
16 | _width: 100px;
17 | *height: 100px;
18 | padding: 0 10px 0px;
19 | padding-top: 20px;
20 | padding-left: 10px;
21 | padding-right: 10px;
22 | padding-bottom: 10px;
23 | }
24 |
25 | .test4 {
26 | padding: 10px;
27 | _width: 100px;
28 | padding-top: 20px;
29 | *height: 100px;
30 | }
31 |
32 | .test5 {
33 | padding: 10px;
34 | padding-top: 20px;
35 | padding-left: 10px;
36 | *height: 100px;
37 | _width: 100px;
38 | }
39 |
40 | .test6 {
41 | _width: 100px;
42 | *height: 100px;
43 | display:none;
44 | }
45 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDNoUnitAfterZero.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 1: unit should be removed when meet 0 in ".test-zero-px"
6 | 1: zero should be removed when meet 0.xxx in ".test-zero-dot-one-px"
7 | 1: unit should be removed when meet 0 in ".test-padding"
8 | }
9 |
10 | .test-zero-px {
11 | width: 0px;
12 | }
13 |
14 | .test-zero {
15 | width: 0;
16 | }
17 |
18 | .test-zero-dot-one-px {
19 | width: 0.1px;
20 | }
21 |
22 | .test-padding {
23 | padding: 1px 0px;
24 | }
25 |
26 | .test-second {
27 | -webkit-animation: rotation 0s linear infinite;
28 | -moz-animation: rotation 0s linear infinite;
29 | -o-animation: rotation 0s linear infinite;
30 | animation: rotation 0s linear infinite;
31 | }
32 |
33 | .test-padding-start-with-zero {
34 | padding: 0 1px 0 1px;
35 | }
36 |
--------------------------------------------------------------------------------
/tests/unit/entity/StyleSheet.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _stylesheet()
5 |
6 | def _stylesheet():
7 | styleSheet = StyleSheet('test.css')
8 | equal(styleSheet.getFile(), 'test.css', 'file name is ok')
9 | styleSheet.setFile('test2.css')
10 | equal(styleSheet.getFile(), 'test2.css', 'file nam changed')
11 | equal(styleSheet._file, styleSheet.getFile(), 'it is the same')
12 |
13 | equal(len(styleSheet.getRuleSets()), 0, 'no rule sets')
14 | equal(styleSheet.getRuleSetBySelector('.test'), None, 'can not find .test')
15 |
16 | styleSheet.addRuleSetByStr('.test', 'width:100px;height:100px', '/* fjdalkf */')
17 | equal(len(styleSheet.getRuleSets()), 1, 'one rule set')
18 | equal(styleSheet.getRuleSetBySelector('.test').selector, '.test', 'find .test')
19 | equal(styleSheet._ruleSets[0].comment, '/* fjdalkf */', 'comment is ok')
20 |
--------------------------------------------------------------------------------
/tests/unit/fix/FixFEDCombineInToOne.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | equal(getFixed('.test {margin:0 0 0 0}', 'margin'), '0', 'margin is fixed')
5 | equal(getFixed('.test {margin:0 auto 0 auto}', 'margin'), '0 auto', 'margin 2 is fixed')
6 | equal(getFixed('.test {margin:auto 0 0 auto}', 'margin'), 'auto 0 0 auto', 'margin 3 is fixed')
7 | equal(getFixed('.test {margin:0 0}', 'margin'), '0', 'margin 4 is fixed')
8 | equal(getFixed('.test {margin:0px 0}', 'margin'), '0', 'margin 5 is fixed')
9 | equal(getFixed('.test {margin:0px 1px 0px 1px}', 'margin'), '0 1px', 'margin 6 is fixed')
10 | equal(getFixed('.test {margin:0px auto 0px auto}', 'margin'), '0 auto', 'margin 7 is fixed')
11 | equal(getFixed('.test {margin:50px auto 0 auto}', 'margin'), '50px auto 0', 'margin 8 is fixed')
12 | equal(getFixed('.test {margin: -15px -15px 0 -15px}', 'margin'), '-15px -15px 0', 'margin 9 is fixed')
13 |
--------------------------------------------------------------------------------
/tests/unit/entity/Rule.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _rule()
5 | _getRuleSet()
6 |
7 | def _getRuleSet():
8 | ruleSet = RuleSet('.selector', 'width:100px;', '/* aa */', None)
9 | rule = Rule("", "", "", ruleSet)
10 | equal(rule.getRuleSet(), ruleSet, 'get rule set')
11 | equal(rule.getRuleSet().selector, '.selector', 'it is what I need')
12 |
13 | def _rule():
14 | rule = Rule(" .test ", " _width ", " 100px; ", None)
15 | equal(rule.selector, '.test', 'selector is ok')
16 | equal(rule.roughSelector, ' .test ', 'roughSelector is ok')
17 | equal(rule.roughName, ' _width ', 'roughName is ok')
18 | equal(rule.name, 'width', 'name is ok')
19 | equal(rule.roughValue, ' 100px; ', 'roughValue is ok')
20 | equal(rule.value, '100px', 'value is ok')
21 | equal(rule.strippedName, '_width', 'stripped name is ok')
22 | equal(rule.strippedValue, '100px;', 'strippedValue is ok')
23 |
--------------------------------------------------------------------------------
/tests/unit/hacks/HackExtras.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _standard_hack()
5 | _keyframes()
6 |
7 | def _standard_hack():
8 | equal(doExtraDetect('@media screen and (max-device-width: 480px)'.replace(' ', '')), WEBKIT, 'webkit mobile hack is ok')
9 | equal(doExtraDetect('@media screen and (-webkit-min-device-pixel-ratio:0)'.replace(' ', '')), WEBKIT, 'webkit hack is ok')
10 | equal(doExtraDetect('@media all and (-webkit-min-device-pixel-ratio:10000), not all and (-webkit-min-device-pixel-ratio:0)'.replace(' ', '')), OPERA, 'opera hack is ok')
11 |
12 | def _keyframes():
13 | equal(doExtraDetect('@keyframes fda'), NONEIE | IE9PLUS, '@keyframes')
14 | equal(doExtraDetect('@-webkit-keyframes fda'), WEBKIT, '@-webkit-keyframes')
15 | equal(doExtraDetect('@-moz-keyframes fda'), FIREFOX, '@-moz-keyframes')
16 | equal(doExtraDetect('@-ms-keyframes fda'), IE9PLUS, '@-ms-keyframes')
17 | equal(doExtraDetect('@-o-keyframes fda'), OPERA, '@-o-keyframes')
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDDoNotSetStyleForTagOnly.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | from .helper import isHTMLTag
6 |
7 | class FEDDoNotSetStyleForTagOnly(RuleSetChecker):
8 |
9 | '''{
10 | "summary":"不要为html tag设置样式",
11 | "desc":"除了重置 CSS(如Reset.css) 的相关设置,其他代码一律不允许为html tag设置样式。"
12 | }'''
13 |
14 | def __init__(self):
15 | self.id = 'no-style-for-tag'
16 | self.errorLevel = ERROR_LEVEL.ERROR
17 | self.errorMsg = 'should not set style for html tag in "${selector}"'
18 |
19 | def check(self, ruleSet, config):
20 | selector = ruleSet.selector.lower()
21 | if selector.find('@media') != -1:
22 | return True
23 | if selector.find('@-moz-document') != -1:
24 | return True
25 | selectors = selector.split(',')
26 | for s in selectors:
27 | if isHTMLTag(s.strip()):
28 | return False
29 | return True
30 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDCanNotSetFontFamily.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | from .helper import isFontFamilyName
6 |
7 | class FEDCanNotSetFontFamily(RuleChecker):
8 |
9 | '''{
10 | "summary":"不允许业务代码设置字体",
11 | "desc":"由于业务代码中随意设置字体,导致字体取值混乱,因此不允许随意在业务代码中设置字体"
12 | }'''
13 |
14 | def __init__(self):
15 | self.id = 'no-font-family'
16 | self.errorLevel = ERROR_LEVEL.ERROR
17 | self.errorMsg = 'can not set font-family for "${selector}"'
18 |
19 | def check(self, rule, config):
20 | if rule.name == 'font-family':
21 | return False
22 |
23 | if rule.name == 'font':
24 | # many fonts
25 | if rule.value.find(',') != -1:
26 | return False
27 |
28 | # one font
29 | splited = rule.value.split(' ')
30 | if isFontFamilyName(splited[len(splited) - 1]):
31 | return False
32 |
33 | return True
34 |
--------------------------------------------------------------------------------
/ckstyle/browsers/BinaryRule.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | '''
5 | 0b111111111
6 | |||||||||
7 | |||||||||
8 | |||||||||--ie6 ---------|
9 | ||||||||--ie7 ---------|
10 | |||||||--ie8 ---------| ALLIE
11 | ||||||--ie9+ ---------|
12 | |||||
13 | |||||--opera
14 | ||||--safari ---------|
15 | |||--firefox | WEBKIT
16 | ||-- chrome ---------|
17 | |-- STD
18 | '''
19 |
20 | ORIGIN = 0b000000000
21 |
22 | STD = 0b100000000
23 | CHROME = 0b010000000
24 | FIREFOX= 0b001000000
25 | SAFARI = 0b000100000
26 | OPERA = 0b000010000
27 |
28 | WEBKIT = CHROME | SAFARI
29 |
30 | NONEIE = CHROME | SAFARI | OPERA | FIREFOX
31 |
32 | IE9PLUS= 0b000001000
33 | IE8 = 0b000000100
34 | IE7 = 0b000000010
35 | IE6 = 0b000000001
36 | ALLIE = IE9PLUS | IE8 | IE7 | IE6
37 |
38 | NOIE6 = IE9PLUS | IE8 | IE7 | NONEIE
39 | NOIE67 = IE9PLUS | IE8 | NONEIE
40 | NOIE678= IE9PLUS | NONEIE
41 | NONE = 0b000000000
42 | ALL = 0b111111111
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDUseLowerCaseSelector.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDUseLowerCaseSelector(RuleSetChecker):
7 |
8 | '''{
9 | "summary":"选择器用小写字母",
10 | "desc":"选择器应该用小写字母, 例如 .demo , 不允许使用大写,例如: .Demo .Test"
11 | }'''
12 |
13 | def __init__(self):
14 | self.id = 'lowercase-selector'
15 | self.errorLevel = ERROR_LEVEL.WARNING
16 | self.errorMsg = 'selector should use lower case, in "${selector}"'
17 |
18 | def check(self, ruleSet, config):
19 | selector = ruleSet.selector
20 | if selector.lower() != selector:
21 | return False
22 |
23 | return True
24 |
25 | def fix(self, ruleSet, config):
26 | # if fix upper to lower, will cause error in HTML(do not do evil)
27 | pass
28 | #selector = ruleSet.selector
29 | #if selector.lower() != selector:
30 | # ruleSet.fixedSelector = ruleSet.fixedSelector.lower()
31 |
--------------------------------------------------------------------------------
/tests/unit/fix/CommentForExtraStatement.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _with_comment()
5 | _without_comment()
6 |
7 | def _with_comment():
8 | css = '''/**
9 | * @descript: topic pages
10 | * @author: ming.zhou
11 | * @date: 2012-12-7
12 | */
13 |
14 | @import url(dfafdas);
15 |
16 | @import url(dfafdas);'''
17 |
18 | expectedFixed = '''/**
19 | * @descript: topic pages
20 | * @author: ming.zhou
21 | * @date: 2012-12-7
22 | */
23 | @import url(dfafdas);
24 |
25 | @import url(dfafdas);
26 | '''
27 |
28 | fixer, msg = doFix(css, '')
29 | equal(msg.strip(), expectedFixed.strip(), 'extra statement with comment is ok')
30 |
31 | def _without_comment():
32 | css = '''
33 |
34 | @import url(dfafdas);
35 |
36 | @import url(dfafdas);'''
37 |
38 | expectedFixed = '''@import url(dfafdas);
39 |
40 | @import url(dfafdas);
41 | '''
42 |
43 | fixer, msg = doFix(css, '')
44 | equal(msg.strip(), expectedFixed.strip(), 'extra statement without comment is ok')
45 |
46 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDHackAttributeInCorrectWay.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | from .helper import isCss3PrefixProp
6 |
7 | class FEDHackAttributeInCorrectWay(RuleChecker):
8 |
9 | '''{
10 | "summary":"hack属性时的检查",
11 | "desc":"必须使用正确的 hack 方式, 比如 _ * + 等,其他的属性前缀一律不允许"
12 | }'''
13 |
14 | def __init__(self):
15 | self.id = 'hack-prop'
16 | self.errorLevel = ERROR_LEVEL.ERROR
17 | self.errorMsg = '"${name}" is not in correct hacking way in "${selector}"'
18 |
19 | def check(self, rule, config):
20 | if rule.value.find(r'\0') != -1:
21 | return False
22 |
23 | stripped = rule.roughName.strip()
24 | if rule.name == stripped.lower():
25 | return True
26 |
27 | if isCss3PrefixProp(rule.name):
28 | return True
29 |
30 | if not stripped.startswith('_') and not stripped.startswith('*') and not stripped.startswith('+'):
31 | return False
32 |
33 | return True
34 |
--------------------------------------------------------------------------------
/tests/unit/commandline/helper.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | sys.path.insert(0, os.path.realpath(os.path.join(__file__, '../../')))
4 | for p in os.environ.get('PYTHONPATH', '').split(';'):
5 | sys.path.append(p)
6 |
7 | from asserts import *
8 | from ckstyle.command.ConsoleCommandParser import handleFixStyleCmdArgs, handleCkStyleCmdArgs, handleCompressCmdArgs
9 |
10 | def doCheck(args):
11 | return doCkstyle(args, handleCkStyleCmdArgs)
12 | def doFix(args):
13 | return doCkstyle(args, handleFixStyleCmdArgs)
14 | def doCompress(args):
15 | return doCkstyle(args, handleCompressCmdArgs)
16 |
17 | def doCkstyle(args, handler):
18 | old = sys.stdout
19 | output = realpath('_tmp.txt')
20 | sys.stdout = open(output, 'w')
21 | handler(args)
22 | sys.stdout = old
23 |
24 | result = open(output, 'r').read()
25 | os.remove(output)
26 | return result.strip()
27 |
28 | def realpath(filepath):
29 | dirpath = os.path.realpath(os.path.join(__file__, '../'))
30 | path = os.path.join(dirpath, filepath)
31 | return path
--------------------------------------------------------------------------------
/ckstyle/reporter/JsonReporter.py:
--------------------------------------------------------------------------------
1 | from .Reporter import Reporter
2 | from .helper import fill
3 | import json
4 |
5 | class JsonReporter(Reporter):
6 | def __init__(self, checker):
7 | self.checker = checker
8 | self.msg = ''
9 | pass
10 |
11 | def doReport(self):
12 | checker = self.checker
13 | counter = 0
14 | formatter = '%s'
15 |
16 | logs, warns, errors = checker.errors()
17 | if len(logs) == 0 and len(warns) == 0 and len(errors) == 0:
18 | self.setMsg('{}')
19 | return
20 | for error in errors:
21 | error["errorMsg"] = fill(error)
22 | for warn in warns:
23 | warn["errorMsg"] = fill(warn)
24 | for log in logs:
25 | log["errorMsg"] = fill(log)
26 |
27 | self.setMsg('{"errors":%s,"warnings":%s,"logs":%s}' % (json.dumps(errors), json.dumps(warns), json.dumps(logs)))
28 |
29 | def export(self):
30 | return self.msg
31 |
32 | def setMsg(self, msg):
33 | self.msg = msg
--------------------------------------------------------------------------------
/editor-plugins/fixstyle_for_sublime_text_2/Main.sublime-menu:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "tools",
4 | "caption": "Tools",
5 | "children":
6 | [
7 | {
8 | "id": "fixstyle",
9 | "caption": "Fixstyle",
10 | "command": "fixstyle"
11 | },
12 | {
13 | "id": "fixstyleSafe",
14 | "caption": "FixstyleSafe",
15 | "command": "fixstylesafe"
16 | },
17 | {
18 | "id": "fixstyleSingleLine",
19 | "caption": "FixstyleSingleLine",
20 | "command": "fixstylesingleline"
21 | },
22 | {
23 | "id": "ckstyle",
24 | "caption": "CkStyle",
25 | "command": "ckstyle"
26 | },
27 | {
28 | "id": "csscompress",
29 | "caption": "CssCompress",
30 | "command": "csscompress"
31 | }
32 | ]
33 | }
34 | ]
35 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDZIndexShouldInRange.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | import string
6 | from .helper import isCss3PrefixProp
7 |
8 | class FEDZIndexShouldInRange(RuleChecker):
9 |
10 | '''{
11 | "summary":"z-index取值应符合范围要求",
12 | "desc":"z-index 的取值如果混乱,则会造成层之间的相互覆盖,
13 | 因此 z-index 取值必须符合一定的范围要求,具体要求请参见人人FED CSS编码规范"
14 | }'''
15 |
16 | def __init__(self):
17 | self.id = 'z-index-in-range'
18 | self.errorLevel = ERROR_LEVEL.ERROR
19 | self.errorMsg = 'value of "z-index" is not correct in "${selector}"'
20 |
21 | def check(self, rule, config):
22 | if rule.name != 'z-index':
23 | return True
24 |
25 | zIndex = None
26 | try:
27 | zIndex = string.atoi(rule.value)
28 | except ValueError:
29 | return False
30 |
31 | if zIndex < -1:
32 | return False
33 |
34 | if zIndex > 2100:
35 | return False
36 |
37 | return True
38 |
--------------------------------------------------------------------------------
/tests/unit/compress/helper.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | sys.path.insert(0, os.path.realpath(os.path.join(__file__, '../../')))
4 | for p in os.environ.get('PYTHONPATH', '').split(';'):
5 | sys.path.append(p)
6 |
7 | from asserts import *
8 | from ckstyle.doCssCompress import doCompress
9 | from ckstyle.browsers.BinaryRule import *
10 |
11 | def doCssCompress(fileContent, fileName = ''):
12 | checker, compressed = doCompress(fileContent, fileName)
13 | return compressed
14 |
15 | def doCssCompress2(fileContent, fileName = ''):
16 | checker, compressed = doCompress(fileContent, fileName)
17 | return checker
18 |
19 | def realpath(filepath):
20 | dirpath = os.path.realpath(os.path.join(__file__, '../'))
21 | path = os.path.join(dirpath, filepath)
22 | return path
23 |
24 | def doCssFileCompress(path):
25 | fileContent = open(realpath(path), 'r').read()
26 | return doCssCompress(fileContent, path)
27 |
28 | def doCssFileCompress2(path):
29 | fileContent = open(realpath(path), 'r').read()
30 | return doCssCompress2(fileContent, path)
--------------------------------------------------------------------------------
/tests/unit/fix/FixAllInSafeMode.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _singleLine()
5 | _multiLine()
6 |
7 | def _singleLine():
8 | defaultConfig.safeMode = True
9 | fixer, msg = doFix('.test {width:"100px";color:#DDDDDD;margin:0 auto 0 auto;} .test2 {width:"100px";color:#DDDDDD;margin-top:0;margin-left:auto;margin-right:auto;margin-bottom:0;}', fileName = '', config = defaultConfig)
10 | defaultConfig.safeMode = False
11 | equal(msg,
12 | '''.test {
13 | width: '100px';
14 | margin: 0 auto;
15 | color: #DDD;
16 | }
17 |
18 | .test2 {
19 | width: '100px';
20 | margin: 0 auto;
21 | color: #DDD;
22 | }''', 'safe mode true, fix is ok')
23 |
24 | def _multiLine():
25 | fixer, msg = doFix('.test {width:"100px";color:#DDDDDD;margin:0 auto 0 auto;} .test2 {width:"100px";color:#DDDDDD;margin-top:0;margin-left:auto;margin-right:auto;margin-bottom:0;}', '')
26 | equal(msg,
27 | '''.test,
28 | .test2 {
29 | width: '100px';
30 | margin: 0 auto;
31 | color: #DDD;
32 | }''', 'default safeMode is false, fix is ok')
33 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDFontSizeShouldBePtOrPx.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 0: font-size unit should be px/pt in ".font-size-is-percent"
6 | 0: font-size unit should be px/pt in ".font-size-is-number"
7 | 0: font-size unit should be px/pt in ".font-size-is-em"
8 | 0: font-size should not be small/medium/large in ".font-size-is-x-small"
9 | 0: font-size should not be small/medium/large in ".font-size-is-x-large"
10 | 0: font-size unit should be px/pt in ".font-size-pt-in-middle"
11 | }
12 |
13 |
14 | .font-size-is-percent {
15 | font-size: 17%;
16 | }
17 |
18 | .font-size-is-number {
19 | font-size: 12.6;
20 | }
21 |
22 | .font-size-is-em {
23 | font-size: 2em;
24 | }
25 |
26 | .font-size-is-x-small {
27 | font-size: x-small;
28 | }
29 | .font-size-is-x-large {
30 | font-size: x-large;
31 | }
32 |
33 | .font-size-pt-in-middle {
34 | font-size: x-pt-smal;
35 | }
36 |
37 | .font-size-is-pt {
38 | font-size: 12pt;
39 | }
40 |
41 | .font-size-is-px {
42 | font-size: 12px;
43 | }
44 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDHackRuleSetInCorrectWay.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 0: not correct hacking way in "@-moz-document url-prefix()"
6 | 0: not correct hacking way in "@-moz-document prefix()"
7 | 0: not correct hacking way in "@media screen and (-webkit-min-device-pixel-ratio:0) and other"
8 | 0: not correct hacking way in "@media all and (-webkit-min-device-pixel-ratio:10), not all and (-webkit-min-device-pixel-ratio:0)"
9 | }
10 |
11 | @-moz-document url-prefix() {
12 | width: 100px;
13 | }
14 |
15 | @-moz-document url-prefix() {
16 | width: 100px;
17 | }
18 |
19 | @-moz-document prefix() {
20 | width: 100px;
21 | }
22 |
23 |
24 | @media screen and (-webkit-min-device-pixel-ratio:0) and other {
25 | width: 100px;
26 | }
27 |
28 | @media all and (-webkit-min-device-pixel-ratio:10000), not all and (-webkit-min-device-pixel-ratio:0) {
29 | width: 100px;
30 | }
31 |
32 | @media all and (-webkit-min-device-pixel-ratio:10), not all and (-webkit-min-device-pixel-ratio:0) {
33 | width: 100px;
34 | }
35 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDFixCommentInValue.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDFixCommentInValue(RuleChecker):
7 |
8 | '''{
9 | "summary":"修复属性中的注释",
10 | "desc":"width:/* fdasfdas */ 100px /* fdafdas */; ==> width:100px;"
11 | }'''
12 |
13 | def __init__(self):
14 | self.id = 'fix-comment-in-value'
15 | self.errorLevel = ERROR_LEVEL.WARNING
16 | self.errorMsg = ''
17 | self.private = True
18 |
19 | def check(self, rule, config):
20 | return True
21 |
22 |
23 | def fix(self, rule, config):
24 | if rule.name == 'expression':
25 | return
26 | value = rule.fixedValue
27 | if value.find('/*') == -1:
28 | return
29 |
30 | splited = value.split('/*')
31 | collector = []
32 | for x in splited:
33 | tmp = x.split('*/')
34 | if len(tmp) == 1:
35 | collector.append(tmp[0])
36 | else:
37 | collector.append(tmp[1])
38 | rule.fixedValue = ''.join(collector)
39 |
--------------------------------------------------------------------------------
/ckstyle/reporter/TextReporter.py:
--------------------------------------------------------------------------------
1 | from .Reporter import Reporter
2 | from .helper import fill
3 |
4 | class TextReporter(Reporter):
5 | def __init__(self, checker):
6 | self.checker = checker
7 | self.msgs = []
8 |
9 | def doReport(self):
10 | checker = self.checker
11 | counter = 0
12 | formatter = '%s %s. %s'
13 |
14 | logs, warns, errors = checker.errors()
15 | if len(logs) == 0 and len(warns) == 0 and len(errors) == 0:
16 | self.appendMsg('aha, no problem')
17 | return
18 |
19 | for error in errors:
20 | counter = counter + 1
21 | self.appendMsg(formatter % ('[ERROR]', counter, fill(error)))
22 | for warn in warns:
23 | counter = counter + 1
24 | self.appendMsg(formatter % (' [WARN]', counter, fill(warn)))
25 | for log in logs:
26 | counter = counter + 1
27 | self.appendMsg(formatter % (' [LOG]', counter, fill(log)))
28 |
29 | def appendMsg(self, msg):
30 | self.msgs.append(msg)
31 |
32 | def export(self):
33 | return '\n'.join(self.msgs)
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDMustContainAuthorInfo.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | from .helper import isHTMLTag
6 |
7 | class FEDMustContainAuthorInfo(StyleSheetChecker):
8 |
9 | '''{
10 | "summary":"需要在文件中添加作者信息",
11 | "desc":"需要在文件中添加作者的信息,本工具认可的作者信息是在文件顶部的注释中添加 @author:xxx"
12 | }'''
13 |
14 | def __init__(self):
15 | self.id = 'add-author'
16 | self.errorMsg_author = 'should add @author in the head of "${file}"'
17 | self.errorMsg_empty = 'empty css file "${file}"'
18 | self.errorMsg = ''
19 | self.errorLevel = ERROR_LEVEL.ERROR
20 |
21 | def check(self, styleSheet, config):
22 | ruleSets = styleSheet.getRuleSets()
23 | if len(ruleSets) == 0:
24 | self.errorMsg = self.errorMsg_empty
25 | return False
26 |
27 | first = ruleSets[0]
28 |
29 | if styleSheet.getFile() != '' and first.comment.find('@author') == -1 and first.comment.find('@renren-inc.com') == -1:
30 | self.errorMsg = self.errorMsg_author
31 | return False
32 | return True
33 |
--------------------------------------------------------------------------------
/ckstyle/plugins/combiners/CombinerFactory.py:
--------------------------------------------------------------------------------
1 | from ckstyle.cmdconsole.ConsoleClass import console
2 |
3 | def doCombine(name, props):
4 | pluginName = camelCase(name) + 'Combiner'
5 | pluginClass = NullCombiner
6 | try:
7 | plugin = __import__('ckstyle.plugins.combiners.' + pluginName, fromlist = [pluginName])
8 | if hasattr(plugin, pluginName):
9 | pluginClass = getattr(plugin, pluginName)
10 | else:
11 | console.error('%s should exist in %s.py' % (pluginName, pluginName))
12 | except ImportError as e:
13 | pass
14 | instance = pluginClass(name, props)
15 | return instance.combine()
16 |
17 | class NullCombiner():
18 | def __init__(self, name, props):
19 | pass
20 | def combine(self):
21 | return None, [], False
22 |
23 | def camelCase(name):
24 | splited = name.split('-')
25 |
26 | collector = []
27 | for x in splited:
28 | collector.append(x.capitalize())
29 | return ''.join(collector)
30 |
31 | if __name__ == '__main__':
32 | print(doCombine('margin', [
33 | ['margin', 'margin', '50px auto 0 auto']
34 | ]))
35 |
--------------------------------------------------------------------------------
/tests/unit/fix/FixFEDUseLowerCaseSelector.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _lower()
5 | _upper()
6 |
7 | def _lower():
8 | fixer, msg = doFix('.test {width:100px;}', '')
9 | styleSheet = fixer.getStyleSheet()
10 | ruleSet = styleSheet.getRuleSets()[0]
11 | equal(ruleSet.fixedSelector, '.test', 'selector lower is ok')
12 |
13 | def _upper():
14 | # if fix upper to lower, will cause error in HTML, do not do evil
15 | return
16 | fixer, msg = doFix('.TEST {width:100px;}', '')
17 | styleSheet = fixer.getStyleSheet()
18 | ruleSet = styleSheet.getRuleSets()[0]
19 | equal(ruleSet.fixedSelector, '.test', 'selector all upper is ok')
20 |
21 | fixer, msg = doFix('.Test {width:100px;}', '')
22 | styleSheet = fixer.getStyleSheet()
23 | ruleSet = styleSheet.getRuleSets()[0]
24 | equal(ruleSet.fixedSelector, '.test', 'selector one upper is ok')
25 |
26 | fixer, msg = doFix('.Test-WRAPPER {width:100px;}', '')
27 | styleSheet = fixer.getStyleSheet()
28 | ruleSet = styleSheet.getRuleSets()[0]
29 | equal(ruleSet.fixedSelector, '.test-wrapper', 'selector upper with - is ok')
30 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDReplaceBorderZeroWithBorderNone.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDReplaceBorderZeroWithBorderNone(RuleChecker):
7 |
8 | '''{
9 | "summary":"用border:none替换border:0",
10 | "desc":"border:0 实际上是有border的,只不过宽度为0, 而 border:none;
11 | 是根本没有border的,对于浏览器来说后者的效率高,但是要注意,后者的代码长度稍微长一些。"
12 | }'''
13 |
14 | def __init__(self):
15 | self.id = 'no-border-zero'
16 | self.errorLevel = ERROR_LEVEL.WARNING
17 | self.errorMsg_borderWidth = 'replace "border-width: 0" with "border-width: none" in "${selector}"'
18 | self.errorMsg_border = 'replace "border: 0" with "border: none" in "${selector}"'
19 | self.errorMsg = ''
20 |
21 | def check(self, rule, config):
22 | if rule.name == 'border' and rule.value == '0':
23 | self.errorMsg = self.errorMsg_border
24 | return False
25 |
26 | if rule.name == 'border-width' and rule.value == '0':
27 | self.errorMsg = self.errorMsg_borderWidth
28 | return False
29 |
30 | return True
31 |
--------------------------------------------------------------------------------
/tests/demo/demo-compress.css:
--------------------------------------------------------------------------------
1 | .test1 {
2 | width: 100px;
3 | height: 200px;
4 | *display: none;
5 | border: 1px solid #FFFFFF;
6 | _display: inline-block;
7 | margin: 10px;
8 | margin-top: 20px;
9 | }
10 |
11 | .test2 {
12 | *display: none;
13 | width: 100px;
14 | border: 1px solid #FFF;
15 | height: 200px;
16 | _display: inline-block;
17 | margin: 20px 10px 10px;
18 | }
19 |
20 | .test3 {
21 | margin: 0 10px 20px;
22 | border: 1px solid #fff;
23 | width: 100px;
24 | height: 200px;
25 | *display: none;
26 | _display: inline-block;
27 | margin-top: 20px;
28 | margin-left: 10px;
29 | margin-right: 10px;
30 | margin-bottom: 10px;
31 | }
32 |
33 | .test4 {
34 | border: 1px solid #ffffff;
35 | *display: none;
36 | width: 100px;
37 | height: 200px;
38 | margin: 10px;
39 | _display: inline-block;
40 | margin-top: 20px;
41 | }
42 |
43 | .test5 {
44 | margin: 10px;
45 | margin-top: 20px;
46 | width: 100px; *display: none; height: 200px;
47 | border: 1px solid #ffffff;
48 | _display: inline-block;
49 | margin-left: 10px;
50 | }
51 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDNoSimpleNumberInSelector.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 1: do not simply use 1,2,3 as selector(use v1/step1/item1), in ".class1"
6 | 1: do not simply use 1,2,3 as selector(use v1/step1/item1), in ".class2"
7 | 1: do not simply use 1,2,3 as selector(use v1/step1/item1), in ".class6"
8 | 1: do not simply use 1,2,3 as selector(use v1/step1/item1), in ".fdjas6"
9 | 1: do not simply use 1,2,3 as selector(use v1/step1/item1), in ".test1000"
10 |
11 | 1: ".class1, .class2, .class6, .class .ok, .v6, .class .step3, .fdjas6, .test1000, .test-item111" contains the same rules in "FEDNoSimpleNumberInSelector.css"
12 | }
13 |
14 | .class1 {
15 | width: 100px;
16 | }
17 |
18 | .class2 {
19 | width: 100px;
20 | }
21 |
22 | .class6 {
23 | width: 100px;
24 | }
25 |
26 | .class .ok {
27 | width: 100px;
28 | }
29 |
30 | .v6 {
31 | width: 100px;
32 | }
33 |
34 | .class .step3 {
35 | width: 100px;
36 | }
37 |
38 | .fdjas6 {
39 | width: 100px;
40 | }
41 |
42 | .test1000 {
43 | width: 100px;
44 | }
45 |
46 | .test-item111 {
47 | width: 100px;
48 | }
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDNoAppearanceNameInSelector.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | from .helper import existsAppearanceWords
6 |
7 | class FEDNoAppearanceNameInSelector(RuleSetChecker):
8 |
9 | '''{
10 | "summary":"选择器中避免表现相关的词汇",
11 | "desc":"避免将在selector中出现 .red .left 等描述性词汇,
12 | 用具体的实际意义来代替,比如 .error .sidebar "
13 | }'''
14 |
15 | def __init__(self):
16 | self.id = 'no-appearance-word-in-selector'
17 | self.errorLevel = ERROR_LEVEL.WARNING
18 | self.errorMsg_origin = 'should not use appearance word "%s" in "${selector}"'
19 | self.errorMsg = ''
20 |
21 | def check(self, ruleSet, config):
22 | selector = ruleSet.selector.lower()
23 |
24 | if selector.find('@media') != -1:
25 | return True
26 | if selector.find('@-moz-document') != -1:
27 | return True
28 |
29 | word = existsAppearanceWords(selector)
30 | if word is not None:
31 | self.errorMsg = self.errorMsg_origin % word
32 | return False
33 |
34 | return True
35 |
--------------------------------------------------------------------------------
/ckstyle/plugins/validators/ValidatorFactory.py:
--------------------------------------------------------------------------------
1 | from ckstyle.cmdconsole.ConsoleClass import console
2 |
3 | def doValidate(name, value):
4 | pluginName = camelCase(name) + 'Validator'
5 | pluginClass = NullValidator
6 | try:
7 | plugin = __import__('ckstyle.plugins.validators.' + pluginName, fromlist = [pluginName])
8 | #plugin = __import__(pluginName, fromlist = [pluginName])
9 | if hasattr(plugin, pluginName):
10 | pluginClass = getattr(plugin, pluginName)
11 | else:
12 | console.error('%s should exist in %s.py' % (pluginName, pluginName))
13 | except ImportError as e:
14 | pass
15 | instance = pluginClass(name, value)
16 | return instance.validate()
17 |
18 | class NullValidator():
19 | def __init__(self, name, value):
20 | pass
21 | def validate(self):
22 | return True, None
23 |
24 | def camelCase(name):
25 | splited = name.split('-')
26 |
27 | collector = []
28 | for x in splited:
29 | collector.append(x.capitalize())
30 | return ''.join(collector)
31 |
32 | if __name__ == '__main__':
33 | print(doValidate('margin', '10 10 10 10'))
34 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDNoStarInSelector.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDNoStarInSelector(RuleSetChecker):
7 |
8 | '''{
9 | "summary":"不要在选择器中使用星号",
10 | "desc":"禁止在选择器中加入*来选择所有元素,例如:
11 |
12 | *html *+html *:not等几种特殊hack除外"
13 | }'''
14 |
15 | def __init__(self):
16 | self.id = 'no-star-in-selector'
17 | self.errorLevel = ERROR_LEVEL.ERROR
18 | self.errorMsg = 'please remove low performance selector "*" from "${selector}"'
19 |
20 | def check(self, ruleSet, config):
21 | selector = ruleSet.selector
22 | if selector.find('*') == -1:
23 | return True
24 |
25 | replaced = selector.replace(' ', '')
26 | if replaced.startswith('*html') or replaced.startswith('*+html'):
27 | return True
28 |
29 | if replaced.find('*:not') != -1:
30 | return True
31 |
32 | # give it to FEDHighPerformanceSelector.py
33 | if replaced.find('*=') != -1 and len(replaced.split('*')) == 2:
34 | return True
35 |
36 | return False
37 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDUseLowerCaseProp.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 1: "WIDTH" should use lower case, in ".name-upper"
6 | 1: "HEIGHT" should use lower case, in ".name-upper-second-attr"
7 | 1: value of "width" should use lower case, in ".value-upper"
8 | 0: can not set font-family for ".value-upper-font-family"
9 | 1: value of "font" should use lower case, in ".value-upper-font"
10 | 0: can not set font-family for ".value-upper-font"
11 | }
12 |
13 | .is-correct-lower {
14 | width: 100px;
15 | height: 200px;
16 | }
17 |
18 | .name-upper {
19 | WIDTH: 100px;
20 | }
21 |
22 | .name-upper-second-attr {
23 | width: 100px;
24 | HEIGHT: 10px;
25 | }
26 |
27 | .value-upper {
28 | width: 100PX;
29 | }
30 |
31 | .value-upper-color-text {
32 | color: RED;
33 | }
34 |
35 | .value-uppper-color {
36 | color: #FFF;
37 | }
38 |
39 | .value-upper-background {
40 | background: #FFF url('xxxx.png') no-repeat left top;
41 | }
42 |
43 | .value-upper-font-family {
44 | font-family: 'Microsoft Yahei';
45 | }
46 |
47 | .value-upper-font {
48 | font: ITALIC bold 12px/30px 'Courier New', Georgia, serif;
49 | }
50 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDNoSimpleNumberInSelector.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | import re
6 | pattern = re.compile('\d+')
7 |
8 | class FEDNoSimpleNumberInSelector(RuleSetChecker):
9 |
10 | '''{
11 | "summary":"不要在选择器中使用简单数字",
12 | "desc":"在业务代码的css中,选择器中不要使用简单的 1, 2, 3 来进行命名,下面的命名方式就是错误的:
13 | .test1 .main1,但是允许使用 v1 step1 item1
14 | 来代表版本、步骤、第几个元素的意思"
15 | }'''
16 |
17 | def __init__(self):
18 | self.id = 'number-in-selector'
19 | self.errorLevel = ERROR_LEVEL.WARNING
20 | self.errorMsg = 'do not simply use 1,2,3 as selector(use v1/step1/item1), in "${selector}"'
21 |
22 | def check(self, ruleSet, config):
23 | selector = ruleSet.selector
24 |
25 | if selector.find('@media') != -1:
26 | return True
27 |
28 | found = pattern.findall(selector)
29 | for x in found:
30 | if selector.find('v' + x) == -1 and selector.find('step' + x) == -1 and selector.find('item' + x) == -1 and selector.find('h' + x) == -1 :
31 | return False
32 | return True
33 |
--------------------------------------------------------------------------------
/ckstyle/entity/NestedStatement.py:
--------------------------------------------------------------------------------
1 | from .EntityUtil import Cleaner, ALL
2 |
3 | class NestedStatement():
4 | def __init__(self, selector, statement, comments, styleSheet = None):
5 | self.extra = True
6 | self.nested = True
7 | self.selector = selector.strip()
8 | self.statement = statement.strip()
9 | self.roughStatement = statement
10 | self.comments = comments.strip()
11 | self.styleSheet = styleSheet
12 |
13 | self.fixedSelector = ''
14 | self.fixedStatement = ''
15 |
16 | self.browser = ALL
17 | self.toBeUsed = {}
18 |
19 | def rebase(self):
20 | self.fixedSelector = ''
21 | self.fixedStatement = ''
22 |
23 | def compress(self, browser = ALL):
24 | if not self.browser & browser:
25 | return ''
26 | return self.fixedSelector + self._compressedStatement()
27 |
28 | def fixed(self, config):
29 | return self.fixedSelector + ' {\n ' + '\n '.join(self.fixedStatement.split('\n')) + '\n}'
30 |
31 | def _compressedStatement(self):
32 | return '{' + Cleaner.clean(self.fixedStatement) + '}'
33 |
34 | def __str__(self):
35 | return '%s' % self.statement
36 |
--------------------------------------------------------------------------------
/ckstyle/browsers/Analyser.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .BinaryRule import *
5 |
6 | basic = {
7 | 'ie6' : IE6,
8 | 'ie7' : IE7,
9 | 'ie8' : IE8,
10 | 'ie9' : IE9PLUS,
11 | 'ie10': IE9PLUS,
12 | 'chrome' : CHROME,
13 | 'firefox' : FIREFOX,
14 | 'opera' : OPERA,
15 | 'safari' : SAFARI,
16 | 'std' : STD | NONEIE
17 | }
18 |
19 | mapping = {
20 | 'ie' : ALLIE,
21 | 'ie9plus' : IE9PLUS,
22 | 'std' : STD | NONEIE
23 | }
24 |
25 | mapping.update(basic)
26 |
27 | allBrowsers = ','.join([x for x in mapping.keys() if x != 'webkit' and x != 'ie9plus'])
28 |
29 | def analyse(text):
30 | if not text or text == '' or text=='none' or text == 'false':
31 | return None
32 | if text == 'all':
33 | text = allBrowsers
34 | text = text.lower()
35 | splited = text.split(',')
36 | browsers = {}
37 |
38 | for browser in splited:
39 | if mapping.has_key(browser):
40 | # 不管如何选择,STD的总是要显示的
41 | browsers[browser] = mapping[browser] | STD
42 | return browsers
43 |
44 | def whatIs(code):
45 | result = []
46 | for name, value in basic.items():
47 | if value & code:
48 | result.append(name)
49 |
50 | return ','.join(result)
51 |
52 | if __name__ == '__main__':
53 | print(analyse('ie6,std'))
54 | print(whatIs(FIREFOX))
--------------------------------------------------------------------------------
/tests/unit/check/FEDMultiLineBraces.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 2: should "enter" after the opening brace in ".multi-no-enter-before-open-brace"
6 | 2: should "enter" before the closing brace in ".multi-brace-no-enter"
7 | 2: extra "space" after the opening brace in ".multi-space-after-brace"
8 | 2: every name/value should in single line in ".multi-brace-no-enter-another"
9 | 2: should have (only) one "space" before value of "width" in ".multi-brace-no-enter"
10 | 2: should have (only) one "space" before value of "width" in ".multi-brace-no-enter-another"
11 |
12 | 1: ".multi-a, .multi-no-enter-before-open-brace, .multi-space-after-brace, .multi-brace-no-enter, .multi-brace-no-enter-another" contains the same rules in "FEDMultiLineBraces.css"
13 | }
14 |
15 | .multi-a {
16 | width: 100px;
17 | height: 100px;
18 | }
19 |
20 | .multi-no-enter-before-open-brace { width: 100px;
21 | height: 100px;
22 | }
23 |
24 | .multi-space-after-brace {
25 | width: 100px;
26 | height: 100px;
27 | }
28 |
29 | .multi-brace-no-enter {
30 | width:100px;
31 | height: 100px; }
32 |
33 | .multi-brace-no-enter-another {
34 | width:100px; height: 100px; }
35 |
--------------------------------------------------------------------------------
/ckstyle/plugins/Base.py:
--------------------------------------------------------------------------------
1 | class ERROR_LEVEL:
2 | ERROR = 0
3 | WARNING = 1
4 | LOG = 2
5 |
6 | class Checker():
7 | def __init__(self):
8 | self.errorLevel = ERROR_LEVEL.LOG
9 | self.errorMsg = '_default_msg'
10 | def check(self, xxx, config):
11 | pass
12 | def getMsg(self):
13 | return self.errorMsg
14 | def getLevel(self):
15 | return self.errorLevel
16 |
17 | class RuleChecker(Checker):
18 | def __init__(self):
19 | self.errorLevel = ERROR_LEVEL.LOG
20 | self.errorMsg = ''
21 | def check(self, rule, config):
22 | return True
23 |
24 | class RuleSetChecker(Checker):
25 | def __init__(self):
26 | self.errorLevel = ERROR_LEVEL.LOG
27 | self.errorMsg = ''
28 | def check(self, ruleSet, config):
29 | return True
30 |
31 | class StyleSheetChecker(Checker):
32 | def __init__(self):
33 | self.errorLevel = ERROL_LEVEL.LOG
34 | self.errorMsg = ''
35 | def check(self, styleSheet, config):
36 | return True
37 |
38 | class ExtraChecker(Checker):
39 | def __init__(self):
40 | self.errorLevel = ERROR_LEVEL.LOG
41 | self.errorMsg = ''
42 | def check(self, ruleSet, config):
43 | return True
--------------------------------------------------------------------------------
/tests/unit/check/FEDSingleLineSpaces.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 2: should have one "space" before "width" in ".single-line-no-space"
6 | 2: should have "only one space" after the opening brace in ".single-line-no-space"
7 | 2: should have one "space" before "height" in ".single-line-no-space"
8 | 2: should not have "space" after "width" in ".extra-space-end"
9 | 2: should have one "space" before value of "width" in ".extra-space-before-value"
10 | 2: found extra "space" after value of "width" in ".extra-space-after-value"
11 | 1: each rule in ".extra-space-after-value" need semicolon in the end, "width" has not
12 |
13 | 1: ".extra-space-end, .extra-space-before-value, .extra-space-after-value" contains the same rules in "FEDSingleLineSpaces.css"
14 | 1: ".correct-single-line-styles, .single-line-no-space" contains the same rules in "FEDSingleLineSpaces.css"
15 | }
16 |
17 | .correct-single-line-styles { width: 100px; height: 100px; }
18 | .single-line-no-space {width:100px;height:100px;}
19 | .extra-space-end { width :100px; }
20 | .extra-space-before-value { width:100px; }
21 | .extra-space-after-value { width: 100px ; }
22 | .extra-space-after-value { width: 100px }
23 |
--------------------------------------------------------------------------------
/tests/unit/parser/NestedStatement.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _withoutEnter()
5 |
6 | def _withoutEnter():
7 | parser = CssParser("@keyframes 'blala' {10% {width: 100px;} 20% {width: 300px;}}@keyframes 'blala2' {10% {width: 200px;}}.test {_width : 100px;}")
8 | parser.doParse()
9 | ok(parser is not None, 'parser is not None')
10 | ok(parser.styleSheet is not None, 'parser.styleSheet is not None')
11 | equal(len(parser.styleSheet.getRuleSets()), 3, 'three rule set')
12 |
13 | styleSheet = parser.styleSheet
14 | first = styleSheet.getRuleSets()[0]
15 | ok(first.extra, 'keyframes is extra')
16 | equal(first.selector, "@keyframes 'blala'", 'it is @keyframes')
17 | equal(first.statement, '10% {width: 100px;} 20% {width: 300px;}', 'statement is ok')
18 |
19 | second = styleSheet.getRuleSets()[1]
20 | ok(second.extra, 'keyframes is extra')
21 | equal(second.selector, "@keyframes 'blala2'", 'it is @keyframes')
22 | equal(second.statement, '10% {width: 200px;}', 'statement is ok')
23 |
24 | rule = styleSheet.getRuleSets()[2]
25 | ok(not rule.extra, 'not extra')
26 | equal(rule.selector, '.test', 'selector is ok')
27 | equal(rule.getRuleByName('width').value, '100px', 'value is ok')
28 |
--------------------------------------------------------------------------------
/tests/unit/commandline/Try.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | res = doCheck([' ', 'check', '-p', realpath('./_test.css')])
5 | res = res.find('[ERROR] 1. should add @author in the head of')
6 | equal(res, 0, 'check by cmd line is ok')
7 |
8 | res = doFix([' ', 'fix', '-p', realpath('./_test.css')])
9 | expect = '''.test {
10 | width: 100px;
11 | }'''
12 | equal(res, expect, 'fix by cmd line is ok')
13 |
14 |
15 | res = doCompress([' ', 'compress', '-p', realpath('./_test.css')])
16 | expect = '''.test{width:100px}'''
17 | equal(res, expect, 'compress by cmd line is ok')
18 |
19 | res = doCompress([' ', 'compress', '-p', realpath('./_test_browsers.css')])
20 | expect = '''.test{width:100px}.test[prop]{width:100px}'''
21 | equal(res, expect, 'compress by cmd line is ok')
22 |
23 | res = doCompress([' ', 'compress', '-p', '--browsers=ie6', realpath('./_test_browsers.css')])
24 | expect = '''.test{width:100px}'''
25 | equal(res, expect, 'compress by cmd line ie6 is ok')
26 |
27 | res = doCompress([' ', 'compress', '-p', '--browsers=ie7', realpath('./_test_browsers.css')])
28 | expect = '''.test{width:100px}.test[prop]{width:100px}'''
29 | equal(res, expect, 'compress by cmd line ie7 is ok')
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDFixNestedStatement.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDFixNestedStatement(ExtraChecker):
7 |
8 | '''{
9 | "summary":"修复嵌套的CSS",
10 | "desc":"@keyframes, @media之类的"
11 | }'''
12 |
13 | def __init__(self):
14 | self.id = 'fix-nested-ruleset'
15 | self.errorLevel = ERROR_LEVEL.ERROR
16 | self.errorMsg = ''
17 | self.private = True
18 |
19 | def check(self, ruleSet, config):
20 | return True
21 |
22 | def fix(self, ruleSet, config):
23 | if not ruleSet.nested:
24 | return
25 | ruleSet.fixedSelector = ruleSet.fixedSelector.replace('"', '\'')
26 | statement = ruleSet.fixedStatement
27 | if (hasattr(config, 'operation') and getattr(config, 'operation') == 'compress'):
28 | from ckstyle.doCssCompress import prepare
29 | checker = prepare(statement, '', config)
30 | # 嵌套的CSS,如果是压缩,也需要精简
31 | msg = checker.doCompress(config._curBrowser)
32 | ruleSet.fixedStatement = msg
33 | else:
34 | from ckstyle.doCssFix import doFix
35 | checker, msg = doFix(statement, '', config)
36 | ruleSet.fixedStatement = msg
37 |
--------------------------------------------------------------------------------
/tests/unit/combiner/Try.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | msg = doCssFileCompress('_test.css')
5 | equal(msg, '@import (url-here);.test,.test2,.test3,.test4,.test5{_width:100px;*height:100px}.test6{display:none;_width:100px;*height:100px}', 'totally compressed')
6 |
7 | msg = doCssFileCompress('_test_different_order.css')
8 | equal(msg, '.test1,.test2,.test3,.test4,.test5{*display:none;_display:inline-block;width:100px;height:200px;border:1px solid #FFF}', 'totally compressed')
9 |
10 | msg = doCssFileCompress('_with_margin.css')
11 | equal(msg, '.test,.test2,.test3,.test4,.test5{_width:100px;*height:100px;margin:20px 10px 10px}.test6{display:none;_width:100px;*height:100px}', 'margin compress ok')
12 |
13 | msg = doCssFileCompress('_just_margin.css')
14 | equal(msg, '.test,.test2,.test3,.test4{margin:20px 10px 10px}', 'just margin compress ok')
15 |
16 | msg = doCssFileCompress('_with_padding.css')
17 | equal(msg, '.test,.test2,.test3,.test4,.test5{_width:100px;*height:100px;padding:20px 10px 10px}.test6{display:none;_width:100px;*height:100px}', 'padding compress ok')
18 |
19 | msg = doCssFileCompress('_just_padding.css')
20 | equal(msg, '.test,.test2,.test3,.test4{padding:20px 10px 10px}', 'just padding compress ok')
21 |
--------------------------------------------------------------------------------
/ckstyle/reporter/helper.py:
--------------------------------------------------------------------------------
1 |
2 | def fill(obj):
3 | def fillRuleSet( obj):
4 | errorMsg = obj["errorMsg"]
5 | if errorMsg.find('${selector}') == -1:
6 | errorMsg = errorMsg + ' (from "' + obj["selector"] + '")'
7 | else:
8 | errorMsg = errorMsg.replace('${selector}', obj["selector"])
9 | return errorMsg
10 |
11 | def fillStyleSheet( obj):
12 | errorMsg = obj["errorMsg"]
13 | if errorMsg.find('${file}') == -1:
14 | errorMsg = errorMsg + ' (from "' + obj["file"] + '")'
15 | else:
16 | errorMsg = errorMsg.replace('${file}', obj["file"])
17 | return errorMsg
18 |
19 | def fillRule(obj):
20 | errorMsg = obj["errorMsg"]
21 | if errorMsg.find('${selector}') == -1:
22 | errorMsg = errorMsg + ' (from "' + obj["selector"] + '")'
23 | else:
24 | errorMsg = errorMsg.replace('${selector}', obj["selector"])
25 | errorMsg = errorMsg.replace('${name}', obj["name"])
26 | errorMsg = errorMsg.replace('${value}', obj["value"])
27 | return errorMsg
28 |
29 | level = obj["level"]
30 | if level == 'rule':
31 | return fillRule(obj)
32 | elif level == 'ruleset':
33 | return fillRuleSet(obj)
34 | elif level == 'stylesheet':
35 | return fillStyleSheet(obj)
36 | return obj["errorMsg"]
--------------------------------------------------------------------------------
/tests/unit/fix/ExtraCss3Prefix.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _not_listed()
5 | _both_not_listed()
6 |
7 | def _both_not_listed():
8 | css = '''html {
9 | -webkit-hyphens: auto;
10 | -moz-hyphens: auto;
11 | -ms-hyphens: auto;
12 | -o-hyphens: auto;
13 | hyphens: auto;
14 | -webkit-user-select: none;
15 | -moz-user-select: none;
16 | -ms-user-select: none;
17 | }'''
18 |
19 | expectedFixed = '''html {
20 | -webkit-hyphens: auto;
21 | -moz-hyphens: auto;
22 | -ms-hyphens: auto;
23 | -o-hyphens: auto;
24 | hyphens: auto;
25 | -webkit-user-select: none;
26 | -moz-user-select: none;
27 | -ms-user-select: none;
28 | }'''
29 |
30 | fixer, msg = doFix(css, '')
31 | equal(msg.strip(), expectedFixed.strip(), 'both css3 prop not listed is ok')
32 |
33 | def _not_listed():
34 | css = '''.html {
35 | -moz-box-sizing: 1px;
36 | -webkit-box-sizing: 1px;
37 | box-sizing: 1px;
38 | }'''
39 |
40 | expectedFixed = '''.html {
41 | -webkit-box-sizing: 1px;
42 | -moz-box-sizing: 1px;
43 | box-sizing: 1px;
44 | }'''
45 |
46 | fixer, msg = doFix(css, '')
47 | equal(msg.strip(), expectedFixed.strip(), 'css3 prefix box-sizing is ok')
48 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDCombineInToOne.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 1: should combine "border-width,border-style" to "border" in ".test-border"
6 | 1: should combine "background-color,background-image,background-repeat" to "background" in ".test-background"
7 | 1: should combine "padding-top,padding-bottom" to "padding" in ".test-padding"
8 | 1: should combine "margin,margin-bottom" to "margin" in ".test-margin"
9 | 1: should combine "font-size,font-family,font-style" to "font" in ".test-font"
10 | 0: can not set font-family for ".test-font"
11 | }
12 |
13 | .test-border {
14 | border-width: 1px;
15 | border-style: solid;
16 | }
17 |
18 | .test-background {
19 | background-color: red;
20 | background-image: url(xxxx);
21 | background-repeat: no-repeat;
22 | }
23 |
24 | .test-padding {
25 | padding-top: 10px;
26 | padding-bottom: 12px;
27 | }
28 |
29 | .test-margin {
30 | margin: 10px;
31 | margin-bottom: 10px;
32 | }
33 |
34 | .test-font {
35 | font-size: 12px;
36 | font-family: 'Microsoft Yahei';
37 | font-style: italic;
38 | }
39 |
40 | .test-font-only-two {
41 | font-size: 12px;
42 | font-style: italic;
43 | }
44 |
45 | .help-message-btn {
46 | padding: 5px 0;
47 | *padding: 6px 0 3px;
48 | }
--------------------------------------------------------------------------------
/tests/unit/entity/ExtraStatement.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _basic()
5 | _opm()
6 | _other()
7 |
8 | def _basic():
9 | stmt = ExtraStatement('@import', '@import url("fjdaslkjfdsa")', None)
10 | ok(stmt.extra, '@import is extra statement')
11 | equal(stmt.operator, '@import', 'operator is @important')
12 | equal(stmt.statement, '@import url("fjdaslkjfdsa")', 'statement is ok')
13 | equal(stmt.styleSheet, None, 'no style sheet')
14 | ok(stmt.isImport(), 'yes, it is import statement')
15 | ok(not stmt.isOpmOperator(), 'no, it is not opm operator')
16 |
17 | def _opm():
18 | stmt = ExtraStatement('@-css-compiler-xxx', '@-css-compiler-xxx fdjafdjafda;', None)
19 | ok(stmt.isOpmOperator(), 'yes, it is opm operator')
20 |
21 | stmt = ExtraStatement('@-css-compiler-xxx', '@-css-compiler-xxx fdjafdjafda;', None)
22 | ok(stmt.isOpmOperator(), 'yes, it is opm operator')
23 |
24 | def _other():
25 | stmt = ExtraStatement('@namspace', '@namspace fdjafdjafda;', None)
26 | ok(not stmt.isOpmOperator(), 'no, it is not opm operator')
27 | ok(not stmt.isImport(), 'no, it is not import')
28 |
29 | stmt = ExtraStatement('@charset', '@charset utf-8;', None)
30 | ok(not stmt.isOpmOperator(), 'no, it is not opm operator')
31 | ok(not stmt.isImport(), 'no, it is not import')
32 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDSingleLineSelector.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDSingleLineSelector(RuleSetChecker):
7 |
8 | '''{
9 | "summary":"单行的选择器检查",
10 | "desc":"单行的选择器检查内容,请参考多行选择器检查和人人FED CSS编码规范"
11 | }'''
12 |
13 | def __init__(self):
14 | self.id = 'single-line-selector'
15 | self.errorLevel = ERROR_LEVEL.LOG
16 | self.errorMsg_noEnterInSingleSelector = 'should not "enter" at the end of "${selector}"'
17 | self.errorMsg_multiSelectorBeforeSemicolon = 'should not have "space" after semicolon in "${selector}"'
18 | self.errorMsg_shouldNotStartsWithSpace = 'should start with "space" in "${selector}"'
19 | self.errorMsg = ''
20 |
21 | def check(self, ruleSet, config):
22 | selector = ruleSet.roughSelector
23 | if selector.find(',') != -1:
24 | return True
25 |
26 | if selector.lstrip().find('\n') != -1:
27 | self.errorMsg = self.errorMsg_noEnterInSingleSelector
28 | return False
29 |
30 | splited = selector.split('\n')
31 | realSelector = splited[len(splited) - 1]
32 |
33 | if realSelector.startswith(' '):
34 | self.errorMsg = self.errorMsg_shouldNotStartsWithSpace
35 | return False
36 |
37 | return True
38 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDDoNotSetStyleForSimpleSelector.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | from .helper import isSimpleSelector
6 |
7 | class FEDDoNotSetStyleForSimpleSelector(RuleSetChecker):
8 |
9 | '''{
10 | "summary":"不要为简单选择器设置样式",
11 | "desc":"一些简单的选择器,比如:
12 | .nav/.list/.content
13 | 非常容易造成属性的相互覆盖,因此在写这样的选择器时,最好加上前缀,比如
14 | .module-name .nav
15 | 工具现有的简单选择器判断,请参考:
16 | plugins/helper.py"
17 | }'''
18 |
19 | def __init__(self):
20 | self.id = 'no-style-for-simple-selector'
21 | self.errorLevel = ERROR_LEVEL.ERROR
22 | self.errorMsg_rough = 'should not set style for "%s" in "${selector}"'
23 | self.errorMsg = ''
24 |
25 | def check(self, ruleSet, config):
26 | selector = ruleSet.selector.lower()
27 |
28 | if selector.find('@media') != -1:
29 | return True
30 |
31 | if selector.find('@-moz-document') != -1:
32 | return True
33 |
34 | selectors = selector.split(',')
35 | for s in selectors:
36 | s = s.strip()
37 | if isSimpleSelector(s):
38 | self.errorMsg = self.errorMsg_rough % s
39 | return False
40 | return True
41 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDNoZeroBeforeDot.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDNoZeroBeforeDot(RuleChecker):
7 |
8 | '''{
9 | "summary":"删除0.x前面的0",
10 | "desc":" 0.xxx 前面的 0 是可以删除的,以实现更好的压缩。例如
11 | 0.3px ==> .3px
12 | rgba(0,0,0,0.3)
13 | ==>
14 | rgba(0,0,0,.3)"
15 | }'''
16 |
17 | def __init__(self):
18 | self.id = 'no-zero-before-dot'
19 | self.errorLevel = ERROR_LEVEL.WARNING
20 | self.errorMsg = 'zero should be removed when meet 0.xxx in "${selector}"'
21 |
22 | def check(self, rule, config):
23 | value = rule.value
24 |
25 | if self._startsWithZeroDot(value):
26 | return False
27 |
28 | values = rule.value.split(' ')
29 | for v in values:
30 | if self._startsWithZeroDot(v.strip()):
31 | return False
32 |
33 | return True
34 |
35 | def fix(self, rule, config):
36 | fixedValue = rule.fixedValue
37 | for v in fixedValue.split(' '):
38 | if self._startsWithZeroDot(v):
39 | rule.fixedValue = rule.fixedValue.replace(v, v[1:])
40 |
41 | def _startsWithZeroDot(self, value):
42 | return value.startswith('0.')
43 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDCommentLengthLessThan80.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 2: comment for ".test-comment-length" length should less than 80 per line
6 | 2: comment for ".test-chn-length" length should less than 80 per line
7 | 2: comment for ".test-chn-and-eng-length" length should less than 80 per line
8 | 2: comment for ".test-comment-multiline-length" length should less than 80 per line
9 | 1: ".test-comment-length, .test-chn-length, .test-chn-and-eng-length, .test-comment-multiline-length, .correct-comment" contains the same rules in "FEDCommentLengthLessThan80.css"
10 | }
11 |
12 | /** fdasfdsaffffffffffffffffffffffffffffffffffffffffffffffffffffffffftested code */
13 | .test-comment-length {
14 | width: 100px;
15 | }
16 |
17 | /* 中文字符中文字符中文字符中文字符中文字符中文字符中文字符中文字符中文字符中文字符 */
18 | .test-chn-length {
19 | width: 100px;
20 | }
21 |
22 | /* fdasjklfjdlskafdlasjfldsajfd符中文字符中文字符中文字符中文字符中文字符中文字符 */
23 | .test-chn-and-eng-length {
24 | width: 100px;
25 | }
26 |
27 | /*
28 | * fdjlfdfdsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
29 | * fffffffffffffffffffjfkldasjflkda
30 | */
31 | .test-comment-multiline-length {
32 | width: 100px;
33 | }
34 |
35 | /*
36 | * fjdklajfdlsjflkdsjkfdsjakl
37 | */
38 | .correct-comment {
39 | width: 100px;
40 | }
41 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDFontSizeShouldBePtOrPx.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDFontSizeShouldBePtOrPx(RuleChecker):
7 |
8 | '''{
9 | "summary":"字体的单位必须用px或pt",
10 | "desc":"字体的单位可以有很多种,比如 px pt em % 等等,为了统一取值,统一要求为 px/pt , 例如:
11 | font-size: 12px;
12 | font-size: 14pt;"
13 | }'''
14 |
15 | def __init__(self):
16 | self.id = 'font-unit'
17 | self.errorLevel = ERROR_LEVEL.ERROR
18 | self.errorMsg_ptOrPx = 'font-size unit should be px/pt in "${selector}"'
19 | self.errorMsg_xsmall = 'font-size should not be small/medium/large in "${selector}"'
20 | self.errorMsg = ''
21 |
22 | def check(self, rule, config):
23 | if rule.name != 'font-size':
24 | return True
25 |
26 | value = rule.value
27 | if value.find('small') != -1 or value.find('medium') != -1 or value.find('large') != -1:
28 | self.errorMsg = self.errorMsg_xsmall
29 | return False
30 |
31 | if value == '0':
32 | return True
33 |
34 | if value.endswith('pt'):
35 | return True
36 |
37 | if value.endswith('px'):
38 | return True
39 |
40 | self.errorMsg = self.errorMsg_ptOrPx
41 | return False
42 |
--------------------------------------------------------------------------------
/tests/unit/compress/_selectors_from_kimblim.css:
--------------------------------------------------------------------------------
1 | div {width:100px}
2 | div span {width:100px}
3 | :link {width:100px}
4 | div:active {width:100px}
5 | div:visited {width:100px}
6 | div:first-line {width:100px}
7 | div:first-letter {width:100px}
8 | div.classname {width:100px}
9 | div#id {width:100px}
10 | .classname.classname {width:100px}
11 | * {width:100px}
12 | div > span {width:100px}
13 | div:first-child {width:100px}
14 | div:hover {width:100px}
15 | div:focus {width:100px}
16 | div + span {width:100px}
17 | div[attr] {width:100px}
18 | div[attr="name"] {width:100px}
19 | div[attr~="name"] {width:100px}
20 | div:before {width:100px}
21 | div:after {width:100px}
22 | div ~ span {width:100px}
23 | div[attr^="name"] {width:100px}
24 | div[attr$="name"] {width:100px}
25 | div[attr*="name"] {width:100px}
26 | div[attr|="name"] {width:100px}
27 | div:root {width:100px}
28 | div:nth-of-type {width:100px}
29 | div:nth-last-of-type {width:100px}
30 | div:first-of-type {width:100px}
31 | div:last-of-type {width:100px}
32 | div:only-of-type {width:100px}
33 | div:only-child {width:100px}
34 | div:last-child {width:100px}
35 | div:nth-child {width:100px}
36 | div:nth-last-child {width:100px}
37 | div:empty {width:100px}
38 | div:target {width:100px}
39 | div:checked {width:100px}
40 | div::selection {width:100px}
41 | div:enabled {width:100px}
42 | div:disabled {width:100px}
43 | div:not(s) {width:100px}
--------------------------------------------------------------------------------
/editor-plugins/fixstyle_for_sublime_text_2/FixStylePlugin.py:
--------------------------------------------------------------------------------
1 | #encoding=utf-8
2 | import sublime, sublime_plugin
3 | import os
4 | from helper import getCkstylePath
5 |
6 | class FixstyleCommand(sublime_plugin.TextCommand):
7 |
8 | def run(self, edit):
9 | path = os.path.realpath(self.view.file_name().decode('utf-8'))
10 | if os.path.splitext(path)[1] != '.css':
11 | sublime.error_message('Not a CSS file!')
12 | return
13 | cmd = getCkstylePath() + ' fix -p "' + path + '"'
14 | returnValue = os.popen3(cmd)
15 |
16 | returnValue = returnValue[1].read() + returnValue[2].read()
17 |
18 | if returnValue.find('[console.error]') != -1:
19 | sublime.error_message(returnValue)
20 | else:
21 | region = sublime.Region(0, self.view.size())
22 | msg = self.getRealMsg(returnValue)
23 | try:
24 | self.view.replace(edit, region, msg)
25 | except Exception:
26 | sublime.error_message('ERROR, maybe because file encoding charset is not utf-8');
27 | self.view.end_edit(edit)
28 |
29 | def getRealMsg(self, msg):
30 | for charset in ['utf-8', 'gbk', 'gb2312']:
31 | try:
32 | msg.decode(charset)
33 | return msg.decode(charset)
34 | except Exception:
35 | pass
36 | return msg
37 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDHackRuleSetInCorrectWay.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDHackRuleSetInCorrectWay(ExtraChecker):
7 |
8 | '''{
9 | "summary":"hack规则时的检查",
10 | "desc":"针对Firefox Opera Safari等浏览器的 hack 方式, 人人FED CSS编码规范中有详细的描述,
11 | 不允许使用规定之外的方式进行规则级别的hack"
12 | }'''
13 |
14 | def __init__(self):
15 | self.id = 'hack-ruleset'
16 | self.errorLevel = ERROR_LEVEL.ERROR
17 | self.errorMsg = 'not correct hacking way in "${selector}"'
18 |
19 | def check(self, ruleSet, config):
20 | if not ruleSet.nested:
21 | return True
22 |
23 | selector = ruleSet.selector.strip()
24 | if selector.find('@-moz-document') != -1:
25 | if selector != '@-moz-document url-prefix()':
26 | return False
27 |
28 | if selector.find('-webkit-min-device-pixel-ratio:0') != -1:
29 | if selector != '@media screen and (-webkit-min-device-pixel-ratio:0)' and selector.find('-webkit-min-device-pixel-ratio:10000') == -1:
30 | return False
31 |
32 | if selector.find('-webkit-min-device-pixel-ratio:10000') != -1:
33 | if selector.find('@media all') == -1 or selector.find('not all and') == -1 or selector.find('-webkit-min-device-pixel-ratio:0') == -1:
34 | return False
35 |
36 | return True
37 |
--------------------------------------------------------------------------------
/editor-plugins/fixstyle_for_sublime_text_2/CssCompressPlugin.py:
--------------------------------------------------------------------------------
1 | #encoding=utf-8
2 | import sublime, sublime_plugin
3 | import os
4 | from helper import getCkstylePath
5 |
6 | class CsscompressCommand(sublime_plugin.TextCommand):
7 |
8 | def run(self, edit):
9 | path = os.path.realpath(self.view.file_name()).decode('utf-8')
10 | if os.path.splitext(path)[1] != '.css':
11 | sublime.error_message('Not a CSS file!')
12 | return
13 |
14 | cmd = getCkstylePath() + ' compress -p "' + path + '"'
15 | returnValue = os.popen3(cmd)
16 |
17 | returnValue = returnValue[1].read() + returnValue[2].read()
18 |
19 | if returnValue.find('[console.error]') != -1:
20 | sublime.error_message(returnValue)
21 | else:
22 | region = sublime.Region(0, self.view.size())
23 | msg = self.getRealMsg(returnValue)
24 | try:
25 | self.view.replace(edit, region, msg)
26 | except Exception:
27 | sublime.error_message('ERROR, maybe because file encoding charset is not utf-8');
28 | self.view.end_edit(edit)
29 |
30 | def getRealMsg(self, msg):
31 | for charset in ['utf-8', 'gbk', 'gb2312']:
32 | try:
33 | msg.decode(charset)
34 | return msg.decode(charset)
35 | except Exception:
36 | pass
37 | return msg
38 |
--------------------------------------------------------------------------------
/editor-plugins/fixstyle_for_sublime_text_2/FixStyleSafePlugin.py:
--------------------------------------------------------------------------------
1 | #encoding=utf-8
2 | import sublime, sublime_plugin
3 | import os
4 | from helper import getCkstylePath
5 |
6 | class FixstylesafeCommand(sublime_plugin.TextCommand):
7 |
8 | def run(self, edit):
9 | path = os.path.realpath(self.view.file_name()).decode('utf-8')
10 | if os.path.splitext(path)[1] != '.css':
11 | sublime.error_message('Not a CSS file!')
12 | return
13 |
14 | cmd = getCkstylePath() + ' fix --safeMode -p "' + path + '"'
15 | returnValue = os.popen3(cmd)
16 |
17 | returnValue = returnValue[1].read() + returnValue[2].read()
18 |
19 | if returnValue.find('[console.error]') != -1:
20 | sublime.error_message(returnValue)
21 | else:
22 | region = sublime.Region(0, self.view.size())
23 | msg = self.getRealMsg(returnValue)
24 | try:
25 | self.view.replace(edit, region, msg)
26 | except Exception:
27 | sublime.error_message('ERROR, maybe because file encoding charset is not utf-8');
28 | self.view.end_edit(edit)
29 |
30 | def getRealMsg(self, msg):
31 | for charset in ['utf-8', 'gbk', 'gb2312']:
32 | try:
33 | msg.decode(charset)
34 | return msg.decode(charset)
35 | except Exception:
36 | pass
37 | return msg
38 |
--------------------------------------------------------------------------------
/ckstyle/command/index.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 | from .ConsoleCommandParser import handleCkStyleCmdArgs, handleFixStyleCmdArgs, handleCompressCmdArgs
4 | from .PluginManager import install, uninstall, installcmd, uninstallcmd, handleExtraCommand
5 | import sys
6 | from .usage import newUsage
7 |
8 | def ckstyle():
9 | args = sys.argv
10 | if len(args) < 2:
11 | newUsage()
12 | return
13 |
14 | commands = {
15 | 'check' : handleCkStyleCmdArgs,
16 | 'fix': handleFixStyleCmdArgs,
17 | 'compress': handleCompressCmdArgs,
18 |
19 | 'install': install,
20 | 'add': install,
21 | 'get': install,
22 |
23 | 'uninstall': uninstall,
24 | 'remove': uninstall,
25 | 'rm': uninstall,
26 |
27 | 'installcmd': installcmd,
28 | 'addcmd': installcmd,
29 | 'getcmd': installcmd,
30 |
31 | 'rmcmd': uninstallcmd,
32 | 'removecmd': uninstallcmd,
33 | 'uninstallcmd': uninstallcmd
34 | }
35 |
36 | subcommand = args[1]
37 | if commands.has_key(subcommand):
38 | commands.get(subcommand)(args)
39 | else:
40 | handleExtraCommand(subcommand, args)
41 |
42 | #handleCkStyleCmdArgs()
43 |
44 | #def fixstyle():
45 | # handleFixStyleCmdArgs()
46 |
47 | #def compress():
48 | # handleCompressCmdArgs()
49 |
50 | #def main():
51 | # ckstyle()
52 |
53 | if __name__ == '__main__':
54 | ckstyle()
55 |
--------------------------------------------------------------------------------
/editor-plugins/fixstyle_for_sublime_text_2/FixStyleSingleLinePlugin.py:
--------------------------------------------------------------------------------
1 | #encoding=utf-8
2 | import sublime, sublime_plugin
3 | import os
4 | from helper import getCkstylePath
5 |
6 | class FixstylesinglelineCommand(sublime_plugin.TextCommand):
7 |
8 | def run(self, edit):
9 | path = os.path.realpath(self.view.file_name()).decode('utf-8')
10 | if os.path.splitext(path)[1] != '.css':
11 | sublime.error_message('Not a CSS file!')
12 | return
13 |
14 | cmd = getCkstylePath() + ' fix -p --singleLine "' + path + '"'
15 | returnValue = os.popen3(cmd)
16 |
17 | returnValue = returnValue[1].read() + returnValue[2].read()
18 |
19 | if returnValue.find('[console.error]') != -1:
20 | sublime.error_message(returnValue)
21 | else:
22 | region = sublime.Region(0, self.view.size())
23 | msg = self.getRealMsg(returnValue)
24 | try:
25 | self.view.replace(edit, region, msg)
26 | except Exception:
27 | sublime.error_message('ERROR, maybe because file encoding charset is not utf-8');
28 | self.view.end_edit(edit)
29 |
30 | def getRealMsg(self, msg):
31 | for charset in ['utf-8', 'gbk', 'gb2312']:
32 | try:
33 | msg.decode(charset)
34 | return msg.decode(charset)
35 | except Exception:
36 | pass
37 | return msg
38 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDNoExpression.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 0: should not use expression in ".abcd"
6 | 0: should not use expression in ".abcd:hover"
7 | 0: should not use expression in ".abcdef:hover"
8 | 0: should not use expression in ".expression-without-this-equals"
9 | 0: should add hack for expression in ".expression-with-this-equals"
10 |
11 | 1: ".abcd, .abcd:hover, .abcdef:hover" contains the same rules in "FEDNoExpression.css"
12 | 1: ".expression-with-expressions, * html .with-star-html-do-not-need-hack" contains the same rules in "FEDNoExpression.css"
13 | }
14 |
15 | .abcd {
16 | width: expression(function() {fdasfdas});
17 | }
18 |
19 | .abcd:hover {
20 | *width: expression(function() {fdasfdas});
21 | }
22 |
23 | .abcdef:hover {
24 | _width: expression(function() {fdasfdas});
25 | }
26 |
27 | .expression-without-this-equals {
28 | width: expression(function() {this = 11;});
29 | }
30 |
31 | .expression-with-this-equals {
32 | width: expression(function() {this.style.width = 11;});
33 | }
34 |
35 | .expression-with-expressions {
36 | *width: expression(Expressions.style.width(this,this.offsetWidth-32));
37 | }
38 |
39 | .expression-with-runtime-style {
40 | *width: expression(function() {this.runtimeStyle.width = 11;});
41 | }
42 |
43 | * html .with-star-html-do-not-need-hack {
44 | width: expression(Expressions.style.width(this,this.offsetWidth-32));
45 | }
--------------------------------------------------------------------------------
/ckstyle/entity/ExtraStatement.py:
--------------------------------------------------------------------------------
1 | from .EntityUtil import Cleaner, ALL
2 | from .RuleSet import RuleSet
3 |
4 | class ExtraStatement(RuleSet):
5 | def __init__(self, operator, statement, comment, styleSheet = None):
6 | self.extra = True
7 | self.nested = False
8 | self.selector = self.operator = operator.strip()
9 | self.comment = comment
10 | self.statement = statement
11 | self.styleSheet = styleSheet
12 |
13 | self.fixedSelector = ''
14 | self.fixedStatement = ''
15 |
16 | self.browser = ALL
17 | self.toBeUsed = {}
18 |
19 | def isImport(self):
20 | return self.operator == '@import'
21 |
22 | def rebase(self):
23 | self.fixedSelector = ''
24 | self.fixedStatement = ''
25 |
26 | def isOpmOperator(self):
27 | return self.operator.find('@-css-compiler') != -1
28 |
29 | def compress(self, browser = ALL):
30 | # do not export @-css-compiler to online
31 | if self.isOpmOperator():
32 | return ''
33 |
34 | if not self.browser & browser:
35 | return ''
36 | msg = Cleaner.clean(self.statement)
37 | if not msg.endswith('}') and not msg.endswith(';'):
38 | msg = msg + ';'
39 | return msg
40 |
41 | def fixed(self, config):
42 | return self.statement.strip() if len(self.comment) == 0 else self.comment + '\n' + self.statement.strip()
43 |
44 | def __str__(self):
45 | return '%s' % self.statement
46 |
--------------------------------------------------------------------------------
/ckstyle/command/args.py:
--------------------------------------------------------------------------------
1 | class CommandArgs():
2 | def __init__(self):
3 | self.operation = None
4 | self.errorLevel = 2
5 | self.recursive = False
6 | self.printFlag = False
7 | self.extension = '.ckstyle.txt'
8 | self.include = 'all'
9 | self.exclude = 'none'
10 | self.standard = ''
11 | self.exportJson = False
12 | self.ignoreRuleSets = ['@unit-test-expecteds']
13 | self.fixedExtension = '.fixed.css'
14 | self.fixToSingleLine = False
15 | self.compressConfig = CompressArgs()
16 | self.safeMode = False
17 | self.noBak = False
18 |
19 | # current browser
20 | self._curBrowser = None
21 |
22 | # plugin config for developers, add plugin section in ckstyle.ini
23 | #
24 | # [plugin]
25 | # pluginA = 1
26 | self.pluginConfig = {}
27 |
28 | def __str__(self):
29 | return 'errorLevel: %s\n recursive: %s\n printFlag: %s\n extension: %s\n include: %s\n exclude: %s' % (self.errorLevel, self.recursive, self.printFlag, self.extension, self.include, self.exclude)
30 |
31 | class CompressArgs():
32 | def __init__(self):
33 | self.extension = '.min.css'
34 | self.combineFile = True
35 | self.browsers = None
36 | self.noBak = False
37 |
38 | def __str__(self):
39 | return 'extension: %s, combineFile: %s, browsers: %s' % (self.recursive, self.extension, self.combineAttr, self.combineRuleSet, self.combineFile, self.browsers)
40 |
--------------------------------------------------------------------------------
/tests/unit/compress/CompressWithOrder.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _basic()
5 | _with_hack()
6 | _with_extra()
7 | _with_different_level()
8 | _with_css3_props()
9 |
10 | def _basic():
11 | msg = doCssCompress('/* @wangjeaf */ .test {width: 100px; display:none; height: 100px;}')
12 | equal(msg, '.test{display:none;width:100px;height:100px}', 'general order compress is ok')
13 |
14 | def _with_hack():
15 | msg = doCssCompress('.test {_width: 100px; *display:none;_display:none;display:block\9;*height: 100px;}')
16 | equal(msg, '.test{*display:none;_display:none;display:block\9;_width:100px;*height:100px}', 'hack order compress is ok')
17 |
18 | def _with_extra():
19 | msg = doCssCompress('@import (url-here);.test {_width: 100px; *height: 100px;}')
20 | equal(msg, '@import (url-here);.test{_width:100px;*height:100px}', 'extra order compress is ok')
21 |
22 | def _with_different_level():
23 | msg = doCssCompress('.test {cursor:pointer;color:red;line-height:10px;background-color:red;margin-top:10px;padding-bottom:10px;position:relative;}')
24 | equal(msg, '.test{position:relative;margin-top:10px;padding-bottom:10px;background-color:red;line-height:10px;color:red;cursor:pointer}', 'different levels is ok')
25 |
26 | def _with_css3_props():
27 | msg = doCssCompress('.test {-moz-transform:xxxx;-webkit-transform:xxxx;transform:xxxx;*zoom:1;width:100px;display:none;}')
28 | equal(msg, '.test{display:none;width:100px;*zoom:1;-webkit-transform:xxxx;-moz-transform:xxxx;transform:xxxx}', 'different levels is ok')
29 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDNoExpression.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDNoExpression(RuleChecker):
7 |
8 | '''{
9 | "summary":"不要使用非一次性表达式",
10 | "desc":"IE下,非一次性expression对性能有很大的影响,或许一次鼠标移动,
11 | 将触发成千上万次的expression表达式的执行,
12 | 因此,为了浏览器的更新换代,应该杜绝使用非一次性表达式。
13 | 本工具针对一次性表达式的检查,将判断expression中是否有如下两个内容:
14 | 1. Expressions
15 | 2. this.style.attrName = "
16 | }'''
17 |
18 | def __init__(self):
19 | self.id = 'no-expression'
20 | self.errorLevel = ERROR_LEVEL.ERROR
21 | self.errorMsg_use = 'should not use expression in "${selector}" '
22 | self.errorMsg_hack = 'should add hack for expression in "${selector}"'
23 | self.errorMsg = ''
24 |
25 | def check(self, rule, config):
26 | value = rule.value
27 | name = rule.name
28 | replaced = value.replace(' ', '')
29 |
30 | if value.find('expression') == -1:
31 | return True
32 |
33 | if replaced.find('Expressions') != -1 or replaced.find('this.style.' + name + '=') != -1 or replaced.find('this.runtimeStyle.' + name + '=') != -1:
34 | if rule.name == rule.strippedName:
35 | selector = rule.selector.replace(' ', '')
36 | if selector.find('*html') == -1:
37 | self.errorMsg = self.errorMsg_hack
38 | return False
39 | return True
40 |
41 | self.errorMsg = self.errorMsg_use
42 | return False
43 |
--------------------------------------------------------------------------------
/tests/unit/fix/FixFEDNoUnitAfterZero.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _rgba()
5 | _rgba_no_space()
6 |
7 | def _rgba_no_space():
8 | css = '''.test1 {
9 | box-shadow: inset 0 0px 0 0 rgba(0,0px,0px,0.1);
10 | }'''
11 |
12 | fixer, msg = doFix(css, '')
13 | expectedFixed = '''.test1 {
14 | box-shadow: inset 0 0 0 0 rgba(0,0,0,.1);
15 | }'''
16 | equal(msg, expectedFixed, 'rgba no space is also ok')
17 |
18 |
19 | def _rgba():
20 | css = '''html {
21 | -webkit-tap-highlight-color: rgba(0px, 0px, 0px, 0.1);
22 | }'''
23 |
24 | fixer, msg = doFix(css, '')
25 |
26 | styleSheet = fixer.getStyleSheet()
27 | ruleSet = styleSheet.getRuleSets()[0]
28 | color = ruleSet.getRuleByName('tap-highlight-color')
29 | equal(color.fixedValue, 'rgba(0, 0, 0, .1)', 'tap-highlight-color is fixed')
30 | equal(color.value, 'rgba(0px, 0px, 0px, 0.1)', 'tap-highlight-color is ok')
31 |
32 | css = '''.current-hot-films-ul {
33 | -webkit-transition:all 0.5s ease-in-out 0s;
34 | -moz-transition:all 0.5s ease-in-out 0s;
35 | -o-transition:all 0.5s ease-in-out 0s;
36 | transition:all 0.5s ease-in-out 0s;
37 | }'''
38 | expectedFixed = '''.current-hot-films-ul {
39 | -webkit-transition: all .5s ease-in-out 0s;
40 | -moz-transition: all .5s ease-in-out 0s;
41 | -o-transition: all .5s ease-in-out 0s;
42 | transition: all .5s ease-in-out 0s;
43 | }'''
44 |
45 | fixer, msg = doFix(css, '')
46 | equal(msg.strip(), expectedFixed.strip(), 'transition is ok, 0s can not be shorter')
47 |
--------------------------------------------------------------------------------
/tests/unit/fix/SoManyColors.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | css = '''.ui-dialog .ui-dialog-content .btn.confirm {
5 | background-color: #2996e3;
6 | background-image: -moz-linear-gradient(top, #2692de, #2d9deb);
7 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#2692de), to(#2d9deb));
8 | background-image: -webkit-linear-gradient(top, #2692de, #2d9deb);
9 | background-image: -o-linear-gradient(top, #2692de, #2d9deb);
10 | background-image: linear-gradient(to bottom, #2692de, #2d9deb);
11 | background-repeat: repeat-x;
12 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff2692', endColorstr='#ff2d9d', GradientType=0);
13 | color: #ffffff;
14 | text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
15 | border-color: #2D9DEB;
16 | }'''
17 |
18 | expected = '''.ui-dialog .ui-dialog-content .btn.confirm {
19 | border-color: #2D9DEB;
20 | background-color: #2996E3;
21 | background-image: -moz-linear-gradient(top,#2692DE,#2D9DEB);
22 | background-image: -webkit-gradient(linear,0 0,0 100%,from(#2692DE),to(#2D9DEB));
23 | background-image: -webkit-linear-gradient(top,#2692DE,#2D9DEB);
24 | background-image: -o-linear-gradient(top,#2692DE,#2D9DEB);
25 | background-image: linear-gradient(to bottom,#2692DE,#2D9DEB);
26 | background-repeat: repeat-x;
27 | color: #FFF;
28 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FF2692',endColorstr='#FF2D9D',GradientType=0);
29 | text-shadow: 0 1px 1px rgba(255,255,255,.75);
30 | }'''
31 | fixer, msg = doFix(css, '')
32 | equal(msg, expected, 'so many colors are ok');
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) individual contributors of this project.
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification,
5 | are permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice,
8 | this list of conditions and the following disclaimer.
9 |
10 | 2. Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in the
12 | documentation and/or other materials provided with the distribution.
13 |
14 | 3. Neither the name of CSSCheckStyle/ckstyle nor the names of its contributors may be used
15 | to endorse or promote products derived from this software without
16 | specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDSingleLineSpaces.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDSingleLineSpaces(RuleChecker):
7 |
8 | '''{
9 | "summary":"单行的空格检查",
10 | "desc":"单行CSS编码风格相关的空格检查,具体内容请参见CSS编码规范"
11 | }'''
12 |
13 | def __init__(self):
14 | self.id = 'single-line-space'
15 | self.errorLevel = ERROR_LEVEL.LOG
16 | self.errorMsg_noSpace = 'should have one "space" before "${name}" in "${selector}"'
17 | self.errorMsg_spaceEnd = 'should not have "space" after "${name}" in "${selector}"'
18 | self.errorMsg_noSpaceBeforeValue = 'should have one "space" before value of "${name}" in "${selector}"'
19 | self.errorMsg_extraSpaceAfterValue = 'found extra "space" after value of "${name}" in "${selector}"'
20 | self.errorMsg = ''
21 |
22 | def check(self, rule, config):
23 | singleLine = rule.getRuleSet().getSingleLineFlag()
24 | if not singleLine:
25 | return True
26 |
27 | if not rule.roughName.startswith(' '):
28 | self.errorMsg = self.errorMsg_noSpace
29 | return False
30 |
31 | if rule.roughName.endswith(' '):
32 | self.errorMsg = self.errorMsg_spaceEnd
33 | return False
34 |
35 | if not rule.roughValue.startswith(' '):
36 | self.errorMsg = self.errorMsg_noSpaceBeforeValue
37 | return False
38 |
39 | value = rule.roughValue.strip()
40 | if value.endswith(' ;') or value.endswith(' '):
41 | self.errorMsg = self.errorMsg_extraSpaceAfterValue
42 | return False
43 |
44 | return True
45 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDMultiLineSpaces.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 2: should have 4 spaces before "width" in ".no-space"
6 | 2: should have 4 spaces before "width" in ".one-space"
7 | 2: should have 4 spaces before "width" in ".two-spaces"
8 | 2: should have 4 spaces before "width" in ".three-spaces"
9 | 2: should have 4 spaces before "width" in ".five-spaces"
10 | 2: should have 4 spaces before "width" in ".five-more-spaces"
11 | 2: should have 4 spaces before "nav-down" in ".css-three-attrs-no-prefix-extra-space"
12 | 2: should have (only) one "space" before value of "width" in ".test-space-before-value"
13 |
14 | 1: ".test-space-before-value, .no-space, .one-space, .two-spaces, .three-spaces, .four-spaces, .five-spaces, .five-more-spaces" contains the same rules in "FEDMultiLineSpaces.css"
15 | 1: ".css-three-attrs-no-prefix-extra-space, .css-three-attrs-no-prefix-no-extra" contains the same rules in "FEDMultiLineSpaces.css"
16 | }
17 |
18 | .test-space-before-value {
19 | width:100px;
20 | }
21 |
22 | .css-three-attrs-no-prefix-extra-space {
23 | nav-down: 1;
24 | }
25 |
26 | .css-three-attrs-no-prefix-no-extra {
27 | nav-down: 1;
28 | }
29 |
30 | .no-space {
31 | width: 100px;
32 | }
33 |
34 | .one-space {
35 | width: 100px;
36 | }
37 |
38 | .two-spaces {
39 | width: 100px;
40 | }
41 |
42 | .three-spaces {
43 | width: 100px;
44 | }
45 |
46 | .four-spaces {
47 | width: 100px;
48 | }
49 |
50 | .five-spaces {
51 | width: 100px;
52 | }
53 |
54 | .five-more-spaces {
55 | width: 100px;
56 | }
57 |
58 | .single-line { width: 100px; height: 200px; }
59 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDSingleLineBraces.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDSingleLineBraces(RuleSetChecker):
7 |
8 | '''{
9 | "summary":"单行的括号检查",
10 | "desc":"与单行CSS编码风格相关的括号检查"
11 | }'''
12 |
13 | def __init__(self):
14 | self.id = 'single-line-brace'
15 | self.errorLevel = ERROR_LEVEL.LOG
16 | self.errorMsg_openingBrace = 'should have "only one space" before the opening brace in "${selector}"'
17 | self.errorMsg_openingBraceEnd = 'should have "only one space" after the opening brace in "${selector}"'
18 | self.errorMsg_closingBrace = 'should have "only one space" before the closing brace in "${selector}"'
19 | self.errorMsg_closingBraceEnd = 'should have "only one space" before the closing brace in "${selector}"'
20 | self.errorMsg = ''
21 |
22 | def check(self, ruleSet, config):
23 | singleLine = ruleSet.getSingleLineFlag()
24 | if not singleLine:
25 | return True
26 | selector = ruleSet.roughSelector
27 | if selector.find(',') == -1:
28 | if selector.endswith(' ') or not selector.endswith(' '):
29 | self.errorMsg = self.errorMsg_openingBrace
30 | return False
31 | else:
32 | return True
33 |
34 | value = ruleSet.roughValue
35 | if not value.startswith(' ') or value.startswith(' '):
36 | self.errorMsg = self.errorMsg_openingBraceEnd
37 | return False
38 | if not value.endswith(' ') or value.endswith(' '):
39 | self.errorMsg = self.errorMsg_closingBraceEnd
40 | return False
41 | return True
42 |
--------------------------------------------------------------------------------
/ckstyle/command/PluginManager.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | import sys
5 | from ckstyle.cmdconsole.ConsoleClass import console
6 | from .helper import fetchPlugin, fetchCmdPlugin, removePlugin, removeCmdPlugin, findCmdPlugin
7 | from .usage import newUsage
8 |
9 | def getNameAndVersion(args):
10 | argLen = len(args)
11 | if argLen < 3 or argLen > 4:
12 | console.error('wrong arg length, use "ckstyle -h" to see help.')
13 | return None, None
14 | pluginName = args[2]
15 | version = ''
16 | if argLen == 4:
17 | version = args[3]
18 |
19 | return pluginName, version
20 |
21 | def install(args):
22 | pluginName, version = getNameAndVersion(args)
23 | if pluginName is None:
24 | return
25 | fetchPlugin(pluginName, version)
26 |
27 | def uninstall(args):
28 | pluginName, version = getNameAndVersion(args)
29 | if pluginName is None:
30 | return
31 | removePlugin(pluginName, version)
32 |
33 | def installcmd(args):
34 | pluginName, version = getNameAndVersion(args)
35 | if pluginName is None:
36 | return
37 | fetchCmdPlugin(pluginName, version)
38 |
39 | def uninstallcmd(args):
40 | pluginName, version = getNameAndVersion(args)
41 | if pluginName is None:
42 | return
43 | removeCmdPlugin(pluginName, version)
44 |
45 | def handleExtraCommand(command, args):
46 | if command.startswith('-') or command.startswith('.'):
47 | newUsage()
48 | return
49 | if not findCmdPlugin(command):
50 | console.show('[CKstyle ERROR] CKstyle can not find the subcommand: "%s".' % command)
51 | console.show('[CKstyle ERROR] Maybe you can type "ckstyle installcmd %s" to install this command from ckstyle-pm.' % command)
52 | return
53 | cmd = fetchCmdPlugin(command)
54 | if cmd is None:
55 | return
56 | cmd()
57 |
--------------------------------------------------------------------------------
/tests/unit/order/InAMessOrder.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 |
4 | def doTest():
5 | fixer, msg = doFix("a.feed-back-v6 { display: block; position: fixed; _position: absolute; top: 155px; _top: expression(documentElement.scrollTop + 'px'); _margin-top: 155px; right: 0; padding: 10px; font-size: 14px; font-weight: bold; width: 1em; background: #F7F7FF; z-index: 1999; }", '')
6 |
7 | equal(fixer.doCompress(), "a.feed-back-v6{display:block;position:fixed;_position:absolute;top:155px;_top:expression(documentElement.scrollTop + 'px');right:0;width:1em;_margin-top:155px;padding:10px;background:#F7F7FF;font-size:14px;font-weight:bold;z-index:1999}", 'compress feed-back-v6 is ok')
8 |
9 | styleSheet = fixer.getStyleSheet()
10 | equal(len(styleSheet.getRuleSets()), 1, 'one ruleset')
11 | ruleSet = styleSheet.getRuleSets()[0]
12 | equal(ruleSet.selector, 'a.feed-back-v6', 'yes it is a.feed-back-v6')
13 |
14 | rules = ruleSet.getRules()
15 | equal(rules[0].name, 'display', 'element name is ok')
16 | equal(rules[1].name, 'position', 'element name is ok')
17 | equal(rules[2].name, 'position', 'element name is ok')
18 | equal(rules[3].name, 'top', 'element name is ok')
19 | equal(rules[4].name, 'top', 'element name is ok')
20 | equal(rules[5].name, 'right', 'element name is ok')
21 | equal(rules[6].name, 'width', 'element name is ok')
22 | equal(rules[7].name, 'margin-top', 'element name is ok')
23 | equal(rules[8].name, 'padding', 'element name is ok')
24 | equal(rules[9].name, 'background', 'element name is ok')
25 | equal(rules[10].name, 'font-size', 'element name is ok')
26 | equal(rules[11].name, 'font-weight', 'element name is ok')
27 | equal(rules[12].name, 'z-index', 'element name is ok')
28 |
--------------------------------------------------------------------------------
/tests/unit/compress/CompressFile.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _basic()
5 | _one_line_file()
6 | _with_extra()
7 | _compress_with_hack_chars()
8 | _extra_statement()
9 | _expression()
10 |
11 | def _basic():
12 | msg = doCssFileCompress('_file.css')
13 | equal(msg, ".test{width:100px;height:200px;_z-index:111}@keyframes 'name'{10%{width:100px}}.another{*width:100px;background-color:#ABC;color:#DDD}", 'file compressed')
14 |
15 | def _one_line_file():
16 | msg = doCssFileCompress('_one_line_file.css')
17 | equal(msg, ".test{width:100px;height:200px;_z-index:111}@keyframes 'name'{10%{width:100px}}.another{*width:100px;background-color:#ABC;color:#DDD}", 'file compressed')
18 |
19 | def _with_extra():
20 | msg = doCssFileCompress('_with_extra.css')
21 | equal(msg, "@charset utf-8;@import url('xxxxx');@namespace lalala;.test{width:100px;height:200px;_z-index:111}@import url('xxx2');@import url('xxx3');", 'file compressed')
22 |
23 | def _compress_with_hack_chars():
24 | msg = doCssFileCompress('_compress_special_hack_chars.css')
25 | equal(msg, "li:nth-child(even){background:gray}* html li.even{background:gray}.test[^=aaa]{background:gray}.test1,.test2{width:100px}", 'file compressed')
26 |
27 | def _extra_statement():
28 | msg = doCssFileCompress('_extra_statement.css')
29 | # do not show @-css-compile content after compress
30 | equal(msg, "@charset utf-8;@import url(fdafdas/fdafdas.css);", 'extra statement compressed')
31 |
32 | def _expression():
33 | msg = doCssFileCompress('_expression.css')
34 | equal(msg, "*html .feed-comment textarea{behavior:expression(function(ele){ele.runtimeStyle.behavior='none';Expressions.pseudo.hover(ele,'textarea_hover')}(this))}", 'expression compressed')
35 |
--------------------------------------------------------------------------------
/tests/unit/hacks/HackRules.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _ie()
5 | _other()
6 | #_ms()
7 | #_moz()
8 | #_o()
9 |
10 | def _ie():
11 | equal(doRuleDetect('_width', 'fd'), IE6, '_width')
12 | equal(doRuleDetect('*width', 'fd'), IE6 | IE7, '*width')
13 | equal(doRuleDetect('+width', 'fd'), IE6 | IE7, '+width')
14 | equal(doRuleDetect('width', r'fd\9'), ALLIE, 'width\\9')
15 | equal(doRuleDetect('width', r'fd\0/'), IE8, 'width\\0/')
16 | equal(doRuleDetect('width', r'fd\0'), IE8 | IE9PLUS, 'width\\0')
17 | equal(doRuleDetect('zoom', '1'), ALLIE, 'zoom')
18 | equal(doRuleDetect('behavior', '1'), ALLIE, 'behavior')
19 | equal(doRuleDetect('filter', '1'), ALLIE, 'filter')
20 | equal(doRuleDetect('width', 'xxxx.Microsoft.AlphaImageLoader'), ALLIE, 'Microsoft')
21 | equal(doRuleDetect('width', 'xxxx.microsoft.AlphaImageLoader'), ALLIE, 'microsoft')
22 | equal(doRuleDetect('width', 'expression()'), ALLIE, 'expression')
23 |
24 | def _other():
25 | equal(doRuleDetect('-webkit-transition', 'fd'), WEBKIT, '-webkit-transition')
26 | equal(doRuleDetect('-moz-transition', 'fd'), FIREFOX, '-moz-transition')
27 | equal(doRuleDetect('-ms-transition', 'fd'), IE9PLUS, '-ms-transition')
28 | equal(doRuleDetect('-khtml-transition', 'fd'), ALLIE, '-khtml-transition')
29 | equal(doRuleDetect('-o-transition', 'fd'), OPERA, '-o-transition')
30 |
31 | equal(doRuleDetect('background', '-webkit-linear-gradient()'), WEBKIT, '-webkit-gradient')
32 | equal(doRuleDetect('background', '-moz-linear-gradient()'), FIREFOX, '-moz-gradient')
33 | equal(doRuleDetect('background', '-ms-linear-gradient()'), IE9PLUS, '-ms-gradient')
34 | equal(doRuleDetect('background', '-khtml-linear-gradient()'), ALLIE, '-khtml-gradient')
35 | equal(doRuleDetect('background', '-o-linear-gradient()'), OPERA, '-o-gradient')
--------------------------------------------------------------------------------
/tests/unit/check/FEDMultiLineSelectors.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 2: should enter in multi-selector, in ".multia, .multiaa"
6 | 2: too many "enter"s in ".multiaaa, .multiaaaa"
7 | 2: extra "space" before comma in ".multib , .multic"
8 | 2: extra "space" after comma in ".multid, .multie"
9 | 2: selector should not start with "space" in ".multif, .multig"
10 | 2: selector should not start with "space" in ".multip, .multiq"
11 | 2: comma should at the end of selector in ".multil , .multim"
12 | 2: selector should end with only one space ".multih, .multii"
13 | 2: selector should end with only one space ".multin, .multio"
14 | 2: selector should end with only one space ".multij, .multik"
15 |
16 | 1: ".multia, .multiaa, .multiaaa, .multiaaaa, .multi-selector, .multi-selectorb, .multib , .multic, .multid, .multie, .multif, .multig, .multih, .multii, .multij, .multik, .multil , .multim, .multip, .multiq" contains the same rules in "FEDMultiLineSelectors.css"
17 | }
18 |
19 | .multia, .multiaa { width: 100px; height: 100px; }
20 |
21 | .multiaaa,
22 |
23 | .multiaaaa { width: 100px; height: 100px; }
24 |
25 | .multi-selector,
26 | .multi-selectorb { width: 100px; height: 100px; }
27 |
28 | .multib ,
29 | .multic { width: 100px; height: 100px; }
30 |
31 | .multid,
32 | .multie { width: 100px; height: 100px; }
33 |
34 | .multif,
35 | .multig { width: 100px; height: 100px; }
36 |
37 | .multih,
38 | .multii{ width: 100px; height: 100px; }
39 |
40 | .multij,
41 | .multik { width: 100px; height: 100px; }
42 |
43 | .multil
44 | , .multim { width: 100px; height: 100px; }
45 |
46 | .multin,
47 | .multio
48 | {
49 | width: 100px;
50 | }
51 |
52 | .multip,
53 | .multiq { width: 100px; height: 100px; }
54 |
55 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDMultiLineBraces.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDMultiLineBraces(RuleSetChecker):
7 |
8 | '''{
9 | "summary":"多行CSS风格的括号检查",
10 | "desc":"用于检查多行风格下的 { 和 } 的编写风格,前后空格符和回车符的情况等。"
11 | }'''
12 |
13 | def __init__(self):
14 | self.id = 'multi-line-brace'
15 | self.errorLevel = ERROR_LEVEL.LOG
16 | self.errorMsg_shouldEnterAfterOpenningBrace = 'should "enter" after the opening brace in "${selector}"'
17 | self.errorMsg_shouldEnterBeforeClosingBrace = 'should "enter" before the closing brace in "${selector}"'
18 | self.errorMsg_extraSpaceAfterOpeningBrace = 'extra "space" after the opening brace in "${selector}"'
19 | self.errorMsg_everyAttrShouldInSingleLine = 'every name/value should in single line in "${selector}"'
20 | self.errorMsg = ''
21 |
22 | def check(self, ruleSet, config):
23 | singleLine = ruleSet.getSingleLineFlag()
24 | if singleLine:
25 | return True
26 |
27 | value = ruleSet.roughValue
28 | splited = value.split('\n')
29 | if splited[0].strip() != '':
30 | self.errorMsg = self.errorMsg_shouldEnterAfterOpenningBrace
31 | return False
32 |
33 | if splited[0].strip() == '' and splited[0].startswith(' '):
34 | self.errorMsg = self.errorMsg_extraSpaceAfterOpeningBrace
35 | return False
36 |
37 | ruleLength = len(ruleSet.getRules())
38 | if ruleLength != 0 and len(value.strip().split('\n')) != ruleLength:
39 | self.errorMsg = self.errorMsg_everyAttrShouldInSingleLine
40 | return False
41 |
42 | if not value.replace(' ', '').endswith('\n'):
43 | self.errorMsg = self.errorMsg_shouldEnterBeforeClosingBrace
44 | return False
45 |
46 | return True
47 |
--------------------------------------------------------------------------------
/tests/unit/fix/FEDCombineSameRuleSets.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _combine_should_not_make_mistake()
5 | _totally_same_ruleset()
6 | do_not_touch_background_position()
7 | _do_not_change_comment()
8 |
9 | def _combine_should_not_make_mistake():
10 | css = '''.a {width:0px}
11 | .a, .b{width:1px}
12 | .b{width:0px}'''
13 |
14 | expected = '''.a {
15 | width: 0;
16 | }
17 |
18 | .a,
19 | .b {
20 | width: 1px;
21 | }
22 |
23 | .b {
24 | width: 0;
25 | }'''
26 | fixer, msg = doFix(css, '')
27 | equal(msg, expected, 'do not make mistake when combine rulesets');
28 |
29 | def do_not_touch_background_position():
30 | css = '''.a {background-position: 0 0}
31 | .test {width:1}
32 | .b {background-position: 0 0} '''
33 |
34 | expected = '''.a {
35 | background-position: 0 0;
36 | }
37 |
38 | .test {
39 | width: 1;
40 | }
41 |
42 | .b {
43 | background-position: 0 0;
44 | }'''
45 | fixer, msg = doFix(css, '')
46 | equal(msg, expected, 'do not combine background-position');
47 |
48 | def _totally_same_ruleset():
49 | css = '''/*fdafda*/
50 | .page-title {
51 | width: 100px;
52 | padding: 0px 1px;
53 | }
54 |
55 | .page-title {
56 | width: 100px;
57 | padding: 0px 1px;
58 | }'''
59 |
60 | expected = '''/* fdafda */
61 | .page-title {
62 | width: 100px;
63 | padding: 0 1px;
64 | }'''
65 | fixer, msg = doFix(css, '')
66 | equal(msg, expected, 'it is the same ruleset');
67 |
68 | def _do_not_change_comment():
69 | css = '''/*fdafda, fda,fda,fdas
70 | */
71 | .page-title {
72 | width: 100px;
73 | padding: 0px 1px;
74 | }
75 |
76 | .page-title {
77 | width: 100px;
78 | padding: 0px 1px;
79 | }'''
80 |
81 | expected = '''/*fdafda, fda,fda,fdas
82 | */
83 | .page-title {
84 | width: 100px;
85 | padding: 0 1px;
86 | }'''
87 | fixer, msg = doFix(css, '')
88 | equal(msg, expected, 'do not change comment is ok');
--------------------------------------------------------------------------------
/ckstyle/entity/Rule.py:
--------------------------------------------------------------------------------
1 | from .EntityUtil import Cleaner, ALL
2 |
3 | class Rule():
4 | def __init__(self, selector, name, value, ruleSet):
5 | self.roughName = name
6 | self.roughValue = value
7 | self.roughSelector = selector
8 |
9 | self.name = Cleaner.clearName(name)
10 | self.value = Cleaner.clearValue(value)
11 | self.selector = Cleaner.clearSelector(selector)
12 |
13 | self.strippedName = name.strip()
14 | self.strippedValue = value.strip()
15 | self.strippedSelector = selector.strip()
16 |
17 | self.fixedName = ''
18 | self.fixedValue = ''
19 |
20 | self.ruleSet = ruleSet
21 |
22 | self.browser = ALL
23 | self.toBeUsed = {}
24 |
25 | def rebase(self):
26 | self.fixedName = ''
27 | self.fixedValue = ''
28 |
29 | def reset(self, name, value):
30 | self.roughName = self.name = self.strippedName = self.fixedName = name
31 | self.roughValue = self.value = self.strippedValue = self.fixedValue = value
32 |
33 | def compress(self, browser = ALL):
34 | #print self.strippedName, self.value, bin(self.browser), bin(browser), self.browser & browser
35 | if not self.browser & browser:
36 | return ''
37 | name = self.name if self.fixedName == '' else self.fixedName.strip()
38 | value = self.value if self.fixedValue == '' else self.fixedValue.strip()
39 | return name + ':' + Cleaner.clean(value) + ';'
40 |
41 | def fixed(self):
42 | name = self.strippedName if self.fixedName == '' else self.fixedName
43 | value = self.strippedValue if self.fixedValue == '' else self.fixedValue
44 | return name + ': ' + Cleaner.clean(value) + ';'
45 |
46 | def getRuleSet(self):
47 | return self.ruleSet
48 |
49 | def __str__(self):
50 | return ' roughName: %s\n name: %s\n roughValue: %s\n value: %s\n' % (self.roughName, self.name, self.roughValue, self.value)
51 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDUseLowerCaseProp.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDUseLowerCaseProp(RuleChecker):
7 |
8 | '''{
9 | "summary":"属性名称应该用小写",
10 | "desc":"所有的CSS属性名称一律小写,例如 width ,大写的方式是不正确的,
11 | 例如: WIDTH:100px;"
12 | }'''
13 |
14 | def __init__(self):
15 | self.id = 'lowercase-prop'
16 | self.errorLevel = ERROR_LEVEL.WARNING
17 | self.errorMsg_name = '"${name}" should use lower case, in "${selector}"'
18 | self.errorMsg_value = 'value of "${name}" should use lower case, in "${selector}"'
19 | self.errorMsg = ''
20 |
21 | def check(self, rule, config):
22 | value = rule.value
23 | name = rule.strippedName
24 |
25 | # give it to FED16ColorShouldUpper.py
26 | if name == 'color':
27 | return True
28 |
29 | if value.find('expression') != -1:
30 | return True
31 |
32 | if name.lower() != name:
33 | self.errorMsg = self.errorMsg_name
34 | return False
35 |
36 | if value.find('#') != -1:
37 | return True
38 |
39 | if name != 'font' and name != 'font-family' and value != value.lower() and value.find('#') == -1:
40 | self.errorMsg = self.errorMsg_value
41 | return False
42 |
43 | if name == 'font-family':
44 | return True
45 |
46 | if name == 'font':
47 | if value.find(',') != -1:
48 | # font: italic bold 12px/30px 'Courier New', Georgia, serif;
49 | other = ' '.join(value.split(',')[0].split("'")[0].split(' ')[0:-1])
50 | if other != other.lower():
51 | self.errorMsg = self.errorMsg_value
52 | return False
53 | return True
54 |
55 | if value.lower() != value:
56 | self.errorMsg = self.errorMsg_value
57 | return False
58 |
59 | return True
60 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDStyleShouldInOrder.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 |
5 | /**
6 | correct order:
7 | 0 : ['display', 'list-style', 'position', 'float', 'clear'],
8 | 2 : ['width', 'height', 'margin', 'padding', 'border'],
9 | 4 : ['background'],
10 | 6 : ['line-height'],
11 | 8 : ['color', 'font', 'text-decoration', 'text-align', 'text-indent', 'vertical-align', 'white-space', 'content'],
12 | 10: ['cursor', 'z-index', 'zoom'],
13 | 12: ['transform', 'transition', 'animation', 'box-shadow', 'border-radius']
14 | # 14 : ['other']
15 | */
16 |
17 | @unit-test-expecteds {
18 | 1: "width" should after "display" in ".test-width-display" (order: display/box/text/other/css3)
19 | 1: "background-color" should after "list-style" in ".test-list-style-background" (order: display/box/text/other/css3)
20 | 1: "z-index" should after "height" in ".test-other" (order: display/box/text/other/css3)
21 | 0: unknown attribute name "nav" found in ".test-unknown"
22 | 1: "nav" should after "z-index" in ".test-unknown" (order: display/box/text/other/css3)
23 | 1: "width" should after "_display" in ".test-with-hack" (order: display/box/text/other/css3)
24 | }
25 |
26 | .test-width-display {
27 | width: 100px;
28 | display: none;
29 | }
30 |
31 | .test-list-style-background {
32 | background-color: red;
33 | list-style: none;
34 | }
35 |
36 | .test-same-order-box {
37 | width: 100px;
38 | height: 100px;
39 | margin: 0;
40 | padding: 0;
41 | border: none;
42 | }
43 |
44 | .test-same-order-display {
45 | display: none;
46 | position: relative;
47 | float: left;
48 | list-style: none;
49 | clear: both;
50 | }
51 |
52 | .test-other {
53 | width: 100px;
54 | z-index: 100;
55 | height: 100px;
56 | }
57 |
58 | .test-unknown {
59 | nav: false;
60 | z-index: 100;
61 | }
62 |
63 | .test-with-hack {
64 | width: 100px;
65 | _display: none;
66 | *display: inline-block;
67 | *zoom: 1;
68 | }
69 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDRemoveDuplicatedAttr.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 |
6 | class FEDRemoveDuplicatedAttr(RuleSetChecker):
7 |
8 | '''{
9 | "summary":"删除重复的属性设置",
10 | "desc":"如果在一个规则集中,对相同的两个属性进行了赋值,而且取值相同,则可以删除前面的赋值,例如:
11 |
12 | .test {
13 | width: 100px;
14 | width: 100px;
15 | }
16 | ==>
17 | .test {
18 | width: 100px;
19 | }"
20 | }'''
21 |
22 | def __init__(self):
23 | self.id = 'remove-duplicated-attr'
24 | self.errorLevel = ERROR_LEVEL.ERROR
25 | self.errorMsg = 'has more than 1 ${name} in "${selector}"'
26 |
27 | def check(self, ruleSet, config):
28 | rules = ruleSet.getRules()
29 | collector = []
30 | for rule in rules:
31 | info = self.ruleInfo(rule)
32 | if info in collector:
33 | return False
34 | collector.append(info)
35 | return True
36 |
37 | def fix(self, ruleSet, config):
38 | # make sure we use the last statement, so reverse and filter and reverse again
39 | # [a1, a2, b, c] ==> [c, b, a2, a1] ==> [c, b, a2] ==> [a2, b, c]
40 | rules = ruleSet.getRules()
41 | rules.reverse()
42 | newRules = []
43 | collector = []
44 | for rule in rules:
45 | info = self.ruleInfo(rule)
46 | if not info in collector:
47 | collector.append(info)
48 | newRules.append(rule)
49 | newRules.reverse()
50 | ruleSet.setRules(newRules)
51 |
52 | def ruleInfo(self, rule):
53 | if rule.fixedName != '':
54 | return rule.fixedName + ':' + rule.fixedValue
55 | return rule.strippedName + ':' + rule.strippedValue
56 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDStyleShouldInOrder.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .helper import getAttrOrder
5 | from .Base import *
6 |
7 | class FEDStyleShouldInOrder(RuleSetChecker):
8 |
9 | '''{
10 | "summary":"属性应该按照推荐的顺序编写",
11 | "desc":"相同的CSS属性,如果按照推荐的顺序来编写,浏览器的处理性能会更高,推荐的顺序一般为:
12 | 显示属性 => 盒模型属性 => 背景/行高 => 文本属性 => 其他"
13 | }'''
14 |
15 | def __init__(self):
16 | self.id = 'keep-in-order'
17 | self.errorLevel = ERROR_LEVEL.WARNING
18 | self.errorMsg_rough = '"%s" should after "%s" in "${selector}" (order: display/box/text/other/css3)'
19 | self.errorMsg = ''
20 |
21 | def check(self, ruleSet, config):
22 | rules = ruleSet.getRules()
23 | if len(rules) < 2:
24 | return True
25 |
26 | order = self._generateNameOrderMapping(rules)
27 | length = len(order)
28 | for i in range(length):
29 | if i == length - 1:
30 | break;
31 | current = order[i]
32 | nextAttr = order[i + 1]
33 |
34 | if current[0] > nextAttr[0]:
35 | self.errorMsg = self.errorMsg_rough % (current[1], nextAttr[1])
36 | return False
37 |
38 | return True
39 |
40 | def fix(self, ruleSet, config):
41 | rules = ruleSet.getRules()
42 | if len(rules) < 2:
43 | return True
44 |
45 | def comp(a, b):
46 | return a[0] - b[0]
47 |
48 | mapping = self._generateNameRuleMapping(rules)
49 | mapping.sort(comp)
50 | sortedRules = []
51 | for x in range(len(mapping)):
52 | sortedRules.append(mapping[x][1])
53 | ruleSet.setRules(sortedRules)
54 |
55 | def _generateNameOrderMapping(self, rules):
56 | return [(getAttrOrder(rule.name, rule.strippedName), rule.strippedName) for rule in rules]
57 |
58 | def _generateNameRuleMapping(self, rules):
59 | return [(getAttrOrder(rule.name, rule.strippedName), rule) for rule in rules]
60 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDSingleLineBraces.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 2: should have "only one space" before the opening brace in ".no-space-after-selector"
6 | 2: should have "only one space" before the opening brace in ".too-many-space-after-selector"
7 | 2: should have "only one space" after the opening brace in ".no-space-after-first-brace"
8 | 2: should have "only one space" after the opening brace in ".too-many-space-after-first-brace"
9 |
10 | 2: should have "only one space" before the closing brace in ".no-space-before-closing"
11 | 2: should have "only one space" before the closing brace in ".too-space-before-closing"
12 |
13 | 2: should start with "space" in ".test-space-before-selector"
14 |
15 | 2: should have one "space" before "width" in ".no-space-after-first-brace"
16 | 2: selector should end with only one space ".no-space-after-selector"
17 | 2: selector should end with only one space ".too-many-space-after-selector"
18 |
19 | 1: ".correct-single-line-styles, .multi-selector, .multi-selectorb, .no-space-before-closing, .too-space-before-closing, .multia, .multib" contains the same rules in "FEDSingleLineBraces.css"
20 | 1: ".no-space-after-selector, .too-many-space-after-selector, .test-space-before-selector, .no-space-after-first-brace, .too-many-space-after-first-brace" contains the same rules in "FEDSingleLineBraces.css"
21 | }
22 |
23 | .correct-single-line-styles { width: 100px; height: 100px; }
24 |
25 | .multi-selector,
26 | .multi-selectorb { width: 100px; height: 100px; }
27 |
28 | .no-space-after-selector{ width: 100px; }
29 | .too-many-space-after-selector { width: 100px; }
30 | .test-space-before-selector { width: 100px; }
31 |
32 | .no-space-after-first-brace {width: 100px; }
33 | .too-many-space-after-first-brace { width: 100px; }
34 |
35 | .no-space-before-closing { width: 100px; height: 100px;}
36 | .too-space-before-closing { width: 100px; height: 100px; }
37 |
38 | .multia,
39 | .multib { width: 100px; height: 100px; }
40 |
--------------------------------------------------------------------------------
/tests/unit/entity/RuleSet.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _ruleSet()
5 |
6 | def _ruleSet():
7 | ruleSet = RuleSet(' .test ', ' width:100px;height:100px; ', '/* aa */ ', None)
8 | ok(not ruleSet.extra, 'ruleset is not extra')
9 | equal(ruleSet.selector, '.test', 'selector is ok')
10 | equal(ruleSet.roughSelector, ' .test ', 'roughSelector is ok')
11 | equal(ruleSet.roughValue, ' width:100px;height:100px; ', 'roughValue is ok')
12 | equal(ruleSet.roughComment, '/* aa */ ', 'rough comment is ok')
13 | equal(ruleSet.values, 'width:100px;height:100px;', 'values is ok')
14 | ok(ruleSet.singleLineFlag, 'it is single line')
15 | ok(ruleSet.getSingleLineFlag(), 'it is single line')
16 | equal(ruleSet.getStyleSheet(), None, 'no stylesheet')
17 |
18 | equal(len(ruleSet.getRules()), 0, 'no rules')
19 | equal(ruleSet.indexOf('width'), -1, 'no _width')
20 | equal(ruleSet.existNames('width'), False, 'no width again')
21 | equal(ruleSet.existNames(' _width '), False, 'no rough _width')
22 | equal(ruleSet.getRuleByName('width'), None, 'can not find width')
23 | equal(ruleSet.getRuleByRoughName(' _width '), None, 'can not find _width')
24 |
25 | ruleSet.addRuleByStr(' .aaa', ' _width ', ' 100px; ')
26 |
27 | equal(len(ruleSet.getRules()), 1, 'one rule')
28 | equal(ruleSet.indexOf('_width'), 0, 'found width')
29 | equal(ruleSet.existNames('width'), True, 'found width again')
30 | equal(ruleSet.existRoughNames(' _width '), True, 'found rough width')
31 | equal(ruleSet.getRuleByName('width').value, '100px', 'find width')
32 | equal(ruleSet.getRuleByRoughName(' _width ').value, '100px', 'find width by rough name')
33 | equal(ruleSet.getRuleByStrippedName('_width').value, '100px', 'find width by stripped name')
34 |
35 | ruleSet.addRuleByStr(' .aaa', 'height', '100px; ')
36 | equal(len(ruleSet.getRules()), 2, 'two rules')
37 | equal(ruleSet.getRules()[0].name, 'width', 'width is first')
38 | equal(ruleSet.getRules()[1].name, 'height', 'height is second')
39 |
--------------------------------------------------------------------------------
/tests/unit/fix/FixComplicatedStatement.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _handle_complicated_statement()
5 |
6 | def _handle_complicated_statement():
7 | css = '''.ui-bar-a {
8 | border: 1px solid #333 /*{a-bar-border}*/;
9 | background: #111 /*{a-bar-background-color}*/;
10 | color: #fff /*{a-bar-color}*/;
11 | font-weight: bold;
12 | text-shadow: 0 /*{a-bar-shadow-x}*/ -1px /*{a-bar-shadow-y}*/ 1px /*{a-bar-shadow-radius}*/ #000 /*{a-bar-shadow-color}*/;
13 | background-image: -webkit-gradient(linear, left top, left bottom, from( #3c3c3c /*{a-bar-background-start}*/), to( #111 /*{a-bar-background-end}*/)); /* Saf4+, Chrome */
14 | background-image: -webkit-linear-gradient( #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); /* Chrome 10+, Saf5.1+ */
15 | background-image: -moz-linear-gradient( #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); /* FF3.6 */
16 | background-image: -ms-linear-gradient( #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); /* IE10 */
17 | background-image: -o-linear-gradient( #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); /* Opera 11.10+ */
18 | background-image: linear-gradient( #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/);
19 | }
20 |
21 | .ui-bar-a .ui-link-inherit {
22 | color: #fff /*{a-bar-color}*/;
23 | }'''
24 |
25 | expectedFixed = '''.ui-bar-a {
26 | border: 1px solid #333;
27 | background: #111;
28 | background-image: -webkit-gradient(linear,left top,left bottom,from(#3C3C3C),to(#111));
29 | background-image: -webkit-linear-gradient(#3C3C3C,#111);
30 | background-image: -moz-linear-gradient(#3C3C3C,#111);
31 | background-image: -ms-linear-gradient(#3C3C3C,#111);
32 | background-image: -o-linear-gradient(#3C3C3C,#111);
33 | background-image: linear-gradient(#3C3C3C,#111);
34 | color: #FFF;
35 | font-weight: bold;
36 | text-shadow: 0 -1px 1px #000;
37 | }
38 |
39 | .ui-bar-a .ui-link-inherit {
40 | color: #FFF;
41 | }'''
42 |
43 | fixer, msg = doFix(css, '')
44 | equal(msg.strip(), expectedFixed.strip(), 'complicated statement is ok')
--------------------------------------------------------------------------------
/editor-plugins/fixstyle.vim:
--------------------------------------------------------------------------------
1 | "=============================================================================
2 | " File: fixstyle.vim
3 | " Author: zhifu.wang(wangjeaf@gmail.com)
4 | " WebPage: https://github.com/wangjeaf/CSSCheckStyle
5 | " License: MIT
6 | if &cp || exists("loaded_fixcssstyle")
7 | finish
8 | endif
9 | let loaded_fixcssstyle = 1
10 |
11 | function! s:is_css()
12 | return expand("%:e") == "css"
13 | endfunction
14 |
15 | function! g:Fixstyle(a)
16 | if !s:is_css()
17 | echo "Not a CSS file."
18 | return
19 | endif
20 |
21 | let ret = system("ckstyle fix -p ".expand('%:t'))
22 |
23 | :g/.*/d
24 | let @0 = ret
25 | :put!0
26 | endfunction
27 |
28 | function! g:FixstyleSingleLine(a)
29 | if !s:is_css()
30 | echo "Not a CSS file."
31 | return
32 | endif
33 |
34 | let ret = system("ckstyle fix -p --singleLine ".expand('%:t'))
35 |
36 | :g/.*/d
37 | let @0 = ret
38 | :put!0
39 | endfunction
40 |
41 | function! g:FixstyleSafe(a)
42 | if !s:is_css()
43 | echo "Not a CSS file."
44 | return
45 | endif
46 |
47 | let ret = system("ckstyle fix -p --safeMode ".expand('%:t'))
48 |
49 | :g/.*/d
50 | let @0 = ret
51 | :put!0
52 | endfunction
53 |
54 | function! g:Ckstyle(a)
55 | if !s:is_css()
56 | echo "Not a CSS file."
57 | return
58 | endif
59 |
60 | let ret = system("ckstyle check ".expand('%:t'))
61 | if filereadable("".expand('%:t').".ckstyle.txt")
62 | echo "[ckstyle] has error, @see ".expand('%:t').".ckstyle.txt"
63 | else
64 | echo "[ckstyle] No mistake found in this CSS, NB!"
65 | endif
66 |
67 | endfunction
68 |
69 | function! g:CssCompress(a)
70 | if !s:is_css()
71 | echo "Not a CSS file."
72 | return
73 | endif
74 |
75 | let ret = system("ckstyle compress -p ".expand('%:t'))
76 |
77 | :g/.*/d
78 | let @0 = ret
79 | :put!0
80 | endfunction
81 |
82 | command! -nargs=? -range=% Fixstyle :call g:Fixstyle(, )
83 | command! -nargs=? -range=% FixstyleSafe :call g:FixstyleSafe(, )
84 | command! -nargs=? -range=% FixstyleSingleLine :call g:FixstyleSingleLine(, )
85 | command! -nargs=? -range=% CssCompress :call g:CssCompress(, )
86 | command! -nargs=? -range=% Ckstyle :call g:Ckstyle(, )
87 |
88 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDUnknownHTMLTagName.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | from .helper import isHTMLTag
6 |
7 | class FEDUnknownHTMLTagName(RuleSetChecker):
8 |
9 | '''{
10 | "summary":"错误的HTML Tag",
11 | "desc":"如果您输入了错误的HTML Tag,本工具也会给出响应的提示"
12 | }'''
13 |
14 | def __init__(self):
15 | self.id = 'unknown-html-tag'
16 | self.errorLevel = ERROR_LEVEL.ERROR
17 | self.errorMsg_rough = 'unknown html tag "%s" found in "${selector}"'
18 | self.errorMsg = ''
19 |
20 | def check(self, ruleSet, config):
21 | selector = ruleSet.selector.lower()
22 | if selector.find('@media') != -1:
23 | return True
24 | if selector.find('@-moz-document') != -1:
25 | return True
26 | selectors = selector.split(',')
27 | for s in selectors:
28 | for r in s.split(' '):
29 | r = r.strip()
30 | if r != '':
31 | if r.find('::') != -1:
32 | # p::selection
33 | tag = r.split('::')[0].split('.')[0].split('#')[0].strip()
34 | else:
35 | # abcd:hover
36 | # abcd.class-name:hover
37 | # abcd#class-name:hover
38 | tag = r.split(':')[0].split('.')[0].split('#')[0].strip()
39 |
40 | # .test > .inner
41 | if tag == '' or tag == '>' or tag == '*' or tag == '+':
42 | continue
43 |
44 | # #id
45 | if tag.find('#') != -1:
46 | continue
47 |
48 | # input[type=button]
49 | if tag.find('[') != -1:
50 | tag = tag.split('[')[0].strip()
51 |
52 | # *+html
53 | if tag.startswith('*+'):
54 | tag = tag[2:]
55 |
56 | # *html
57 | elif tag.startswith('*'):
58 | tag = tag[1:]
59 |
60 | if not isHTMLTag(tag):
61 | self.errorMsg = self.errorMsg_rough % tag
62 | return False
63 | return True
64 |
--------------------------------------------------------------------------------
/ckstyle/entity/EntityUtil.py:
--------------------------------------------------------------------------------
1 | from ckstyle.browsers.BinaryRule import ALL
2 |
3 | import re
4 | replacer1 = re.compile('\s*{\s*')
5 | replacer2 = re.compile('\s*:\s*')
6 | replacer3 = re.compile('\s*;\s*}\s*')
7 | replacer4 = re.compile('\s*;\s*')
8 | replacer5 = re.compile('\s\s+')
9 | replacer6 = re.compile('\(\s+')
10 | replacer7 = re.compile('\s+\)')
11 | replacer8 = re.compile('\s+,')
12 | replacer9 = re.compile(',\s+')
13 |
14 | class Cleaner():
15 | @staticmethod
16 | def clean(msg):
17 | msg = msg.strip().replace('\r', '').replace('\n', '').replace(' ' * 4, ' ')
18 | msg = replacer1.sub('{', msg)
19 | msg = replacer2.sub(':', msg)
20 | msg = replacer3.sub('}', msg)
21 | msg = replacer4.sub(';', msg)
22 | msg = replacer5.sub(' ', msg)
23 | msg = replacer6.sub('(', msg)
24 | msg = replacer7.sub(')', msg)
25 | msg = replacer8.sub(',', msg)
26 | msg = replacer9.sub(',', msg)
27 | msg = msg.strip()
28 | return msg
29 |
30 | @staticmethod
31 | def clearName(name):
32 | name = name.strip()
33 | # #padding: 10px???
34 | if name.startswith('_') or name.startswith('*') or name.startswith('+') or name.startswith('#'):
35 | name = name[1:]
36 | if name.startswith('-'):
37 | if name.startswith('-moz-') or name.startswith('-webkit-') or name.startswith('-ms-') or name.startswith('-o-') or name.startswith('-khtml-'):
38 | name = '-'.join(name.split('-')[2:])
39 | return name.lower()
40 |
41 | @staticmethod
42 | def clearValue(value):
43 | value = value.strip()
44 | if value.endswith(';'):
45 | value = value[0: - 1]
46 | return value
47 |
48 | @staticmethod
49 | def clearValues(values):
50 | values = values.strip()
51 | return values
52 |
53 | @staticmethod
54 | def clearSelector(selector):
55 | return ' '.join(selector.split('\n')).strip()
56 |
57 | @staticmethod
58 | def clearComment(comment):
59 | comment = comment.strip()
60 | if len(comment) != 0 and comment.find('\n') == -1:
61 | comment = comment.replace('/*', '').replace('*/', '').strip()
62 | comment = '/* ' + comment + ' */'
63 | return comment
64 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDNoUnitAfterZero.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | import re
6 |
7 | pattern_unit = re.compile(r'(0\s*[\w]+)')
8 | replacer_unit = re.compile(',\s+')
9 |
10 | class FEDNoUnitAfterZero(RuleChecker):
11 |
12 | '''{
13 | "summary":"删除0后面的单位",
14 | "desc":"0后面的单位可以删除,以实现更好的压缩。比如 0px ==> 0 ,0em ==> 0 等,
15 | 但是transition: 0s的s不能省略"
16 | }'''
17 |
18 | def __init__(self):
19 | self.id = 'del-unit-after-zero'
20 | self.errorLevel = ERROR_LEVEL.WARNING
21 | self.errorMsg = 'unit should be removed when meet 0 in "${selector}"'
22 |
23 | def check(self, rule, config):
24 |
25 | values = rule.value.split(' ')
26 |
27 | for v in values:
28 | v = v.strip()
29 | if v.find('(') != -1:
30 | matched = self._startsWithZero(v.split('(')[1])
31 | else:
32 | matched = self._startsWithZero(v)
33 |
34 | if matched is None:
35 | continue
36 |
37 | for m in matched:
38 | if m != '0s':
39 | return False
40 |
41 | return True
42 |
43 | def fix(self, rule, config):
44 | if rule.name == 'expression':
45 | return
46 |
47 | fixed = rule.fixedValue
48 | rule.fixedValue = rule.fixedValue.replace(',', ', ')
49 |
50 | collector = []
51 | for v in rule.fixedValue.split(' '):
52 | v = v.strip()
53 | if v.find('(') != -1:
54 | matched = self._startsWithZero(v.split('(')[1])
55 | else:
56 | matched = self._startsWithZero(v)
57 |
58 | if matched is None:
59 | collector.append(v)
60 | continue
61 |
62 | finalV = v;
63 | for m in matched:
64 | if m != '0s':
65 | finalV = finalV.replace(m, '0')
66 | collector.append(finalV)
67 |
68 | rule.fixedValue = replacer_unit.sub(', ', ' '.join(collector))
69 |
70 | def _startsWithZero(self, value):
71 | matcher = pattern_unit.match(value)
72 | if matcher is not None:
73 | return matcher.groups()
74 | return None
75 |
--------------------------------------------------------------------------------
/ckstyle/plugins/FEDMultiLineSpaces.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 |
4 | from .Base import *
5 | from .helper import isCss3PrefixProp
6 |
7 | class FEDMultiLineSpaces(RuleChecker):
8 |
9 | '''{
10 | "summary":"CSS多行风格的空格检查",
11 | "desc":"多行风格下,CSS的空格检查包括:
12 |
13 | - 选择器的空格
14 | - 属性的空格
15 | - 结尾}的空格
16 |
17 | 具体请参见人人相关的CSS规范"
18 | }'''
19 |
20 | def __init__(self):
21 | self.id = 'multi-line-space'
22 | self.errorLevel = ERROR_LEVEL.LOG
23 | self.errorMsg_name_pre = 'should have 4 spaces before "${name}" in "${selector}"'
24 | self.errorMsg_name_after = 'should not have "space" after "${name}" in "${selector}"'
25 | self.errorMsg_value_pre = 'should have (only) one "space" before value of "${name}" in "${selector}"'
26 | self.errorMsg = ''
27 |
28 | def check(self, rule, config):
29 | singleLine = rule.getRuleSet().getSingleLineFlag()
30 | if singleLine:
31 | return True
32 |
33 | prefix = ' ' * 4
34 | name = rule.roughName
35 | value = rule.roughValue
36 | stripped = rule.roughName.strip()
37 |
38 | # leave special css3 props for FEDCss3AttrChecker
39 | if isCss3PrefixProp(rule.name):
40 | if name.endswith(' '):
41 | self.errorMsg = self.errorMsg_name_after
42 | return False
43 |
44 | if not value.startswith(' ') or value.startswith(' '):
45 | self.errorMsg = self.errorMsg_value_pre
46 | return False
47 |
48 | return True
49 |
50 | if name.find('\t') != -1:
51 | name = name.replace('\t', prefix)
52 | if not name.startswith(prefix):
53 | self.errorMsg = self.errorMsg_name_pre
54 | return False
55 | if name.startswith(' ' * 5):
56 | self.errorMsg = self.errorMsg_name_pre
57 | return False
58 | if name.endswith(' '):
59 | self.errorMsg = self.errorMsg_name_after
60 | return False
61 |
62 | if not value.startswith(' ') or value.startswith(' '):
63 | self.errorMsg = self.errorMsg_value_pre
64 | return False
65 |
66 | return True
67 |
--------------------------------------------------------------------------------
/ckstyle/cssparser/helper.py:
--------------------------------------------------------------------------------
1 | def isAlphaChar(char):
2 | return 97 <= ord(char) <= 122
3 |
4 | specialTexts = [
5 | {'start':'@', 'text':'@import', 'end':';\n'},
6 | {'start':'@', 'text':'@charset', 'end':';\n'},
7 | {'start':'@', 'text':'@namespace', 'end':';'},
8 | {'start':'@', 'text':'@-css-compiler ', 'end':'}'},
9 | {'start':'@', 'text':'@-css-compiler{', 'end':'}'},
10 | {'start':'@', 'text':'@-css-compiler-', 'end':';\n'}]
11 |
12 | specialStartChars = set([x['start'] for x in specialTexts])
13 |
14 | nestedStatements = ['keyframes', '@media', '@-moz-document']
15 |
16 | def isSpecialStart(char):
17 | for x in specialStartChars:
18 | if x == char:
19 | return True
20 | return False
21 |
22 | def isNestedStatement(selector):
23 | if selector.find('@') == -1:
24 | return False
25 | for x in nestedStatements:
26 | if selector.find(x) != -1:
27 | return True
28 | return False
29 |
30 | def handleSpecialStatement(text, i, length, char):
31 | for obj in specialTexts:
32 | if char == obj['start'] and isSpecialString(text, i, obj["text"]):
33 | a, b = findCharFrom(text, i, length, obj["end"])
34 | return a, b, obj["text"]
35 | return None, None, None
36 |
37 | def findCharFrom(text, i, length, left, right = None):
38 | counter = 1
39 | collector = ''
40 | for j in range(i + 1, length):
41 | if right == None:
42 | if text[j] == left or left.find(text[j]) != -1:
43 | break;
44 | else:
45 | collector = collector + text[j]
46 | else:
47 | if text[j] == left:
48 | collector = collector + text[j]
49 | counter = counter + 1
50 | elif text[j] == right:
51 | collector = collector + text[j]
52 | counter = counter - 1
53 | if counter == 0:
54 | break;
55 | else:
56 | collector = collector + text[j]
57 | return j, collector
58 |
59 | def isSpecialString(text, i, string):
60 | return text[i: i + len(string)] == string
61 |
62 | def isCommentStart(char, text, i):
63 | return char == '/' and i + 1 < len(text) and text[i + 1] == '*'
64 |
65 | def isCommentEnd(char, text, i):
66 | return char == '/' and text[i - 1] == '*'
67 |
68 |
--------------------------------------------------------------------------------
/ckstyle/entity/StyleSheet.py:
--------------------------------------------------------------------------------
1 | from .RuleSet import RuleSet
2 | from .ExtraStatement import ExtraStatement
3 | from .NestedStatement import NestedStatement
4 | from .EntityUtil import ALL
5 |
6 | class StyleSheet():
7 | def __init__(self, fileName = ''):
8 | self._ruleSets = [];
9 | self._file = fileName
10 | self.browser = ALL
11 | self.toBeUsed = {}
12 |
13 | def addRuleSetByStr(self, selector, attrs, comment):
14 | self._ruleSets.append(RuleSet(selector, attrs, comment, self))
15 |
16 | def addExtraStatement(self, operator, statement, comment):
17 | self._ruleSets.append(ExtraStatement(operator, statement, comment, self))
18 |
19 | def addNestedRuleSet(self, selector, attrs, comment):
20 | self._ruleSets.append(NestedStatement(selector, attrs, comment, self))
21 |
22 | def setFile(self, fileName):
23 | self._file = fileName
24 |
25 | def getFile(self):
26 | return self._file
27 |
28 | def getRuleSets(self):
29 | return self._ruleSets
30 |
31 | def removeRuleSetByIndex(self, index):
32 | self._ruleSets[index] = None
33 |
34 | def removeRuleSet(self, ruleSet):
35 | newRuleSets = []
36 | for x in self._ruleSets:
37 | if x == ruleSet:
38 | continue
39 | newRuleSets.append(x)
40 | self._ruleSets = newRuleSets
41 |
42 | def clean(self):
43 | newRuleSets = []
44 | for x in self._ruleSets:
45 | if x is None:
46 | continue
47 | newRuleSets.append(x)
48 | self._ruleSets = newRuleSets
49 |
50 | def getRuleSetBySelector(self, selector):
51 | for ruleSet in self._ruleSets:
52 | if ruleSet.selector == selector:
53 | return ruleSet
54 |
55 | def compress(self, browser = ALL):
56 | result = []
57 | for ruleSet in self._ruleSets:
58 | if not ruleSet.browser & browser:
59 | continue
60 | result.append(ruleSet.compress(browser))
61 | return ''.join(result)
62 |
63 | def fixed(self, config):
64 | result = []
65 | for ruleSet in self._ruleSets:
66 | result.append(ruleSet.fixed(config))
67 | return '\n\n'.join(result)
68 |
69 | def rebase(self):
70 | for ruleSet in self._ruleSets:
71 | ruleSet.rebase()
72 |
--------------------------------------------------------------------------------
/tests/unit/check/FEDHighPerformanceSelector.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @author: zhifu.wang@renren-inc.com
3 | */
4 | @unit-test-expecteds {
5 | 0: please shorter the selector ".a .b .c .d .e .f"
6 | 0: use less tag in "body div ul li"
7 | 0: use less tag in "body .a .b li"
8 | 0: use less tag in "ul li"
9 | 0: use less tag in "ol li"
10 | 0: use less tag in "dl dt"
11 | 0: use less tag in "dl dd"
12 |
13 | 0: should not put "HTMLtag" and "#id" together in "div#test"
14 | 0: should not put "HTMLtag" and ".class" together in "body.class ul li"
15 | 0: should not put "HTMLtag" and ".class" together in "body.class ul .class .a"
16 | 0: should not put "HTMLtag" and ".class" together in "ul.test li"
17 |
18 | 0: should not use ~=,^=,|=,$=,*= in selector of ".div[class~=xxx]"
19 | 0: should not use ~=,^=,|=,$=,*= in selector of ".div[class^=xxx]"
20 | 0: should not use ~=,^=,|=,$=,*= in selector of ".div[class*=xxx]"
21 | 0: should not use ~=,^=,|=,$=,*= in selector of ".div[class|=xxx]"
22 | 0: should not use ~=,^=,|=,$=,*= in selector of ".div[class$=xxx]"
23 |
24 | 0: should not put "HTMLtag" and ".class" together in "div.class::selection"
25 |
26 | 1: ".div[class*=xxx], .div[class|=xxx], .div[class$=xxx], .div[class^=xxx], .div[class~=xxx], div#test, .a .b .c .d .e .f, body div ul li, body .a .b li, body.class ul li, body.class ul .class .a, ul.test li, ul li, ol li, dl dt, dl dd" contains the same rules in "FEDHighPerformanceSelector.css"
27 | }
28 |
29 | div.class::selection {
30 | color: red;
31 | }
32 |
33 | .div[class*=xxx] {
34 | width: 100px;
35 | }
36 |
37 | .div[class|=xxx] {
38 | width: 100px;
39 | }
40 |
41 | .div[class$=xxx] {
42 | width: 100px;
43 | }
44 |
45 | .div[class^=xxx] {
46 | width: 100px;
47 | }
48 |
49 | .div[class~=xxx] {
50 | width: 100px;
51 | }
52 |
53 | div#test {
54 | width: 100px;
55 | }
56 |
57 | .a .b .c .d .e .f {
58 | width: 100px;
59 | }
60 |
61 | body div ul li {
62 | width: 100px;
63 | }
64 |
65 | body .a .b li {
66 | width: 100px;
67 | }
68 |
69 | body.class ul li {
70 | width: 100px;
71 | }
72 |
73 | body.class ul .class .a {
74 | width: 100px;
75 | }
76 |
77 | ul.test li {
78 | width: 100px;
79 | }
80 |
81 | ul li {
82 | width: 100px;
83 | }
84 |
85 | ol li {
86 | width: 100px;
87 | }
88 |
89 | dl dt {
90 | width: 100px;
91 | }
92 |
93 | dl dd {
94 | width: 100px;
95 | }
96 |
--------------------------------------------------------------------------------
/tests/unit/parser/ExtraStatement.py:
--------------------------------------------------------------------------------
1 | from helper import *
2 |
3 | def doTest():
4 | _withEnter()
5 | _withoutEnter()
6 |
7 | def _withoutEnter():
8 | parser = CssParser('@import url(xxxx);@charset utf-8;.test {_width : 100px;}')
9 | parser.doParse()
10 | ok(parser is not None, 'parser is not None')
11 | ok(parser.styleSheet is not None, 'parser.styleSheet is not None')
12 | equal(len(parser.styleSheet.getRuleSets()), 3, 'three rule set')
13 |
14 | styleSheet = parser.styleSheet
15 | importer = styleSheet.getRuleSets()[0]
16 | ok(importer.extra, 'import is extra')
17 | ok(importer.isImport(), 'it is import')
18 | equal(importer.operator, '@import', 'it is @import')
19 | equal(importer.statement, '@import url(xxxx);', 'statement is ok')
20 |
21 | charset = styleSheet.getRuleSets()[1]
22 | ok(charset.extra, 'charset is extra')
23 | ok(not charset.isImport(), 'it is not import')
24 | equal(charset.operator, '@charset', 'it is @charset')
25 | equal(charset.statement, '@charset utf-8;', 'statement is ok')
26 |
27 | rule = styleSheet.getRuleSets()[2]
28 | ok(not rule.extra, 'not extra')
29 | equal(rule.selector, '.test', 'selector is ok')
30 | equal(rule.getRuleByName('width').value, '100px', 'value is ok')
31 |
32 | def _withEnter():
33 | parser = CssParser('@import url(xxxx);\n@charset utf-8;\n.test {_width : 100px;}')
34 | parser.doParse()
35 | ok(parser is not None, 'parser is not None')
36 | ok(parser.styleSheet is not None, 'parser.styleSheet is not None')
37 | equal(len(parser.styleSheet.getRuleSets()), 3, 'three rule set')
38 |
39 | styleSheet = parser.styleSheet
40 | importer = styleSheet.getRuleSets()[0]
41 | ok(importer.extra, 'import is extra')
42 | ok(importer.isImport(), 'it is import')
43 | equal(importer.operator, '@import', 'it is @import')
44 | equal(importer.statement, '@import url(xxxx);', 'statement is ok')
45 |
46 | charset = styleSheet.getRuleSets()[1]
47 | ok(charset.extra, 'charset is extra')
48 | ok(not charset.isImport(), 'it is not import')
49 | equal(charset.operator, '@charset', 'it is @charset')
50 | equal(charset.statement, '@charset utf-8;', 'statement is ok')
51 |
52 | rule = styleSheet.getRuleSets()[2]
53 | ok(not rule.extra, 'not extra')
54 | equal(rule.selector, '.test', 'selector is ok')
55 | equal(rule.getRuleByName('width').value, '100px', 'value is ok')
56 |
--------------------------------------------------------------------------------
/utils/exportRules.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/python
2 | #encoding=utf-8
3 | import os
4 | import json
5 |
6 | def loadPlugins(pluginDir):
7 | ids = []
8 | tmp = '{id:"%s", priority:%s, summary:"%s", desc:"%s", checked:%s}';
9 |
10 | '''从plugins目录动态载入检查类'''
11 | for filename in os.listdir(pluginDir):
12 | if not filename.endswith('.py') or filename.startswith('_'):
13 | continue
14 | if filename == 'Base.py' or filename == 'helper.py':
15 | continue
16 | pluginName = os.path.splitext(filename)[0]
17 |
18 | # 获取plugins的引用
19 | plugin = __import__("ckstyle.plugins." + pluginName, fromlist = [pluginName])
20 | pluginClass = None
21 | if hasattr(plugin, pluginName):
22 | pluginClass = getattr(plugin, pluginName)
23 | else:
24 | console.error('[TOOL] class %s should exist in %s.py' % (pluginName, pluginName))
25 | continue
26 | # 构造plugin的类
27 | instance = pluginClass()
28 | if (hasattr(instance, 'private') and getattr(instance, 'private') is True):
29 | continue
30 | obj = {}
31 | obj["id"] = instance.id
32 | obj["priority"] = instance.errorLevel
33 | obj["desc"] = pluginClass.__name__
34 | obj["summary"] = pluginClass.__name__
35 | doc = pluginClass.__doc__
36 |
37 | data = None
38 | if (doc != None):
39 | doc = doc.replace('\n', '')
40 | doc = doc.replace('\t', '')
41 | doc = doc.replace(' ', '')
42 | try:
43 | data = json.loads(doc)
44 | except Exception, e:
45 | print '[JSON ERROR] doc error in ' + pluginClass.__name__ + ', not json format'
46 | obj["desc"] = pluginClass.__doc__
47 | if data is not None:
48 | if data.has_key('summary') and data['summary'] != 'xxx':
49 | obj['summary'] = data['summary'].encode('utf-8')
50 | if data.has_key('desc') and data['desc'] != 'xxx':
51 | obj['desc'] = data['desc'].encode('utf-8')
52 |
53 | obj["checked"] = 'true' if (instance.errorLevel == 0 or instance.errorLevel == 1) else 'false'
54 | ids.append(tmp % (obj["id"], obj["priority"], obj["summary"], obj["desc"], obj["checked"]))
55 |
56 | open('D:/git/CSSCheckStyle-website/js/rules.js', 'w').write('var RULES = [\n ' + (',\n '.join(ids)) + '\n];');
57 |
58 | loadPlugins('../ckstyle/plugins')
--------------------------------------------------------------------------------