├── csssempai ├── json2.py ├── __init__.py ├── magic.py ├── sempai.py └── cssjson.py ├── .python-version ├── setup.cfg ├── .gitignore ├── .requirements ├── setup.py └── README.md /csssempai/json2.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | css_sempai_test 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 3 | 4 | -------------------------------------------------------------------------------- /csssempai/__init__.py: -------------------------------------------------------------------------------- 1 | from .sempai import imports 2 | #from . import tests 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nohup.out 2 | dist/ 3 | css_sempai.egg-info/ 4 | build/ 5 | csstest.css 6 | tmp/ 7 | *.pyc 8 | npm-debug.log 9 | -------------------------------------------------------------------------------- /csssempai/magic.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from .sempai import SempaiLoader 4 | 5 | sys.meta_path.append(SempaiLoader) 6 | 7 | -------------------------------------------------------------------------------- /.requirements: -------------------------------------------------------------------------------- 1 | pbr==1.8.1 2 | six==1.10.0 3 | stevedore==1.12.0 4 | tinycss==0.3 5 | virtualenv==14.0.6 6 | virtualenv-clone==0.2.6 7 | virtualenvwrapper==4.7.1 8 | wheel==0.29.0 9 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | try: 4 | from setuptools import setup 5 | except ImportError: 6 | from distutils.core import setup 7 | 8 | with open('README.md') as file_readme: 9 | readme = file_readme.read() 10 | 11 | setup(name='css-sempai', 12 | version='0.4.0', 13 | description='Use CSS files as if they\'re python modules', 14 | long_description=readme, 15 | author='nerdfiles', 16 | author_email='nerdfiles@gmail.com', 17 | license='WTFPL', 18 | url='https://github.com/nerdfiles/css-sempai', 19 | classifiers=[ 20 | 'Intended Audience :: Developers', 21 | 'License :: OSI Approved :: MIT License', 22 | 'Programming Language :: Python', 23 | 'Programming Language :: Python :: 2.6', 24 | 'Programming Language :: Python :: 2.7', 25 | 'Programming Language :: Python :: 3.2', 26 | 'Programming Language :: Python :: 3.3', 27 | 'Programming Language :: Python :: 3.4', 28 | ], 29 | keywords='please don\'t use this library for anything', 30 | packages=['csssempai', 'csssempai.tests'], 31 | test_suite='csssempai.tests' 32 | ) 33 | 34 | -------------------------------------------------------------------------------- /csssempai/sempai.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import imp 3 | import json 4 | import tinycss 5 | import os 6 | import sys 7 | import itertools 8 | 9 | 10 | class DottedDict(dict): 11 | 12 | def __getattr__(self, attr): 13 | try: 14 | return self[attr] 15 | except KeyError: 16 | raise AttributeError("'{}'".format(attr)) 17 | 18 | __setattr__ = dict.__setitem__ 19 | 20 | __delattr__ = dict.__delitem__ 21 | 22 | 23 | def get_css_path(directory, name): 24 | css_path = os.path.join(directory, '{name}.css'.format(name=name)) 25 | if os.path.isfile(css_path): 26 | return css_path 27 | 28 | 29 | class SempaiLoader(object): 30 | def __init__(self, css_path): 31 | self.css_path = css_path 32 | 33 | @classmethod 34 | def find_module(cls, name, path=None): 35 | for d in sys.path: 36 | css_path = get_css_path(d, name) 37 | if css_path is not None: 38 | return cls(css_path) 39 | 40 | if path is not None: 41 | name = name.split('.')[-1] 42 | for d in path: 43 | css_path = get_css_path(d, name) 44 | if css_path is not None: 45 | return cls(css_path) 46 | 47 | 48 | def load_module(self, name): 49 | if name in sys.modules: 50 | return sys.modules[name] 51 | 52 | mod = imp.new_module(name) 53 | mod.__file__ = self.css_path 54 | mod.__loader__ = self 55 | 56 | #decoder = json.JSONDecoder(object_hook=DottedDict) 57 | parser = tinycss.make_parser('page3') 58 | 59 | try: 60 | stylesheet = parser.parse_stylesheet_file(self.css_path) 61 | except: 62 | raise ImportError( 63 | 'Could not open file.') 64 | 65 | b = {} 66 | 67 | #import pdb; pdb.set_trace() 68 | for i in stylesheet.rules: 69 | b[i.selector.as_css()] = i.declarations 70 | 71 | mod.__dict__.update(b) 72 | 73 | sys.modules[name] = mod 74 | return mod 75 | 76 | 77 | @contextlib.contextmanager 78 | def imports(): 79 | try: 80 | sys.meta_path.append(SempaiLoader) 81 | yield 82 | finally: 83 | sys.meta_path.remove(SempaiLoader) 84 | 85 | -------------------------------------------------------------------------------- /csssempai/cssjson.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | class Pyzzle: 5 | 6 | def isInstanceOf(x): 7 | return x 8 | 9 | def isEmpty(x): 10 | return x.length is 0 11 | 12 | def __init__(self): 13 | print(self) 14 | 15 | selX = re.match('[^\s\;\{\}][^\;\{\}]*)\{') 16 | endX = re.match('\}') 17 | lineX = re.match('([^\;\{\}]*)\;') 18 | commentX = re.match('\/\*[\s\S]*?\*\/') 19 | lineAttrX = re.match('([^\:]+):([^\;]*);') 20 | 21 | altX = re.match( 22 | '(\/\*[\s\S]*?\*\/)|([^\s\;\{\}][^\;\{\}]*(?=\{))|(\})|([^\;\{\}]+\;(?!\s*\*\/))') # gmi 23 | 24 | # Capture groups 25 | capComment = 1 26 | capSelector = 2 27 | capEnd = 3 28 | capAttr = 4 29 | HTMLStyleElement = None 30 | 31 | def isCssJson(node): 32 | return (node.attributes and node.children) if not isEmpty( 33 | node) else False 34 | 35 | def isValidStyleNode(node): 36 | return ( 37 | isInstanceOf( 38 | node, 39 | HTMLStyleElement)) and node.sheet.cssRules.length > 0 40 | 41 | def timestamp(): 42 | return Date.now() or Date() 43 | 44 | def strAttr(name, value, depth): 45 | return '\t'.repeat(depth) + name + ': ' + value + ';\n' 46 | 47 | def strNode(name, value, depth): 48 | cssString = '\t'.repeat(depth) + name + ' {\n' 49 | cssString += self.toCSS(value, depth + 1) 50 | cssString += '\t'.repeat(depth) + '}\n' 51 | return cssString 52 | 53 | class Base(object): 54 | content = "" 55 | 56 | def get_content(self): 57 | return self.content 58 | 59 | def to_json(self): 60 | node = Dict() 61 | node['children'] = {} 62 | node['attributes'] = {} 63 | match = None 64 | count = 0 65 | return self.json 66 | 67 | def to_css(self): 68 | return self.css 69 | 70 | def to_head(self): 71 | return self.head 72 | 73 | class CSS(Base): 74 | content = "html {}" 75 | 76 | class JSON(Base): 77 | content = "{}" 78 | 79 | class BaseFactory(): 80 | 81 | def convert(self, typ): 82 | targetclass = typ.capitalize() 83 | return globals()[targetclass]() 84 | 85 | base_obj = BaseFactory() 86 | base = ['css', 'json'] 87 | for b in base: 88 | print base_obj.convert(b).get_content() 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # css-sempai 2 | 3 | Yeah, I said it. Or may re-implement `json-sempai` as a client-side middleware or server-side templated style generator. 4 | 5 | ## Ideally: 6 | 7 | /* some css */ 8 | 9 | ## Becomes [this][0]: 10 | 11 | ```json 12 | { 13 | "html": { 14 | "margin": 0, 15 | "padding": 0, 16 | "body": { 17 | "margin": 0, 18 | "padding": 0, 19 | "section": { 20 | "max-width": "800px" 21 | } 22 | } 23 | } 24 | } 25 | ``` 26 | 27 | ## Try [this][1]: 28 | 29 | ```python 30 | >>> from csssempai import magic 31 | >>> import stylesheet 32 | >>> stylesheet 33 | 34 | >>> stylesheet.html.body.section['max-width'] 35 | ``` 36 | 37 | ### That is: 38 | 39 | If we were to try this end-to-end in JS. 40 | 41 | ```javascript 42 | // Sempai loaded elsewhere... 43 | require(['stylesheet'], function (stylesheet) { 44 | var styleInterface = Sempai(stylesheet); 45 | return new Descartes(styleInterface); 46 | }); 47 | ``` 48 | 49 | Or (the [Openchain][3] idea): 50 | 51 | ```javascript 52 | var styleInterface = { 53 | "html": { 54 | "body": { 55 | "#WebPage": { 56 | ".breadcrumb": function () { 57 | function getRemoteStyleInterface() { 58 | return JSON.parse($.ajax({ 59 | type : "GET", 60 | url : '/v/1.0.0+222/!WebPage.breadcrumb', 61 | async : false 62 | }).responseText); 63 | } 64 | return getRemoteStyleInterface(); 65 | } 66 | } 67 | } 68 | } 69 | }; 70 | new Descartes(styleInterface) 71 | ``` 72 | 73 | ```javascript 74 | var styleInterface = JSON.parse($.ajax({ 75 | type : "GET", 76 | url : '/v/1.0.0+222/!WebPage.breadcrumb', 77 | async : false 78 | }).responseText); 79 | new Descartes(styleInterface) 80 | ``` 81 | 82 | ## Case Use 83 | 84 | 1. Would be neat for server-side variable loading of dynamic CSS Sekizai conditionings in Django. 85 | 2. Gives me a reason to put CSS as JSON into [Openchain][3] if I am in ‘templated style’, this could be a middleware to Django. 86 | I was also thinking that CSS Documents in this way could be scaffolded as JSONAPI. Or maybe we need a CSSAPI — etiquette for sending small bits of CSS. My work on Openchain makes this relevant — where your CSS is loaded dynamically based on Unitary Reference Architecture that is expressed in the cryptographic ledger. 87 | 3. TDD CSS 88 | 4. Inline E-mail CSS generator from commandline 89 | 5. css lexical categorizer (css pears) 90 | 6. static code analysis (csssmell) 91 | 7. css doc generator 92 | 8. css evolution diff 93 | 9. css ctags formatter 94 | 10. css state change logger (beef) 95 | Set up wifi hotspots with Openchain-based off-chain ledgers to process transactions. So they use the wifi, and if they want to make a wallet, the wallet tracks their usage and their social media network usage that's published anonymously. Pull the social data, process it, publish it through the chain. Set up css loggers to track states of elements as users surf. Track products. Give real-time "vision" of other users navigational habits and interactions with products. Pitch as bitcoin enabling "product vision" like night vision. Censorship resistence enables digital clairvoyance. 96 | 11. css-sass-utils like XML-HTML-utils[0]. 97 | 98 | ## CSS2AngularJS 99 | 100 | Sometimes I want to do like `$ cp` on parts of the CSS document and paste into another CSS doc. Or use a CSS doc as a config to hxextract from an HTML doc. Or use a CSS doc to generate jQuery canonical event code like Yeoman. It'd be cool to generate Controllers/Directives in AngularJS from CSS docs using DOM events as symbol hooks. So styling an element with a particular BEM schema would entail Controller or Directive relations and macro triggers. 101 | 102 | ul > li > a.MainCtrl--section__paymentDirective { } 103 | 104 | Or: 105 | 106 | [ng-controller="MainCtrl"] section [paymentDirective] a { } 107 | 108 | Runs: 109 | 110 | yo ang:controller MainCtrl 111 | yo ang:directive paymentDirective 112 | 113 | Basically treat keys in CSS like `cmd` fields in Grunt to pre/post hook operations. 114 | 115 | ## (But really...) Dealing with Pseudoselectors 116 | 117 | Your CSS wants to say this to sempai: 118 | 119 | ```json 120 | { 121 | "links": { 122 | "self": "/url" 123 | }, 124 | "data": [{ 125 | "relationships": { 126 | "div": { 127 | "links": { 128 | "hover": "/uri" 129 | }, 130 | "data": [{ 131 | "id": 1, 132 | "relationships": { 133 | "h1": { 134 | "links": { 135 | "target": "/uri" 136 | }, 137 | "data": [{ 138 | "id": 2 139 | }] 140 | } 141 | } 142 | }], 143 | "included": [{ 144 | "id": 2, 145 | "type": "h1" 146 | }] 147 | } 148 | } 149 | }], 150 | "included": [{ 151 | "id": 1, 152 | "type": "div" 153 | }] 154 | } 155 | ``` 156 | 157 | At the end of the day, sempai makes for us a cssapi. 158 | 159 | Use `jq` to ease querying through this beast’s fur. 160 | 161 | ## WAT ? 162 | 163 | `"hover": "/uri"` is specifying an endpoint to download more CSS. So something like a Descartes wrapper would exist to process such keys as pre-compile phase interpolations. 164 | So sempai might see pseudoselectors and then rip them out like weeds, leaving instead a relative URI to inform the relevant Descartes interface wrapper. 165 | Pre-compile Phase Interpolated Hooks as Pseudoselectors (:nth-child(), :hover, etc.) are expected by Descartes Transformation moments in the interface with 166 | with CSSAPI spec. 167 | 168 | — 169 | 170 | [0]: https://descartes.io/ 171 | [1]: https://github.com/kragniz/json-sempai/ 172 | [3]: https://docs.openchain.org/en/latest/api/ledger.html#id3 173 | [4]: http://www.w3.org/Tools/HTML-XML-utils/README 174 | --------------------------------------------------------------------------------