├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE.txt ├── Makefile ├── README.md ├── VS.md ├── bower.json ├── build ├── restyle.amd.js ├── restyle.js ├── restyle.max.amd.js ├── restyle.max.js ├── restyle.max.properties.js ├── restyle.max.proxy.js ├── restyle.node.js ├── restyle.properties.js └── restyle.proxy.js ├── index.html ├── package.json ├── specs.md ├── src ├── properties-a.js ├── properties-z.js ├── properties.js ├── proxy-a.js ├── proxy-z.js ├── proxyHandler.js ├── restyle.js └── utils.js ├── template ├── amd.after ├── amd.before ├── copyright ├── license.after ├── license.before ├── md.after ├── md.before ├── node.after ├── node.before ├── var.after └── var.before └── test ├── .test.js └── restyle.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/* 2 | test/* 3 | template/* 4 | node_modules/* 5 | build/*.amd.js 6 | build/*.max.js 7 | Makefile 8 | index.html 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | - 0.12 5 | - 4 6 | - 6 7 | - 7 8 | git: 9 | depth: 1 10 | branches: 11 | only: 12 | - master 13 | before_script: 14 | - "npm install wru" 15 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014-2015 by Andrea Giammarchi - @WebReflection 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build var node proxy properties amd size hint clean test web preview pages dependencies 2 | 3 | # repository name 4 | REPO = restyle 5 | 6 | # make var files 7 | VAR = src/$(REPO).js 8 | 9 | # make node files 10 | NODE = $(VAR) 11 | 12 | # make amd files 13 | AMD = $(VAR) 14 | 15 | LICENSE = template/license.before\ 16 | LICENSE.txt\ 17 | template/license.after 18 | 19 | # README constant 20 | 21 | 22 | # default build task 23 | build: 24 | make clean 25 | make var 26 | make node 27 | make amd 28 | make proxy 29 | make properties 30 | make test 31 | # make hint 32 | make size 33 | 34 | # build generic version 35 | var: 36 | mkdir -p build 37 | cat template/var.before $(VAR) template/var.after >build/no-copy.$(REPO).max.js 38 | node node_modules/.bin/uglifyjs --verbose build/no-copy.$(REPO).max.js >build/no-copy.$(REPO).js 39 | cat $(LICENSE) build/no-copy.$(REPO).max.js >build/$(REPO).max.js 40 | cat template/copyright build/no-copy.$(REPO).js >build/$(REPO).js 41 | rm build/no-copy.$(REPO).max.js 42 | rm build/no-copy.$(REPO).js 43 | 44 | # build node.js version 45 | node: 46 | mkdir -p build 47 | cat template/license.before LICENSE.txt template/license.after template/node.before $(NODE) template/node.after >build/$(REPO).node.js 48 | 49 | # build AMD version 50 | amd: 51 | mkdir -p build 52 | cat template/amd.before $(AMD) template/amd.after >build/no-copy.$(REPO).max.amd.js 53 | node node_modules/.bin/uglifyjs --verbose build/no-copy.$(REPO).max.amd.js >build/no-copy.$(REPO).amd.js 54 | cat $(LICENSE) build/no-copy.$(REPO).max.amd.js >build/$(REPO).max.amd.js 55 | cat template/copyright build/no-copy.$(REPO).amd.js >build/$(REPO).amd.js 56 | rm build/no-copy.$(REPO).max.amd.js 57 | rm build/no-copy.$(REPO).amd.js 58 | 59 | 60 | # build restyle.proxy 61 | proxy: 62 | mkdir -p build 63 | cat src/proxy-a.js src/utils.js src/proxyHandler.js src/proxy-z.js >build/no-copy.$(REPO).max.proxy.js 64 | node node_modules/.bin/uglifyjs --verbose build/no-copy.$(REPO).max.proxy.js >build/no-copy.$(REPO).proxy.js 65 | cat $(LICENSE) build/no-copy.$(REPO).max.proxy.js >build/$(REPO).max.proxy.js 66 | cat template/copyright build/no-copy.$(REPO).proxy.js >build/$(REPO).proxy.js 67 | rm build/no-copy.$(REPO).max.proxy.js 68 | rm build/no-copy.$(REPO).proxy.js 69 | 70 | 71 | # build restyle.properties (similar to proxy but more compatible) 72 | properties: 73 | mkdir -p build 74 | cat src/properties-a.js src/utils.js src/properties.js src/properties-z.js >build/no-copy.$(REPO).max.properties.js 75 | node node_modules/.bin/uglifyjs --verbose build/no-copy.$(REPO).max.properties.js >build/no-copy.$(REPO).properties.js 76 | cat $(LICENSE) build/no-copy.$(REPO).max.properties.js >build/$(REPO).max.properties.js 77 | cat template/copyright build/no-copy.$(REPO).properties.js >build/$(REPO).properties.js 78 | rm build/no-copy.$(REPO).max.properties.js 79 | rm build/no-copy.$(REPO).properties.js 80 | 81 | size: 82 | wc -c build/$(REPO).max.js 83 | gzip -c build/$(REPO).js | wc -c 84 | 85 | # hint built file 86 | hint: 87 | node node_modules/.bin/jshint build/$(REPO).max.js 88 | 89 | # clean/remove build folder 90 | clean: 91 | rm -rf build 92 | 93 | # tests, as usual and of course 94 | test: 95 | npm test 96 | 97 | # launch polpetta (ctrl+click to open the page) 98 | web: 99 | node node_modules/.bin/tiny-cdn run 100 | 101 | # markdown the readme and view it 102 | preview: 103 | node_modules/.bin/md2html.js README.md >README.md.htm 104 | cat template/md.before README.md.htm template/md.after >README.md.html 105 | open README.md.html 106 | sleep 3 107 | rm README.md.htm README.md.html 108 | 109 | pages: 110 | make var 111 | mkdir -p ~/tmp 112 | mkdir -p ~/tmp/$(REPO) 113 | cp .gitignore ~/tmp/ 114 | cp -rf src ~/tmp/$(REPO) 115 | cp -rf build ~/tmp/$(REPO) 116 | cp -rf test ~/tmp/$(REPO) 117 | cp index.html ~/tmp/$(REPO) 118 | git checkout gh-pages 119 | cp ~/tmp/.gitignore ./ 120 | mkdir -p test 121 | rm -rf test 122 | cp -rf ~/tmp/$(REPO) test 123 | git add .gitignore 124 | git add test 125 | git add test/. 126 | git commit -m 'automatic test generator' 127 | git push 128 | git checkout master 129 | rm -r ~/tmp/$(REPO) 130 | 131 | # modules used in this repo 132 | dependencies: 133 | rm -rf node_modules 134 | mkdir node_modules 135 | npm install wru 136 | npm install polpetta 137 | npm install uglify-js@1 138 | npm install jshint 139 | npm install markdown 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | restyle 2 | ======= 3 | 4 | [![build status](https://secure.travis-ci.org/WebReflection/restyle.svg)](http://travis-ci.org/WebReflection/restyle) 5 | 6 | **new** 10 minutes intro about [restyle.js in vimeo](https://vimeo.com/86671874) 7 | 8 | This project has been somehow inspired by [absurd.js](http://krasimir.github.io/absurd/) but it is not exactly the same. 9 | 10 | You can check [restyle specifications](https://github.com/WebReflection/restyle/blob/master/specs.md#restyle-object-specifications) or go directly to a [face 2 face against absurd](https://github.com/WebReflection/restyle/blob/master/VS.md#restyle-vs-absurd-by-examples) but the long story short is that no JavaScript library out there fits in about 1KB and feels as natural as `restyle` does in typing CSS. 11 | 12 | Good news is, you can choose more now but let's see what's in the menu here ;-) 13 | 14 | ### In A Nutshell 15 | `restyle` is a function able to transform the following: 16 | 17 | ```javascript 18 | // we are in a browser 19 | // defining some style at runtime 20 | var myStyle = (function(){ 21 | 22 | // some function helper 23 | function getSomeNumber(boundary) { 24 | return Math.floor(Math.random() * (boundary + 1)); 25 | } 26 | 27 | // something we could reuse all over 28 | function hex(red, green, blue) { 29 | return '#'.concat( 30 | ('0' + red.toString(16)).slice(-2), 31 | ('0' + green.toString(16)).slice(-2), 32 | ('0' + blue.toString(16)).slice(-2) 33 | ); 34 | } 35 | 36 | // the fresh new appended style object wrap 37 | return restyle({ 38 | body: { 39 | backgroundColor: hex(100, 60, 25), 40 | padding: { 41 | top: 50, 42 | left: '30%' 43 | } 44 | }, 45 | '.component > li': { 46 | width: window.innerWidth, 47 | height: getSomeNumber(200) 48 | }, 49 | '.component > .icon-spinner': { 50 | animation: { 51 | name: 'spin', 52 | duration: '4s' 53 | } 54 | }, 55 | '@keyframes spin': { 56 | from: { 57 | transform: 'rotate(0deg)' 58 | }, 59 | to: { 60 | transform: 'rotate(360deg)' 61 | } 62 | } 63 | }); 64 | }()); 65 | ``` 66 | 67 | into this runtime appended and generated cross browser CSS style: 68 | 69 | ```css 70 | body { 71 | background-color: #643c19; 72 | padding-top: 50px; 73 | padding-left: 30%; 74 | } 75 | 76 | .component > li { 77 | width: 1251px; 78 | height: 182px; 79 | } 80 | 81 | .component > .icon-spinner { 82 | -webkit-animation-name: spin; 83 | -moz-animation-name: spin; 84 | -ms-animation-name: spin; 85 | -o-animation-name: spin; 86 | animation-name: spin; 87 | -webkit-animation-duration: 4s; 88 | -moz-animation-duration: 4s; 89 | -ms-animation-duration: 4s; 90 | -o-animation-duration: 4s; 91 | animation-duration: 4s; 92 | } 93 | 94 | @-webkit-keyframes spin { 95 | from { 96 | -webkit-transform: rotate(0deg); 97 | transform: rotate(0deg); 98 | } 99 | 100 | to { 101 | -webkit-transform: rotate(360deg); 102 | transform: rotate(360deg); 103 | } 104 | } 105 | 106 | @-moz-keyframes spin { 107 | from { 108 | -moz-transform: rotate(0deg); 109 | transform: rotate(0deg); 110 | } 111 | 112 | to { 113 | -moz-transform: rotate(360deg); 114 | transform: rotate(360deg); 115 | } 116 | } 117 | 118 | @-ms-keyframes spin { 119 | from { 120 | -ms-transform: rotate(0deg); 121 | transform: rotate(0deg); 122 | } 123 | 124 | to { 125 | -ms-transform: rotate(360deg); 126 | transform: rotate(360deg); 127 | } 128 | } 129 | 130 | @-o-keyframes spin { 131 | from { 132 | -o-transform: rotate(0deg); 133 | transform: rotate(0deg); 134 | } 135 | 136 | to { 137 | -o-transform: rotate(360deg); 138 | transform: rotate(360deg); 139 | } 140 | } 141 | 142 | @keyframes spin { 143 | from { 144 | -webkit-transform: rotate(0deg); 145 | -moz-transform: rotate(0deg); 146 | -ms-transform: rotate(0deg); 147 | -o-transform: rotate(0deg); 148 | transform: rotate(0deg); 149 | } 150 | 151 | to { 152 | -webkit-transform: rotate(360deg); 153 | -moz-transform: rotate(360deg); 154 | -ms-transform: rotate(360deg); 155 | -o-transform: rotate(360deg); 156 | transform: rotate(360deg); 157 | } 158 | } 159 | ``` 160 | 161 | with the ability to drop all those styles at once: 162 | 163 | ```javascript 164 | myStyle.remove(); 165 | ``` 166 | 167 | 168 | ### New In Version 0.4 169 | It is now possible to simplify transitions through the `transition` public static method method. 170 | ```js 171 | var transition = restyle.transition( 172 | genericElement, 173 | from: { 174 | opacity: '0', 175 | height: 34 176 | }, 177 | to: { 178 | opacity: '1' // will keep height 34 179 | }, 180 | function onTransitionEnd(e) { 181 | console.log('transition completed'); 182 | e.detail.clean(); // remove related styles 183 | } 184 | ); 185 | ``` 186 | 187 | It is also possible to create multiple transitions from a starting point. 188 | ```js 189 | var transition = restyle.transition( 190 | genericElement, 191 | from: { 192 | opacity: '0', 193 | height: 0 194 | }, 195 | to: [{ 196 | height: 200 // will keep opacity '0' 197 | }, { 198 | opacity: '1' // will keep height 200 199 | }], 200 | function onTransitionEnd(e) { 201 | console.log('transition completed'); 202 | e.detail.clean(); // remove related styles 203 | } 204 | ); 205 | ``` 206 | In latter case the final callback happens when last transition is completed. 207 | 208 | At any time it is possible to ignore the callback via `transition.drop()` or to clean all styles and transitions via `transition.clean()`. 209 | 210 | Please note that unless explicitly done, related styles will not be dropped. 211 | 212 | If you need to keep the transition end CSS please add a class and after that clean everything else. 213 | 214 | 215 | 216 | ### New In Version 0.3 217 | It is now possible to simplify animations through the `animate` method. 218 | 219 | ```js 220 | // a generic animation style 221 | var glowAnimation = restyle({ 222 | '@keyframes glow-animation': { 223 | '0%': { boxShadow: '0px 0px 0px 0px rgba(255,255,255,1)' }, 224 | '100%': { boxShadow: '0px 0px 32px 16px rgba(255,255,255,1)' } 225 | }, 226 | '.glow': { 227 | animation: { 228 | name: 'glow-animation', 229 | duration: '1s', 230 | direction: 'normal' 231 | } 232 | } 233 | }); 234 | 235 | // glowing function 236 | function glow(el, callback) { 237 | el.classList.add('glow'); 238 | return glowAnimation.animate(el, 'glow-animation', callback); 239 | } 240 | 241 | // whenever is needed 242 | document.querySelector('#link') 243 | .addEventListener('click', function (e) { 244 | // glow 245 | glow(e.currentTarget, function (event) { 246 | console.log(event); 247 | }); 248 | }); 249 | 250 | ``` 251 | The fallback is based on `setTimeout` and the returned object has a `.drop()` method able to cancel the animation end event. 252 | The duration is retrieved automatically when the fallback is used. 253 | Please note the fallback is compatible with `s` or `ms` as seconds or milliseconds and nothing else. 254 | 255 | It is possible to retrieve an animation duration through the `.getAnimationDuration(domElement, animationName)` method which returns `-1` in case of failure. 256 | 257 | 258 | ### New In Version 0.2 259 | The signature has been improved to accept a first argument representing a generic container/component prefix. 260 | ```js 261 | var compStyle = restyle('my-component-name', { 262 | 'div.large': { 263 | width: '100%' 264 | }, 265 | span: { 266 | display: 'none' 267 | } 268 | }); 269 | ``` 270 | Above code will produce a CSS similar to the following one: 271 | ```css 272 | my-component-name div.large { 273 | width: 100%; 274 | } 275 | my-component-name span { 276 | display: none; 277 | } 278 | ``` 279 | This can be very handy when you have to style [Custom Elements](https://github.com/WebReflection/document-register-element#document-register-element) or generic reusable web components. 280 | 281 | 282 | ### Signature 283 | ```javascript 284 | 285 | restyle( 286 | [component, ] // an optional string used to auto prefix all styles under a node/component 287 | Object // a JSONish object as spec'd 288 | [, prefixes] // optional prefixes 289 | // as node.js module this is by default an empty array 290 | // generating prefixes-less CSS for other pre/post processors 291 | // in browsers this is by default all vendors prefixes 292 | // without bothering much that -webkit-background does not even exist 293 | // browsers will simply ignore CSS that is meaningless 294 | 295 | [, document] // browsers only, eventually a different document from another realm 296 | ):Object; 297 | 298 | ``` 299 | 300 | ### Specifications 301 | The first `Object` parameter in `restyle` signature is spec'd as such: 302 | ``` 303 | 304 | selector any CSS selector 305 | { 306 | body: { 307 | // ... 308 | }, 309 | 'ul.dat > li:first-child': { 310 | // ... 311 | } 312 | } 313 | 314 | property a property name or a group name 315 | { 316 | div: { 317 | // properties 318 | width: 256, // will result in "256px" 319 | transform: 'rotate(360deg)', 320 | background: 'transparent url(image.png) 0 0' 321 | } 322 | } 323 | camelCase will be translated into camel-case 324 | (backgroundImage => background-image) 325 | 326 | value the property value or a group of properties 327 | if int, will be set as 'px' value 328 | 329 | group key/value properties names/values object 330 | or 331 | an Array of possible values for the property 332 | { 333 | div: { 334 | // group 335 | background: { 336 | color: 'transparent', 337 | image: 'url(image.png)', 338 | position: '0 0' 339 | } 340 | } 341 | } 342 | or 343 | { 344 | '.flexbox': { 345 | // mutiple values 346 | display: [ 347 | '-webkit-box', 348 | '-moz-box', 349 | '-ms-flexbox', 350 | '-webkit-flex', 351 | 'flex' 352 | ] 353 | } 354 | } 355 | 356 | special keyframes, media queries, 357 | anything that starts with @ 358 | { 359 | div: { 360 | // as before 361 | }, 362 | // special selectors 363 | '@keyframes spin': { 364 | // cpecialContent 365 | } 366 | } 367 | 368 | specialContent everything supported by restyle as CSS 369 | { 370 | // special selectors 371 | '@keyframes spin': { 372 | // properties => values or groups 373 | '0%': {transform: 'rotate(0deg)'}, 374 | '100%': {transform: 'rotate(360deg)'} 375 | }, 376 | '@media all and (color)': { 377 | 'body': { 378 | background: randomRainbow() 379 | } 380 | } 381 | } 382 | 383 | ``` 384 | 385 | ### Reason & Benefits 386 | Here a list of bullets to support `restyle` idea, grouped by usage. 387 | 388 | #### As DOM Runtime 389 | 390 | * all values, groups, and even keys, can be generated at runtime after features detection or states 391 | * all vendor prefixes are placed automatically, no redundant CSS to write or download 392 | * all changes are confined in a single style element that can be dropped at any time 393 | * it can be used to style custom components preserving the overall application size 394 | * it fits in less than 1KB minzipped 395 | 396 | #### As node.js module or Preprocessor 397 | 398 | * compared to Sass, Stylus, Less, and others, there's nothing new to learn: it's basically JSON that transpile to CSS 399 | * can be used upfront other preprocessors such beautifiers, cross platform transformers, etc. 400 | * CSS can be exported as generic JS modules, with the ability to include, require, and use any sort of utility able to simplify CSS creation, aggregate objects upfront for unified style, anything else you might think would be useful 401 | * it's simple, fast and straightforward 402 | 403 | #### Compatibility 404 | 405 | * `restyle` is compatible with new browsers but also old as _IE6_ . If in doubt, [check the live test](http://webreflection.github.io/restyle/test/) 406 | * every node.js is able to use `restyle` too as the Travis passing build on top says ;-) 407 | 408 | ### Examples 409 | It is possible to test them [directly in this page](http://webreflection.github.io/restyle/) but here few examples. 410 | ```javascript 411 | // this example code 412 | restyle({ 413 | 'html, body': { 414 | margin: 0, 415 | padding: 0, 416 | width: '100%', 417 | height: '100%', 418 | overflow: 'hidden', 419 | textAlign: 'center', 420 | fontFamily: 'sans-serif' 421 | }, 422 | section: { 423 | margin: 'auto', 424 | marginTop: 20 425 | } 426 | }, []); 427 | ``` 428 | 429 | It will generate a style with the following content. 430 | 431 | ``` 432 | html, body { 433 | margin: 0px; 434 | padding: 0px; 435 | width: 100%; 436 | height: 100%; 437 | overflow: hidden; 438 | text-align: center; 439 | font-family: sans-serif; 440 | } 441 | 442 | section { 443 | margin: auto; 444 | margin-top: 20px; 445 | } 446 | ``` 447 | 448 | Things become more interesting with more complex CSS and prefixed support: 449 | ```javascript 450 | restyle({ 451 | 'div > button:first-child': { 452 | transform: 'rotate(30deg)' 453 | } 454 | }, ['moz', 'webkit']); 455 | ``` 456 | 457 | will result in 458 | 459 | ```css 460 | div > button:first-child { 461 | -webkit-transform: rotate(30deg); 462 | -moz-transform: rotate(30deg); 463 | transform: rotate(30deg); 464 | } 465 | ``` 466 | 467 | while this little piece of code: 468 | 469 | ```javascript 470 | restyle({ 471 | 'body > div': { 472 | animation: { 473 | name: 'spin', 474 | duration: '4s' 475 | } 476 | }, 477 | '@keyframes spin': { 478 | from: { 479 | transform: 'rotate(0deg)' 480 | }, 481 | to: { 482 | transform: 'rotate(360deg)' 483 | } 484 | } 485 | }); 486 | ``` 487 | 488 | will produce the following 489 | 490 | ```css 491 | body > div{ 492 | -webkit-animation-name:spin; 493 | -moz-animation-name:spin; 494 | -ms-animation-name:spin; 495 | -o-animation-name:spin; 496 | animation-name:spin; 497 | -webkit-animation-duration:4s; 498 | -moz-animation-duration:4s; 499 | -ms-animation-duration:4s; 500 | -o-animation-duration:4s; 501 | animation-duration:4s; 502 | } 503 | @-webkit-keyframes spin{ 504 | from{ 505 | -webkit-transform:rotate(0deg); 506 | transform:rotate(0deg); 507 | } 508 | to{ 509 | -webkit-transform:rotate(360deg); 510 | transform:rotate(360deg); 511 | } 512 | } 513 | @-moz-keyframes spin{ 514 | from{ 515 | -moz-transform:rotate(0deg); 516 | transform:rotate(0deg); 517 | } 518 | to{ 519 | -moz-transform:rotate(360deg); 520 | transform:rotate(360deg); 521 | } 522 | } 523 | @-ms-keyframes spin{ 524 | from{ 525 | -ms-transform:rotate(0deg); 526 | transform:rotate(0deg); 527 | } 528 | to{ 529 | -ms-transform:rotate(360deg); 530 | transform:rotate(360deg); 531 | } 532 | } 533 | @-o-keyframes spin{ 534 | from{ 535 | -o-transform:rotate(0deg); 536 | transform:rotate(0deg); 537 | } 538 | to{ 539 | -o-transform:rotate(360deg); 540 | transform:rotate(360deg); 541 | } 542 | } 543 | @keyframes spin{ 544 | from{ 545 | -webkit-transform:rotate(0deg); 546 | -moz-transform:rotate(0deg); 547 | -ms-transform:rotate(0deg); 548 | -o-transform:rotate(0deg); 549 | transform:rotate(0deg); 550 | } 551 | to{ 552 | -webkit-transform:rotate(360deg); 553 | -moz-transform:rotate(360deg); 554 | -ms-transform:rotate(360deg); 555 | -o-transform:rotate(360deg); 556 | transform:rotate(360deg); 557 | } 558 | } 559 | ``` 560 | 561 | ### Special Features 562 | There are few tricks hidden in the simple `restyle` logic where `Array` values are able to combine multiple declarations at once. 563 | 564 | #### multiple values, same property 565 | The most classic example here would be `flex-box` _mess_, simplified through a variable 566 | ```javascript 567 | var flexBox = [ 568 | '-webkit-box', 569 | '-moz-box', 570 | '-ms-flexbox', 571 | '-webkit-flex', 572 | 'flex' 573 | ]; 574 | ``` 575 | reusable whenever it's needed: 576 | ```javascript 577 | restyle({ 578 | 'div.container': { 579 | display: flexBox 580 | } 581 | }); 582 | ``` 583 | resulting into the following CSS 584 | ```css 585 | div.container { 586 | display: -webkit-box; 587 | display: -moz-box; 588 | display: -ms-flexbox; 589 | display: -webkit-flex; 590 | display: flex; 591 | } 592 | ``` 593 | 594 | #### multiple styles, same selector 595 | Another example would be _mixins_ or reusable functions in order to define some grouped style and reuse this whenever is needed. 596 | ```javascript 597 | function flexbox() { 598 | return { 599 | display: [ 600 | '-webkit-box', 601 | '-moz-box', 602 | '-ms-flexbox', 603 | '-webkit-flex', 604 | 'flex' 605 | ] 606 | }; 607 | } 608 | function flex(values) { 609 | return { 610 | boxFlex: values, 611 | flex: values 612 | }; 613 | } 614 | function order(value) { 615 | return { 616 | boxOrdinalGroup: value, 617 | flexOrder: value, 618 | order: value 619 | }; 620 | } 621 | restyle({ 622 | '.wrapper': flexbox(), 623 | '.item': [ 624 | flex('1 200px'), 625 | order(2) 626 | ] 627 | }); 628 | ``` 629 | Above is a `restyle` example of [ths Sass one](http://css-tricks.com/snippets/css/a-guide-to-flexbox/) showed in CSS tricks. 630 | 631 | 632 | 633 | ### What Is NOT 634 | Just to be clear what `restyle` is not a CSS validator, beautifier, or uglifier, plus it is not responsible or capable of making everything magically works. 635 | 636 | As example, `flex-box` is not fixed, neither early or non standard implementation of any feature. 637 | However, **you can simply combine** a common class fix for flex-box and use `restyle` to add more or simply specify other properties, there are no implicit limits in what you can write through `restyle`. 638 | 639 | **You are free to fix things** indeed by your own, deciding very specific CSS accordingly with the browser if done at runtime or simply trusting other pre-processors if done on the server side with the benefit that the object will be reused in both worlds, as example: 640 | 641 | ```javascript 642 | var flexValue = '1 200px', 643 | orderValue = 2, 644 | flexBox = [ 645 | '-webkit-box', 646 | '-moz-box', 647 | '-ms-flexbox', 648 | '-webkit-flex', 649 | 'flex' 650 | ]; 651 | 652 | var flex = restyle({ 653 | '.wrapper': { 654 | display: flexBox 655 | }, 656 | '.item': { 657 | boxFlex: flexValue, 658 | flex: flexValue, 659 | boxOrdinalGroup: orderValue, 660 | flexOrder: orderValue, 661 | order: orderValue 662 | } 663 | }); 664 | ``` 665 | 666 | ### F.A.Q. 667 | 668 | * **why so many prefixes in the DOM version ?** Not influent at runtime, invisible via node, but I've hopefully [replied to this here](http://webreflection.blogspot.com/2014/02/restylejs-simplified-css-approach.html#prefixes) already ;-) 669 | * **should I serve all CSS only via `restyle` at runtime?** you can do whatever you want. You can combine normal CSS with restyle in order to add special FX only or new features where prefixes are a mess. You can use restyle only to fix things that need to be fixed for browsers that support JS. You can use only `restyle` if your app depends on JavaScript so there's no way it's going to be used or useful at all without JS enabled. You chose, don't blame the tool, it's here to help when needed ;-) 670 | * **what's the difference with absurd.js?** by the time I am writing this, `restyle` works better for WebApp development at runtime and wins in size and performance but it cannot compete against `absurd` on the server side since it does nothing that `absurd` does, only the object syntax is similar. Bear in mind I've said **similar** but **not identical**, `absurd.js` is by design not able to solve a property name from a tagName while `restyle` simply represents CSS without magic involved. 671 | * **can I use `restyle` for serving both server and client at runtime?** Yes, again, you can use `restyle` as you wish. On the server, you can use same logic you would apply on the client and maybe chose to serve that pre-processed file inside a noscript as external link, using `restyle` for all other JS centric cases or for **graceful enhancement** without compromising the layout. CSS modules can be shared, reused, the same, both pre-processed as CSS behind other pre-processors, or just with all prefixes generated at runtime for more complex scenarios. Go wild, still respect your site/app users ;-) 672 | * **didn't Netscape with [JSSS](http://en.wikipedia.org/wiki/JavaScript_Style_Sheets) ... bla bla?** probably you didn't read what `restyle` is, neither what JSSS proposal was. Please take a minute to understand again what is this about, and feel free to use JSSS if you think that's even an option. 673 | 674 | If you have any hint about some syntax that could improve `restyle` ease please let me know, thanks. 675 | -------------------------------------------------------------------------------- /VS.md: -------------------------------------------------------------------------------- 1 | # restyle VS absurd by examples 2 | Taking [absurd.js](http://krasimir.github.io/absurd/) test page, here something you might want to compare. 3 | 4 | Bear in mind I am stating the obvious all over in this page since `restyle` once again does not add any magic, it's rather a 1:1 with CSS representation. 5 | 6 | Check [specs.md](https://github.com/WebReflection/restyle/blob/master/specs.md#restyle-object-specifications) if you want to know more about `restyle` syntax specs. 7 | 8 | ### basic object 9 | 10 | #### absurd 11 | ```javascript 12 | api.add({ 13 | body: { 14 | marginTop: "20px", 15 | width: "100%" 16 | }, 17 | header: { 18 | width: "100%" 19 | } 20 | }); 21 | ``` 22 | will produce 23 | ```css 24 | body { 25 | margin-top: 20px; 26 | } 27 | body, header { 28 | width: 100%; 29 | } 30 | ``` 31 | note that `body` and `header` have been magically grouped with the same property. 32 | 33 | #### restyle 34 | ```javascript 35 | restyle({ 36 | body: { 37 | marginTop: 20 38 | }, 39 | 'body, header': { 40 | width: '100%' 41 | } 42 | }); 43 | ``` 44 | will produce exactly same CSS 45 | 46 | 47 | ### nesting 48 | 49 | #### absurd 50 | ```javascript 51 | api.add({ 52 | body: { 53 | marginTop: "20px", 54 | width: "100%", 55 | p: { 56 | color: "#BADA55" 57 | } 58 | } 59 | }); 60 | ``` 61 | will produce 62 | ```css 63 | body { 64 | margin-top: 20px; 65 | width: 100%; 66 | } 67 | body p { 68 | color: #BADA55; 69 | } 70 | ``` 71 | 72 | #### restyle 73 | ```javascript 74 | restyle({ 75 | body: { 76 | marginTop: 20, 77 | width: '100%' 78 | }, 79 | 'body p': { 80 | color: '#BADA55' 81 | } 82 | }); 83 | ``` 84 | will produce exactly same CSS 85 | 86 | 87 | ### pseudo-classes 88 | 89 | #### absurd 90 | ```javascript 91 | api.add({ 92 | body: { 93 | a: { 94 | color: "#FFF", 95 | ":hover": { 96 | textDecoration: "none" 97 | }, 98 | ":after": { 99 | display: "block", 100 | content: "---" 101 | } 102 | } 103 | } 104 | }); 105 | ``` 106 | will produce 107 | ```css 108 | body a { 109 | color: #FFF; 110 | } 111 | body a:hover { 112 | text-decoration: none; 113 | } 114 | body a:after { 115 | display: block; 116 | content: ---; 117 | } 118 | ``` 119 | 120 | #### restyle 121 | ```javascript 122 | restyle({ 123 | 'body a': { 124 | color: '#FFF' 125 | }, 126 | 'body a:hover': { 127 | textDecoration: 'none' 128 | }, 129 | 'body a:after': { 130 | display: 'block', 131 | content: '---' 132 | } 133 | }); 134 | ``` 135 | will produce exactly same CSS 136 | 137 | 138 | ### JS variables and functions 139 | 140 | #### absurd 141 | ```javascript 142 | var themeColor = "#BADA55"; 143 | var textStyles = function(size) { 144 | return { 145 | color: themeColor, 146 | fontSize: size + "px", 147 | lineHeight: size + "px" 148 | } 149 | } 150 | api.add({ 151 | body: { 152 | color: function() { 153 | return "#000" 154 | }, 155 | p: textStyles(16), 156 | h1: [ 157 | textStyles(50), 158 | { lineHeight: "60px" } 159 | ] 160 | } 161 | }); 162 | ``` 163 | will produce 164 | ```css 165 | body { 166 | color: #000; 167 | } 168 | body p, body h1 { 169 | color: #BADA55; 170 | } 171 | body p { 172 | font-size: 16px; 173 | line-height: 16px; 174 | } 175 | body h1 { 176 | font-size: 50px; 177 | line-height: 60px; 178 | } 179 | ``` 180 | 181 | #### restyle 182 | ```javascript 183 | var themeColor = '#BADA55'; 184 | var textStyles = function(fs, lh) { 185 | return { 186 | fontSize: fs, 187 | lineHeight: lh || fs 188 | } 189 | } 190 | restyle({ 191 | body: { 192 | color: '#000' 193 | }, 194 | 'body p, body h1': { 195 | color: themeColor 196 | }, 197 | 'body p': textStyles(16), 198 | 'body h1': textStyles(50, 60) 199 | }); 200 | ``` 201 | will produce exactly same CSS. 202 | 203 | 204 | ### media queries 205 | 206 | #### absurd 207 | ```javascript 208 | api.add({ 209 | body: { 210 | ".wrapper": { 211 | width: "940px", 212 | "@media all (min-width: 550px)": { 213 | width: "400px" 214 | } 215 | }, 216 | p: { 217 | "@media all (min-width: 550px)": { 218 | fontSize: "20px" 219 | } 220 | } 221 | } 222 | }); 223 | ``` 224 | will produce 225 | ```css 226 | body .wrapper { 227 | width: 940px; 228 | } 229 | @media all (min-width: 550px) { 230 | body .wrapper { 231 | width: 400px; 232 | } 233 | body p { 234 | font-size: 20px; 235 | } 236 | } 237 | ``` 238 | 239 | #### restyle 240 | ```javascript 241 | restyle({ 242 | 'body .wrapper': { 243 | width: 940, 244 | }, 245 | '@media all (min-width: 550px)': { 246 | 'body .wrapper': { 247 | width: 400 248 | }, 249 | 'body p': { 250 | fontSize: 20 251 | } 252 | } 253 | }); 254 | ``` 255 | will produce exactly same CSS. 256 | 257 | 258 | ### mixins 259 | 260 | In this case `restyle` has a very simple implementation, values as `Array` 261 | ```javascript 262 | function red() { 263 | return { 264 | color: '#FFF', 265 | background: '#A30' 266 | }; 267 | } 268 | function bordered() { 269 | return { 270 | border: '1px solid black' 271 | }; 272 | } 273 | restyle({ 274 | '.red-bordered': [ 275 | red(), 276 | bordered() 277 | ] 278 | }); 279 | ``` 280 | Due simplicity of its parser, the generated layout will look like the following: 281 | ```css 282 | .red-bordered { 283 | color: #FFF; 284 | background: #A30; 285 | } 286 | .red-bordered { 287 | border: 1px solid black; 288 | } 289 | ``` 290 | Once again, `restyle` goal is to "just work" in the smallest, simplest, yet efficient way to think CSS via JS objects. 291 | 292 | 293 | ### Conclusions 294 | Other cases are probably not possible in `restyle` as well as few `restyle` cases are not possible in `absurd`. 295 | 296 | In few words, this comparison is to show that there is really nothing behind `restyle` about components, storage, HTML, etc, etc, at least now you hopefully know what is `restyle` about and how you should use it. -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "restyle", 3 | "version": "0.4.2", 4 | "homepage": "https://github.com/WebReflection/restyle", 5 | "authors": [ 6 | "Andrea Giammarchi " 7 | ], 8 | "description": "a JS to CSS transformer", 9 | "main": "./build/restyle.max.amd.js", 10 | "keywords": [ 11 | "CSS", 12 | "JS", 13 | "HTML5" 14 | ], 15 | "license": "MIT", 16 | "ignore": [ 17 | "**/.*", 18 | "node_modules", 19 | "bower_components", 20 | "src", 21 | "template", 22 | "test", 23 | "tests" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /build/restyle.amd.js: -------------------------------------------------------------------------------- 1 | /*! (C) Andrea Giammarchi Mit Style License */ 2 | define(function(e){"use strict";function f(e,t,n,r,i){this.component=e,this.node=t,this.css=n,this.prefixes=r,this.doc=i}function l(e){e instanceof f||(e=a(this.component,e,this.prefixes,this.doc)),this.remove(),f.call(this,e.component,e.node,e.css,e.prefixes,e.doc)}function c(e,t,n){return t+"-"+n.toLowerCase()}function h(e){if(o(e))for(var t,n,r=[],i=e,e={},s=0;s 2 | 3 | 4 | wru test 5 | 6 | 7 | 8 | 9 | 13 | 54 | 55 | 56 | 88 | 89 | 90 |
91 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.7.0", 3 | "license": "MIT", 4 | "name": "restyle", 5 | "description": "a JS to CSS transformer", 6 | "homepage": "https://github.com/WebReflection/restyle", 7 | "keywords": [ 8 | "CSS", 9 | "JS", 10 | "CustomElement", 11 | "transformer" 12 | ], 13 | "author": { 14 | "name": "Andrea Giammarchi", 15 | "web": "http://webreflection.blogspot.com/" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git://github.com/WebReflection/restyle.git" 20 | }, 21 | "main": "./build/restyle.node.js", 22 | "scripts": { 23 | "test": "node test/.test.js", 24 | "web": "tiny-cdn run" 25 | }, 26 | "devDependencies": { 27 | "jshint": "^2.9.4", 28 | "tiny-cdn": "^0.7.0", 29 | "uglify-js": "^1.3.5", 30 | "wru": "^0.3.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /specs.md: -------------------------------------------------------------------------------- 1 | # restyle object specifications 2 | This page is dedicated to explain with examples how `restyle` objects are parsed and what these will produce as CSS. 3 | 4 | 5 | ### root object 6 | ```javascript 7 | restyle({ 8 | selector: object 9 | }); 10 | ``` 11 | A root object might contain one or more selectors as [property names](http://www.ecma-international.org/ecma-262/5.1/#sec-7.6) as it would be for any generic JavaScript object. 12 | `{body:{}}` as well as `{'section#id > ul.class a:first-child':{}}` are all valid selector, and it's possible to combine them within the property name itself, literally as if it was a CSS selector. 13 | ```javascript 14 | restyle({ 15 | 'html, body'; { 16 | padding: 0, 17 | margin: 0, 18 | width: '100%', 19 | height: '100%' 20 | }, 21 | section: { 22 | textAlign: 'center' 23 | } 24 | }); 25 | ``` 26 | Above example will produce something like the following CSS: 27 | ```css 28 | html, body { 29 | padding: 0px; 30 | margin: 0px; 31 | width: 100%; 32 | height: 100%; 33 | } 34 | section { 35 | text-align: center; 36 | } 37 | ``` 38 | 39 | #### @media, @keyframes and special selectors 40 | If the root object has a property which name starts with `@`, its value will be handled as the root of that media or keyframe, for the only exception of `@page` and `@font-face` where values will be handled as regular. 41 | ```javascript 42 | restyle({ 43 | 'header:before': { 44 | height: 80, 45 | content: '"hi, this is header"' 46 | }, 47 | '@media all and (max-width: 320px)': { 48 | 'header:before': { 49 | display: 'none' 50 | }, 51 | body: { 52 | padding: 0 53 | } 54 | } 55 | }); 56 | ``` 57 | The resulting CSS will look like the following: 58 | ```css 59 | header:before { 60 | height: 80px; 61 | content: "hi, this is header"; 62 | } 63 | @media all and (max-width: 320px) { 64 | header:before { 65 | display: none; 66 | }, 67 | body { 68 | padding: 0px; 69 | } 70 | } 71 | ``` 72 | `@keyfarmes` will behave similarly so that the following: 73 | restyle({ 74 | '@keyframes spin': { 75 | from: {transform: 'rotate(0deg)'}, 76 | to: {transform: 'rotate(360deg)'} 77 | } 78 | }); 79 | will produce the expected CSS as it is for `@media`. 80 | 81 | 82 | ### selector properties 83 | Each selector will address a _key/value pairs_ object. 84 | ```javascript 85 | selector: { 86 | key: value 87 | } 88 | ``` 89 | In this case, _camelCase_ keys will be transformed into _camel-case_ one so that `fontSize` will produce the CSS property name `font-size` and so on with `lineHeight`, `textDecoration` and all sort of camelCase name you can think about. 90 | 91 | The _value_ can be either a `string`, which will be used as it is, a `number`, which will be suffixed as pixel value, an `Object`, which will be used for sub-values of a group, or an `Array`, which will be used to repeat the property name with each entry of this Array. 92 | 93 | Following details about each possible value type. 94 | 95 | #### value as typeof(string) 96 | This is straightforward, it will simply produce a property with that value. 97 | ```javascript 98 | restyle({ 99 | div: { 100 | background: 'transparent url(image.png) top center no-repeat' 101 | } 102 | }); 103 | ``` 104 | The outcome will be as simple as the following one: 105 | ```css 106 | div { 107 | background: transparent url(image.png) top center no-repeat; 108 | } 109 | ``` 110 | Bear in mind that right now there is no logic involved for any special property so that even `content` should be properly escaped. 111 | ```javascript 112 | restyle({ 113 | '.icon:before': { 114 | content: '"\\f123"' 115 | } 116 | }); 117 | ``` 118 | Producing `.icon:before{content:"\f123";}` 119 | 120 | #### value as typeof(number) 121 | Simple to remember, numbers are pixels ... not rounded pixels, not normalized pixels, just exact same value plus the `px` suffix. 122 | ```javascript 123 | restyle({ 124 | div: { 125 | width: 320 126 | } 127 | }); 128 | ``` 129 | will produce `div{width:320px;}`. 130 | 131 | This is particularly handy when used at runtime, but it could simplify some computation via node module too. 132 | 133 | #### value as Object 134 | A classic example where an object comes handy, is the `background: transparent url(image.png) top center no-repeat;` one, where many `background` properties are declared at once. 135 | 136 | These kind of declarations are usually as hard to remember as multiple arguments invoking a function, something _named arguments_ elegantly solved in many programming languages, hence the need for a nested object. 137 | ```javascript 138 | restyle({ 139 | div: { 140 | background: { 141 | color: 'transparent', 142 | image: 'url(image.png)', 143 | repeat: 'no-repeat', 144 | position: 'top center' 145 | } 146 | } 147 | }); 148 | ``` 149 | The CSS output will mirror as such: 150 | ```css 151 | div { 152 | background-color: transparent; 153 | background-image: url(image.png); 154 | background-repeat: no-repeat; 155 | background-position: top center; 156 | } 157 | ``` 158 | Remember, `restyle` module aim is not to apply magic, rather to be a robust, simple, and fast parser. 159 | We can always use pre-processor optimizers _after_ a CSS has been produced via `restyle`. 160 | 161 | Last, but not least, value objects can contain value objects, i.e. 162 | ```javascript 163 | restyle({ 164 | div: { 165 | border: { 166 | top: { 167 | color: '#000', 168 | style: 'solid', 169 | width: 1 170 | }, 171 | bottom: { 172 | color: 'red', 173 | style: 'dashed', 174 | width: 4 175 | } 176 | } 177 | } 178 | }); 179 | ``` 180 | Producing the following CSS 181 | ```css 182 | div { 183 | border-top-color: #000; 184 | border-top-style: solid; 185 | border-top-width: 1px; 186 | border-bottom-color: red; 187 | border-bottom-style: dashed; 188 | border-bottom-width: 4px; 189 | } 190 | ``` 191 | I hope you agree with me value objects can reduce CSS typing and errors quite a lot ;-) 192 | 193 | #### value as Array 194 | Since in JavaScript we cannot write twice the same property name without overwriting the previous one or causing errors, `restyle` accepts `Array` values whenever is meant to repeat the property name with each value, i.e. 195 | ```javascript 196 | restyle({ 197 | '.flexbox': { 198 | display: [ 199 | '-webkit-box', 200 | '-moz-box', 201 | '-ms-flexbox', 202 | '-webkit-flex', 203 | 'flex' 204 | ] 205 | } 206 | }); 207 | ``` 208 | producing 209 | ```css 210 | .flexbox { 211 | display: -webkit-box; 212 | display: -moz-box; 213 | display: -ms-flexbox; 214 | display: -webkit-flex; 215 | display: flex; 216 | } 217 | ``` 218 | 219 | Another classic example is the `@font-face` one, where the following CSS: 220 | ```css 221 | @font-face { 222 | font-family: 'restyled'; 223 | font-weight: normal; 224 | font-style: normal; 225 | src:url('/fonts/restyled.eot'); 226 | src:url('/fonts/restyled.eot?#iefix') format('embedded-opentype'), 227 | url('/fonts/restyled.svg#restyled') format('svg'), 228 | url('/fonts/restyled.woff') format('woff'), 229 | url('/fonts/restyled.ttf') format('truetype'); 230 | } 231 | ``` 232 | could be generated "_as is_" through: 233 | ```javascript 234 | restyle({ 235 | '@font-face': { 236 | font: { 237 | family: 'restyled', 238 | weight: 'normal', 239 | style: 'normal' 240 | }, 241 | src: [ 242 | font('restyled.eot'), 243 | [ // note: this is **inline** as second value 244 | font('restyled.eot?#iefix', 'embedded-opentype'), 245 | font('restyled.svg#restyled', 'svg'), 246 | font('restyled.woff', 'woff'), 247 | font('restyled.ttf', 'truetype') 248 | ] 249 | ] 250 | } 251 | }); 252 | function font(src, type) { 253 | return "url('" + src + "')" + ( 254 | type ? " format('" + type + "')" : "" 255 | ); 256 | } 257 | ``` 258 | -------------------------------------------------------------------------------- /src/properties-a.js: -------------------------------------------------------------------------------- 1 | restyle.properties = (function(){ 2 | -------------------------------------------------------------------------------- /src/properties-z.js: -------------------------------------------------------------------------------- 1 | 2 | return properties; 3 | 4 | }()); -------------------------------------------------------------------------------- /src/properties.js: -------------------------------------------------------------------------------- 1 | 2 | var properties = Object.create(null); 3 | [].concat( 4 | 5 | // if you find something missing please let me know, thanks! 6 | 7 | // transitions name 8 | [ 9 | 'linear', 10 | 'ease', 11 | 'ease-in', 12 | 'ease-out', 13 | 'ease-in-out', 14 | 'initial' 15 | ], 16 | // generic font family 17 | [ 18 | 'serif', 19 | 'sans-serif', 20 | 'cursive', 21 | 'fantasy', 22 | 'monospace' 23 | ], 24 | // basic colors 25 | [ 26 | 'black', 27 | 'silver', 28 | 'gray', 29 | 'white', 30 | 'maroon', 31 | 'red', 32 | 'purple', 33 | 'fuchsia', 34 | 'green', 35 | 'lime', 36 | 'olive', 37 | 'yellow', 38 | 'navy', 39 | 'blue', 40 | 'teal', 41 | 'aqua' 42 | ], 43 | // extended colors 44 | [ 45 | 'aliceblue', 46 | 'antiquewhite', 47 | 'aqua', 48 | 'aquamarine', 49 | 'azure', 50 | 'beige', 51 | 'bisque', 52 | 'black', 53 | 'blanchedalmond', 54 | 'blue', 55 | 'blueviolet', 56 | 'brown', 57 | 'burlywood', 58 | 'cadetblue', 59 | 'chartreuse', 60 | 'chocolate', 61 | 'coral', 62 | 'cornflowerblue', 63 | 'cornsilk', 64 | 'crimson', 65 | 'cyan', 66 | 'darkblue', 67 | 'darkcyan', 68 | 'darkgoldenrod', 69 | 'darkgray', 70 | 'darkgreen', 71 | 'darkgrey', 72 | 'darkkhaki', 73 | 'darkmagenta', 74 | 'darkolivegreen', 75 | 'darkorange', 76 | 'darkorchid', 77 | 'darkred', 78 | 'darksalmon', 79 | 'darkseagreen', 80 | 'darkslateblue', 81 | 'darkslategray', 82 | 'darkslategrey', 83 | 'darkturquoise', 84 | 'darkviolet', 85 | 'deeppink', 86 | 'deepskyblue', 87 | 'dimgray', 88 | 'dimgrey', 89 | 'dodgerblue', 90 | 'firebrick', 91 | 'floralwhite', 92 | 'forestgreen', 93 | 'fuchsia', 94 | 'gainsboro', 95 | 'ghostwhite', 96 | 'gold', 97 | 'goldenrod', 98 | 'gray', 99 | 'green', 100 | 'greenyellow', 101 | 'grey', 102 | 'honeydew', 103 | 'hotpink', 104 | 'indianred', 105 | 'indigo', 106 | 'ivory', 107 | 'khaki', 108 | 'lavender', 109 | 'lavenderblush', 110 | 'lawngreen', 111 | 'lemonchiffon', 112 | 'lightblue', 113 | 'lightcoral', 114 | 'lightcyan', 115 | 'lightgoldenrodyellow', 116 | 'lightgray', 117 | 'lightgreen', 118 | 'lightgrey', 119 | 'lightpink', 120 | 'lightsalmon', 121 | 'lightseagreen', 122 | 'lightskyblue', 123 | 'lightslategray', 124 | 'lightslategrey', 125 | 'lightsteelblue', 126 | 'lightyellow', 127 | 'lime', 128 | 'limegreen', 129 | 'linen', 130 | 'magenta', 131 | 'maroon', 132 | 'mediumaquamarine', 133 | 'mediumblue', 134 | 'mediumorchid', 135 | 'mediumpurple', 136 | 'mediumseagreen', 137 | 'mediumslateblue', 138 | 'mediumspringgreen', 139 | 'mediumturquoise', 140 | 'mediumvioletred', 141 | 'midnightblue', 142 | 'mintcream', 143 | 'mistyrose', 144 | 'moccasin', 145 | 'navajowhite', 146 | 'navy', 147 | 'oldlace', 148 | 'olive', 149 | 'olivedrab', 150 | 'orange', 151 | 'orangered', 152 | 'orchid', 153 | 'palegoldenrod', 154 | 'palegreen', 155 | 'paleturquoise', 156 | 'palevioletred', 157 | 'papayawhip', 158 | 'peachpuff', 159 | 'peru', 160 | 'pink', 161 | 'plum', 162 | 'powderblue', 163 | 'purple', 164 | 'red', 165 | 'rosybrown', 166 | 'royalblue', 167 | 'saddlebrown', 168 | 'salmon', 169 | 'sandybrown', 170 | 'seagreen', 171 | 'seashell', 172 | 'sienna', 173 | 'silver', 174 | 'skyblue', 175 | 'slateblue', 176 | 'slategray', 177 | 'slategrey', 178 | 'snow', 179 | 'springgreen', 180 | 'steelblue', 181 | 'tan', 182 | 'teal', 183 | 'thistle', 184 | 'tomato', 185 | 'turquoise', 186 | 'violet', 187 | 'wheat', 188 | 'white', 189 | 'whitesmoke', 190 | 'yellow', 191 | 'yellowgreen' 192 | ], 193 | // all other properties 194 | [ 195 | 'above', 196 | 'absolute', 197 | 'always', 198 | 'armenian', 199 | 'auto', 200 | 'avoid', 201 | 'baseline', 202 | 'below', 203 | 'bidi-override', 204 | 'block', 205 | 'bold', 206 | 'bolder', 207 | 'border-box', 208 | 'bottom', 209 | 'break-word', 210 | 'capitalize', 211 | 'center', 212 | 'center-left', 213 | 'center-right', 214 | 'circle', 215 | 'close-quote', 216 | 'code', 217 | 'collapse', 218 | 'content-box', 219 | 'continuous', 220 | 'cue-after', 221 | 'cue-before', 222 | 'decimal', 223 | 'decimal-leading-zero', 224 | 'digits', 225 | 'disc', 226 | 'embed', 227 | 'far-left', 228 | 'far-right', 229 | 'fast', 230 | 'faster', 231 | 'fixed', 232 | 'georgian', 233 | 'hidden', 234 | 'hide', 235 | 'high', 236 | 'higher', 237 | 'inherit', 238 | 'inline', 239 | 'inline-block', 240 | 'inline-table', 241 | 'invert', 242 | 'italic', 243 | 'justify', 244 | 'left', 245 | 'left-side', 246 | 'leftwards', 247 | 'level', 248 | 'lighter', 249 | 'list-item', 250 | 'loud', 251 | 'low', 252 | 'lower', 253 | 'lower-alpha', 254 | 'lower-greek', 255 | 'lower-latin', 256 | 'lower-roman', 257 | 'lowercase', 258 | 'medium', 259 | 'middle', 260 | 'no-close-quote', 261 | 'no-open-quote', 262 | 'no-repeat', 263 | 'none', 264 | 'normal', 265 | 'nowrap', 266 | 'oblique', 267 | 'once', 268 | 'open-quote', 269 | 'pre', 270 | 'pre-line', 271 | 'pre-wrap', 272 | 'relative', 273 | 'repeat', 274 | 'repeat-x', 275 | 'repeat-y', 276 | 'right', 277 | 'right-side', 278 | 'rightwards', 279 | 'scroll', 280 | 'show', 281 | 'silent', 282 | 'slow', 283 | 'slower', 284 | 'small-caps', 285 | 'soft', 286 | 'spell-out', 287 | 'square', 288 | 'sub', 289 | 'super', 290 | 'table', 291 | 'table-caption', 292 | 'table-cell', 293 | 'table-column', 294 | 'table-column-group', 295 | 'table-footer-group', 296 | 'table-header-group', 297 | 'table-row', 298 | 'table-row-group', 299 | 'text-bottom', 300 | 'text-top', 301 | 'top', 302 | 'transparent', 303 | 'upper-alpha', 304 | 'upper-latin', 305 | 'upper-roman', 306 | 'uppercase', 307 | 'visible', 308 | 'x-fast', 309 | 'x-high', 310 | 'x-loud', 311 | 'x-low', 312 | 'x-slow', 313 | 'x-soft' 314 | ]).forEach(function(name){ 315 | properties[utils.$camel(name)] = name; 316 | }); 317 | 318 | Object.keys(utils).forEach(function(method){ 319 | if (method[0] !== '$') { 320 | properties[method] = utils[method]; 321 | } 322 | }); 323 | 324 | -------------------------------------------------------------------------------- /src/proxy-a.js: -------------------------------------------------------------------------------- 1 | restyle.proxy = (function(){ 2 | -------------------------------------------------------------------------------- /src/proxy-z.js: -------------------------------------------------------------------------------- 1 | 2 | return new Proxy( 3 | Object.create(null), 4 | proxyHandler 5 | ); 6 | 7 | }()); -------------------------------------------------------------------------------- /src/proxyHandler.js: -------------------------------------------------------------------------------- 1 | 2 | var proxyHandler = { 3 | has: function (target, name) { 4 | return name[0] === '$' || ( 5 | utils.$global.hasOwnProperty(name) ? 6 | utils.$clash.test(name) : 7 | true 8 | ); 9 | }, 10 | set: function (target, name, value, receiver) { 11 | target[name] = value; 12 | }, 13 | get: function (target, name, receiver) { 14 | return name[0] === '$' ? 15 | target[name] || utils.$global[name] : ( 16 | utils.hasOwnProperty(name) ? 17 | utils[name] : 18 | utils.$unCamel(name) 19 | ); 20 | } 21 | }; 22 | 23 | /* i.e. 24 | with (restyle.proxy) { 25 | 26 | // some variable declaration 27 | $color = '#ABC'; 28 | 29 | // style to return 30 | ({ 31 | body: { 32 | color: $color 33 | } 34 | }); 35 | } 36 | */ 37 | 38 | -------------------------------------------------------------------------------- /src/restyle.js: -------------------------------------------------------------------------------- 1 | (function (O) { 2 | 'use strict'; 3 | 4 | var 5 | toString = O.toString, 6 | has = O.hasOwnProperty, 7 | camelFind = /([a-z])([A-Z])/g, 8 | ignoreSpecial = /^@(?:page|font-face)/, 9 | isMedia = /^@(?:media)/, 10 | isArray = Array.isArray || function (arr) { 11 | return toString.call(arr) === '[object Array]'; 12 | }, 13 | empty = [], 14 | restyle; 15 | 16 | function ReStyle(component, node, css, prefixes, doc) { 17 | this.component = component; 18 | this.node = node; 19 | this.css = css; 20 | this.prefixes = prefixes; 21 | this.doc = doc; 22 | } 23 | 24 | function replace(substitute) { 25 | if (!(substitute instanceof ReStyle)) { 26 | substitute = restyle( 27 | this.component, substitute, this.prefixes, this.doc 28 | ); 29 | } 30 | this.remove(); 31 | ReStyle.call( 32 | this, 33 | substitute.component, 34 | substitute.node, 35 | substitute.css, 36 | substitute.prefixes, 37 | substitute.doc 38 | ); 39 | } 40 | 41 | ReStyle.prototype = { 42 | overwrite: replace, 43 | replace: replace, 44 | set: replace, 45 | remove: function () { 46 | var node = this.node, 47 | parentNode = node.parentNode; 48 | if (parentNode) { 49 | parentNode.removeChild(node); 50 | } 51 | }, 52 | valueOf: function () { 53 | return this.css; 54 | } 55 | }; 56 | 57 | function camelReplace(m, $1, $2) { 58 | return $1 + '-' + $2.toLowerCase(); 59 | } 60 | 61 | function convertArray(obj) { 62 | if (isArray(obj)) { 63 | for (var 64 | j, curr, 65 | prev = [], 66 | arr = obj, 67 | obj = {}, 68 | i = 0; i < arr.length; i++ 69 | ) { 70 | curr = arr[i]; 71 | if (typeof curr === 'string') { 72 | prev.push(curr); 73 | } else { 74 | for (j = 0; j < prev.length; j++) { 75 | obj[prev[j]] = curr; 76 | } 77 | prev = []; 78 | } 79 | } 80 | } 81 | return obj; 82 | } 83 | 84 | function create(key, value, prefixes) { 85 | var 86 | css = [], 87 | pixels = typeof value === 'number' ? 'px' : '', 88 | k = key.replace(camelFind, camelReplace), 89 | i; 90 | for (i = 0; i < prefixes.length; i++) { 91 | css.push('-', prefixes[i], '-', k, ':', value, pixels, ';'); 92 | } 93 | css.push(k, ':', value, pixels, ';'); 94 | return css.join(''); 95 | } 96 | 97 | function property(previous, key) { 98 | return previous.length ? previous + '-' + key : key; 99 | } 100 | 101 | function generate(css, previous, obj, prefixes) { 102 | var key, value, i; 103 | for (key in obj) { 104 | if (has.call(obj, key)) { 105 | if (typeof obj[key] === 'object') { 106 | if (isArray(obj[key])) { 107 | value = obj[key]; 108 | for (i = 0; i < value.length; i++) { 109 | css.push( 110 | create(property(previous, key), value[i], prefixes) 111 | ); 112 | } 113 | } else { 114 | generate( 115 | css, 116 | property(previous, key), 117 | obj[key], 118 | prefixes 119 | ); 120 | } 121 | } else { 122 | css.push( 123 | create(property(previous, key), obj[key], prefixes) 124 | ); 125 | } 126 | } 127 | } 128 | return css.join(''); 129 | } 130 | 131 | function parse(component, obj, prefixes) { 132 | var 133 | css = [], 134 | vComponent = component.slice(0, -1), 135 | amp, at, cmp, special, k, v, 136 | same, key, vKey, value, i, j; 137 | for (key in obj) { 138 | if (has.call(obj, key)) { 139 | vKey = ''; 140 | j = key.length; 141 | amp = j ? (-1 < key.indexOf('&')) : false; 142 | if (j) { 143 | at = key.charAt(0) === '@'; 144 | if (amp) vKey = key.replace(/&/g, vComponent); 145 | } 146 | else { 147 | key = vComponent; 148 | at = false; 149 | } 150 | same = at || !component.indexOf((vKey || key) + ' '); 151 | cmp = at && isMedia.test(key) ? component : ''; 152 | special = at && !ignoreSpecial.test(key); 153 | k = special ? key.slice(1) : key; 154 | value = empty.concat(obj[j ? key : '']); 155 | for (i = 0; i < value.length; i++) { 156 | v = value[i]; 157 | if (special) { 158 | j = prefixes.length; 159 | while (j--) { 160 | css.push('@-', prefixes[j], '-', k, '{', 161 | parse(cmp, v, [prefixes[j]]), 162 | '}'); 163 | } 164 | css.push((vKey || key), '{', parse(cmp, v, prefixes), '}'); 165 | } else { 166 | css.push( 167 | same ? (vKey || key) : (vKey || (component + key)), 168 | '{', generate([], '', v, prefixes), '}' 169 | ); 170 | } 171 | } 172 | } 173 | } 174 | return css.join(''); 175 | } 176 | 177 | // hack to avoid JSLint shenanigans 178 | if ({undefined: true}[typeof document]) { 179 | // in node, by default, no prefixes are used 180 | restyle = function (component, obj, prefixes) { 181 | if (typeof component === 'object') { 182 | prefixes = obj; 183 | obj = component; 184 | component = ''; 185 | } else { 186 | component += ' '; 187 | } 188 | return parse(component, convertArray(obj), prefixes || empty); 189 | }; 190 | // useful for different style of require 191 | restyle.restyle = restyle; 192 | } else { 193 | restyle = function (component, obj, prefixes, doc) { 194 | if (typeof component === 'object') { 195 | doc = prefixes; 196 | prefixes = obj; 197 | obj = component; 198 | c = (component = ''); 199 | } else { 200 | c = component + ' '; 201 | } 202 | var c, d = doc || (doc = document), 203 | css = parse(c, convertArray(obj), prefixes || (prefixes = restyle.prefixes)), 204 | head = d.head || 205 | d.getElementsByTagName('head')[0] || 206 | d.documentElement, 207 | node = head.insertBefore( 208 | d.createElement('style'), 209 | head.lastChild 210 | ); 211 | node.type = 'text/css'; 212 | // it should have been 213 | // if ('styleSheet' in node) {} 214 | // but JSLint bothers in that way 215 | if (node.styleSheet) { 216 | node.styleSheet.cssText = css; 217 | } else { 218 | node.appendChild(d.createTextNode(css)); 219 | } 220 | return new ReStyle(component, node, css, prefixes, doc); 221 | }; 222 | } 223 | 224 | // bringing animation utility in window-aware world only 225 | if (!{undefined: true}[typeof window]) { 226 | restyle.animate = (function (g) { 227 | 228 | var 229 | rAF = window.requestAnimationFrame || 230 | window.webkitRequestAnimationFrame || 231 | window.mozRequestAnimationFrame || 232 | window.msRequestAnimationFrame || 233 | function (fn) { setTimeout(fn, 10); }, 234 | liveStyles = {}, 235 | uid = 'restyle-'.concat(Math.random() * (+new Date()), '-'), 236 | uidIndex = 0, 237 | animationType, 238 | transitionType 239 | ; 240 | 241 | switch (true) { 242 | case !!g.AnimationEvent: 243 | animationType = 'animationend'; 244 | break; 245 | case !!g.WebKitAnimationEvent: 246 | animationType = 'webkitAnimationEnd'; 247 | break; 248 | case !!g.MSAnimationEvent: 249 | animationType = 'MSAnimationEnd'; 250 | break; 251 | case !!g.OAnimationEvent: 252 | animationType = 'oanimationend'; 253 | break; 254 | } 255 | 256 | switch (true) { 257 | case !!g.TransitionEvent: 258 | transitionType = 'transitionend'; 259 | break; 260 | case !!g.WebKitTransitionEvent: 261 | transitionType = 'webkitTransitionEnd'; 262 | break; 263 | case !!g.MSTransitionEvent: 264 | transitionType = 'MSTransitionEnd'; 265 | break; 266 | case !!g.OTransitionEvent: 267 | transitionType = 'oTransitionEnd'; 268 | break; 269 | } 270 | 271 | restyle.transition = function (el, info, callback) { 272 | var 273 | transition = info.transition || 'all .3s ease-out', 274 | id = el.getAttribute('id'), 275 | to = [].concat(info.to), 276 | from = update({}, info.from), 277 | noID = !id, 278 | style = {}, 279 | currentID, 280 | result, 281 | live, 282 | t 283 | ; 284 | function drop() { 285 | if (transitionType) { 286 | el.removeEventListener(transitionType, onTransitionEnd, false); 287 | } else { 288 | clearTimeout(t); 289 | t = 0; 290 | } 291 | } 292 | function next() { 293 | style[currentID] = (live.last = update(from, to.shift())); 294 | live.css.replace(style); 295 | if (transitionType) { 296 | el.addEventListener(transitionType, onTransitionEnd, false); 297 | } else { 298 | t = setTimeout(onTransitionEnd, 10); 299 | } 300 | } 301 | function onTransitionEnd(e) { 302 | drop(); 303 | if (to.length) { 304 | rAF(next); 305 | } else { 306 | if (!e) e = new CustomEvent('transitionend', {detail: result}); 307 | else e.detail = result; 308 | if (callback) callback.call(el, e); 309 | } 310 | } 311 | function update(target, source) { 312 | for (var k in source) target[k] = source[k]; 313 | return target; 314 | } 315 | if (noID) el.setAttribute('id', id = (uid + uidIndex++).replace('.','-')); 316 | currentID = '#' + id; 317 | if (liveStyles.hasOwnProperty(id)) { 318 | live = liveStyles[id]; 319 | from = (live.last = update(live.last, from)); 320 | style[currentID] = from; 321 | live.transition.remove(); 322 | live.css.replace(style); 323 | } else { 324 | live = liveStyles[id] = { 325 | last: (style[currentID] = from), 326 | css: restyle(style) 327 | }; 328 | } 329 | rAF(function() { 330 | style[currentID] = {transition: transition}; 331 | live.transition = restyle(style); 332 | rAF(next); 333 | }); 334 | return (result = { 335 | clean: function () { 336 | if (noID) el.removeAttribute('id'); 337 | drop(); 338 | live.transition.remove(); 339 | live.css.remove(); 340 | delete liveStyles[id]; 341 | }, 342 | drop: drop 343 | }); 344 | }; 345 | 346 | ReStyle.prototype.getAnimationDuration = function (el, name) { 347 | for (var 348 | chunk, duration, 349 | classes = el.className.split(/\s+/), 350 | i = classes.length; i--; 351 | ) { 352 | chunk = classes[i]; 353 | if ( 354 | chunk.length && 355 | (new RegExp('\\.' + chunk + '(?:|\\{|\\,)([^}]+?)\\}')).test(this.css) 356 | ) { 357 | chunk = RegExp.$1; 358 | if ( 359 | (new RegExp( 360 | 'animation-name:' + 361 | name + 362 | ';.*?animation-duration:([^;]+?);' 363 | )).test(chunk) || 364 | (new RegExp( 365 | 'animation:\\s*' + name + '\\s+([^\\s]+?);' 366 | )).test(chunk) 367 | ) { 368 | chunk = RegExp.$1; 369 | duration = parseFloat(chunk); 370 | if (duration) { 371 | return duration * (/[^m]s$/.test(chunk) ? 1000 : 1); 372 | } 373 | } 374 | } 375 | } 376 | return -1; 377 | }; 378 | 379 | ReStyle.prototype.getTransitionDuration = function (el) { 380 | var 381 | cs = getComputedStyle(el), 382 | duration = cs.getPropertyValue('transition-duration') || 383 | /\s(\d+(?:ms|s))/.test( 384 | cs.getPropertyValue('transition') 385 | ) && RegExp.$1 386 | ; 387 | return parseFloat(duration) * (/[^m]s$/.test(duration) ? 1000 : 1); 388 | }; 389 | 390 | ReStyle.prototype.transit = transitionType ? 391 | function (el, callback) { 392 | function onTransitionEnd(e) { 393 | drop(); 394 | callback.call(el, e); 395 | } 396 | function drop() { 397 | el.removeEventListener(transitionType, onTransitionEnd, false); 398 | } 399 | el.addEventListener(transitionType, onTransitionEnd, false); 400 | return {drop: drop}; 401 | } : 402 | function (el, callback) { 403 | var i = setTimeout(callback, this.getTransitionDuration(el)); 404 | return {drop: function () { 405 | clearTimeout(i); 406 | }}; 407 | } 408 | ; 409 | 410 | ReStyle.prototype.animate = animationType ? 411 | function animate(el, name, callback) { 412 | function onAnimationEnd(e) { 413 | if (e.animationName === name) { 414 | drop(); 415 | callback.call(el, e); 416 | } 417 | } 418 | function drop() { 419 | el.removeEventListener(animationType, onAnimationEnd, false); 420 | } 421 | el.addEventListener(animationType, onAnimationEnd, false); 422 | return {drop: drop}; 423 | } : 424 | function animate(el, name, callback) { 425 | var i, drop, duration = this.getAnimationDuration(el, name); 426 | if (duration < 0) { 427 | drop = O; 428 | } else { 429 | i = setTimeout( 430 | function () { 431 | callback.call(el, { 432 | type: 'animationend', 433 | animationName: name, 434 | currentTarget: el, 435 | target: el, 436 | stopImmediatePropagation: O, 437 | stopPropagation: O, 438 | preventDefault: O 439 | }); 440 | }, 441 | duration 442 | ); 443 | drop = function () { 444 | clearTimeout(i); 445 | }; 446 | } 447 | return {drop: drop}; 448 | } 449 | ; 450 | }(window)); 451 | } 452 | 453 | restyle.customElement = function (name, constructor, proto) { 454 | var 455 | key, 456 | ext = 'extends', 457 | prototype = Object.create(constructor.prototype), 458 | descriptor = {prototype: prototype}, 459 | has = descriptor.hasOwnProperty, 460 | isExtending = proto && has.call(proto, ext) 461 | ; 462 | if (isExtending) { 463 | descriptor[ext] = proto[ext]; 464 | } 465 | for (key in proto) { 466 | if (key !== ext) { 467 | prototype[key] = ( 468 | key === 'css' ? 469 | restyle( 470 | isExtending ? 471 | (proto[ext] + '[is=' + name + ']') : 472 | name, 473 | proto[key] 474 | ) : 475 | proto[key] 476 | ); 477 | } 478 | } 479 | return document.registerElement(name, descriptor); 480 | }; 481 | 482 | restyle.prefixes = [ 483 | 'webkit', 484 | 'moz', 485 | 'ms', 486 | 'o' 487 | ]; 488 | 489 | return restyle; 490 | 491 | /** 492 | * not sure if TODO since this might be prependend regardless the parser 493 | * @namespace url(http://www.w3.org/1999/xhtml); 494 | * @charset "UTF-8"; 495 | */ 496 | 497 | }({})) -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | 2 | var utils = function(){ 3 | var 4 | angle = function (name) { 5 | return function (degrees) { 6 | return name + '(' + deg(degrees) + ')'; 7 | }; 8 | }, 9 | camelCaseFind = /([a-z])([A-Z])/g, 10 | camelCaseReplace = function (m, $1, $2) { 11 | return $1 + '-' + $2.toLowerCase(); 12 | }, 13 | deg = function (degrees) { 14 | return typeof degrees === 'number' ? 15 | (degrees + 'deg') : degrees; 16 | }, 17 | doubleQuoteFind = /"/g, 18 | doubleQuoteReplace = '\\"', 19 | empty = [], 20 | hexToRgb = function (hex) { 21 | var i; 22 | hex = hex.slice(1); 23 | if (hex.length < 6) { 24 | hex = [ 25 | hex[0] + hex[0], 26 | hex[1] + hex[1], 27 | hex[2] + hex[2] 28 | ].join(''); 29 | } 30 | i = hex.length === 6 ? 0 : 2; 31 | return [ 32 | parseInt(hex.slice(i, i + 2), 16), 33 | parseInt(hex.slice(i + 2, i + 4), 16), 34 | parseInt(hex.slice(i + 4, i + 6), 16), 35 | i ? (parseInt(hex.slice(0, 2), 16) / 255).toFixed(3) : 1 36 | ]; 37 | }, 38 | namedFunction = function (name, args) { 39 | return name + '(' + empty.join.call(args, ',') + ')' 40 | }, 41 | namedMethod = function (name) { 42 | return function () { 43 | return namedFunction(name, arguments); 44 | }; 45 | }, 46 | separatorFind = /([a-z])-([a-z])/g, 47 | separatorReplace = function (m, $1, $2) { 48 | return $1 + $2.toUpperCase(); 49 | }, 50 | utils = { 51 | $clash: /^(?:top|scroll)$/, 52 | $global: typeof window === 'undefined' ? 53 | global : window, 54 | $camel: function (str) { 55 | return str.replace( 56 | separatorFind, 57 | separatorReplace 58 | ); 59 | }, 60 | $unCamel: function (str) { 61 | return str.replace( 62 | camelCaseFind, 63 | camelCaseReplace 64 | ); 65 | }, 66 | abs: Math.abs, 67 | concat: function () { 68 | return empty.join.call(arguments, ' '); 69 | }, 70 | cubicBezier: namedMethod('cubic-bezier'), 71 | floor: Math.floor, 72 | hex: function (r, g, b, a) { 73 | return '#'.concat( 74 | a == null ? '' : ( 75 | '0' + Math.round(a * 255).toString(16) 76 | ).slice(-2), 77 | ('0' + r.toString(16)).slice(-2), 78 | ('0' + g.toString(16)).slice(-2), 79 | ('0' + b.toString(16)).slice(-2) 80 | ); 81 | }, 82 | join: function () { 83 | return empty.join.call( 84 | empty.concat.apply(empty, arguments), 85 | ',' 86 | ); 87 | }, 88 | matrix: namedMethod('matrix'), 89 | matrix3d: namedMethod('matrix3d'), 90 | max: Math.max, 91 | min: Math.min, 92 | rgb: function rgb(r, g, b) { 93 | return g == null ? 94 | rgb.apply(utils, hexToRgb(r)) : 95 | 'rgb(' + [r, g, b] + ')'; 96 | }, 97 | rgba: function rgba(r, g, b, a) { 98 | return g == null ? 99 | rgba.apply(utils, hexToRgb(r)) : 100 | 'rgba(' + [r, g, b, a] + ')'; 101 | }, 102 | rotate: angle('rotate'), 103 | rotate3d: function (x, y, z, degrees) { 104 | return namedFunction('rotate3d', [x, y, z, deg(degrees)]); 105 | }, 106 | rotateX: angle('rotateX'), 107 | rotateY: angle('rotateY'), 108 | rotateZ: angle('rotateZ'), 109 | scale: namedMethod('scale'), 110 | scale3d: namedMethod('scale3d'), 111 | scaleX: namedMethod('scaleX'), 112 | scaleY: namedMethod('scaleY'), 113 | scaleZ: namedMethod('scaleZ'), 114 | skew: function (x, y) { 115 | return skew('rotate3d', [deg(x), deg(y)]); 116 | }, 117 | skewX: angle('skewX'), 118 | skewY: angle('skewY'), 119 | perspective: namedMethod('perspective'), 120 | round: Math.round, 121 | quote: function (text) { 122 | return '"' + text.replace( 123 | doubleQuoteFind, 124 | doubleQuoteReplace 125 | ) + '"'; 126 | }, 127 | translate: namedMethod('translate'), 128 | translate3d: namedMethod('translate3d'), 129 | translateX: namedMethod('translateX'), 130 | translateY: namedMethod('translateY'), 131 | translateZ: namedMethod('translateZ'), 132 | url: function(src) { 133 | return 'url(' + utils.quote(src) + ')' 134 | } 135 | } 136 | ; 137 | 138 | return utils; 139 | 140 | }(); 141 | -------------------------------------------------------------------------------- /template/amd.after: -------------------------------------------------------------------------------- 1 | ); -------------------------------------------------------------------------------- /template/amd.before: -------------------------------------------------------------------------------- 1 | define( -------------------------------------------------------------------------------- /template/copyright: -------------------------------------------------------------------------------- 1 | /*! (C) Andrea Giammarchi Mit Style License */ 2 | -------------------------------------------------------------------------------- /template/license.after: -------------------------------------------------------------------------------- 1 | 2 | */ 3 | -------------------------------------------------------------------------------- /template/license.before: -------------------------------------------------------------------------------- 1 | /*jslint forin: true, plusplus: true, indent: 2, browser: true, unparam: true */ 2 | /*! 3 | -------------------------------------------------------------------------------- /template/md.after: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /template/md.before: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 31 | 32 | -------------------------------------------------------------------------------- /template/node.after: -------------------------------------------------------------------------------- 1 | ; -------------------------------------------------------------------------------- /template/node.before: -------------------------------------------------------------------------------- 1 | module.exports = -------------------------------------------------------------------------------- /template/var.after: -------------------------------------------------------------------------------- 1 | ; -------------------------------------------------------------------------------- /template/var.before: -------------------------------------------------------------------------------- 1 | var restyle = -------------------------------------------------------------------------------- /test/.test.js: -------------------------------------------------------------------------------- 1 | var 2 | fs = require('fs'), 3 | path = require('path'), 4 | spawn = require('child_process').spawn, 5 | modules = path.join(__dirname, '..', 'node_modules', 'wru', 'node', 'program.js'), 6 | tests = [], 7 | ext = /\.js$/, 8 | code = 0, 9 | many = 0; 10 | 11 | function exit($code) { 12 | if ($code) { 13 | code = $code; 14 | } 15 | if (!--many) { 16 | if (!code) { 17 | fs.writeFileSync( 18 | path.join(__dirname, '..', 'index.html'), 19 | fs.readFileSync( 20 | path.join(__dirname, '..', 'index.html'), 21 | 'utf-8' 22 | ).replace(/var TESTS = \[.*?\];/, 'var TESTS = ' + JSON.stringify(tests) + ';'), 23 | 'utf-8' 24 | ); 25 | } 26 | process.exit(code); 27 | } 28 | } 29 | 30 | fs.readdirSync(__dirname).filter(function(file){ 31 | if (ext.test(file) && (fs.existsSync || path.existsSync)(path.join(__dirname, '..', 'src', file))) { 32 | ++many; 33 | tests.push(file.replace(ext, '')); 34 | spawn( 35 | 'node', [modules, path.join('test', file)], { 36 | detached: false, 37 | stdio: [process.stdin, process.stdout, process.stderr] 38 | }).on('exit', exit); 39 | } 40 | }); -------------------------------------------------------------------------------- /test/restyle.js: -------------------------------------------------------------------------------- 1 | //remove: 2 | var restyle = require('../build/restyle.node.js'); 3 | //:remove 4 | 5 | 6 | var hasDOM = typeof document !== typeof hasDOM; 7 | 8 | wru.test([ 9 | { 10 | name: 'basics', 11 | test: function () { 12 | var 13 | length = hasDOM && document.getElementsByTagName('style').length, 14 | initialStyle = hasDOM && window.getComputedStyle && 15 | getComputedStyle(document.body, null).getPropertyValue('background-color'), 16 | obj = restyle( 17 | { 18 | body: { 19 | background: '#EEE' 20 | } 21 | }, 22 | [] 23 | ); 24 | wru.assert('parsed correctly', obj == 'body{background:#EEE;}'); 25 | if (hasDOM) { 26 | wru.assert('node inserted', 27 | document.getElementsByTagName('style').length === length + 1 28 | ); 29 | if (initialStyle) { 30 | wru.assert('it restyled', 31 | initialStyle !== getComputedStyle(document.body, null).getPropertyValue('background-color') 32 | ); 33 | } 34 | obj.remove(); 35 | wru.assert('node removed', 36 | document.getElementsByTagName('style').length === length 37 | ); 38 | if (initialStyle) { 39 | wru.assert('it de-restyled', 40 | initialStyle === getComputedStyle(document.body, null).getPropertyValue('background-color') 41 | ); 42 | } 43 | } 44 | } 45 | },{ 46 | name: 'nested properties', 47 | test: function () { 48 | var obj = restyle( 49 | { 50 | body: { 51 | background: { 52 | color: '#999', 53 | image: 'url("/favicon.ico")' 54 | }, 55 | padding: 100 56 | }, 57 | div: { 58 | font: { 59 | family: 'sans-serif', 60 | size: 32 61 | } 62 | } 63 | }, 64 | [] 65 | ); 66 | setTimeout(wru.async(function(){ 67 | wru.assert(obj == ''.concat( 68 | 'body{', 69 | 'background-color:#999;', 70 | 'background-image:url("/favicon.ico");', 71 | 'padding:100px;', 72 | '}', 73 | 'div{', 74 | 'font-family:sans-serif;', 75 | 'font-size:32px;', 76 | '}' 77 | )); 78 | if (hasDOM) obj.remove(); 79 | }), 1000); 80 | } 81 | },{ 82 | name: '@keyframes', 83 | test: function () { 84 | var obj = restyle({ 85 | 'body > div': { 86 | animation: { 87 | name: 'spin', 88 | duration: '4s' 89 | } 90 | }, 91 | '@keyframes spin': { 92 | from: { 93 | transform: 'rotate(0deg)' 94 | }, 95 | to: { 96 | transform: 'rotate(360deg)' 97 | } 98 | } 99 | }, hasDOM ? null : restyle.prefixes); 100 | // should produce the following 101 | wru.log(obj == ''.concat( 102 | 'body > div{', 103 | '-webkit-animation-name:spin;', 104 | '-moz-animation-name:spin;', 105 | '-ms-animation-name:spin;', 106 | '-o-animation-name:spin;', 107 | 'animation-name:spin;', 108 | '-webkit-animation-duration:4s;', 109 | '-moz-animation-duration:4s;', 110 | '-ms-animation-duration:4s;', 111 | '-o-animation-duration:4s;', 112 | 'animation-duration:4s;', 113 | '}', 114 | '@-webkit-keyframes spin{', 115 | 'from{', 116 | '-webkit-transform:rotate(0deg);', 117 | 'transform:rotate(0deg);', 118 | '}', 119 | 'to{', 120 | '-webkit-transform:rotate(360deg);', 121 | 'transform:rotate(360deg);', 122 | '}', 123 | '}', 124 | '@-moz-keyframes spin{', 125 | 'from{', 126 | '-moz-transform:rotate(0deg);', 127 | 'transform:rotate(0deg);', 128 | '}', 129 | 'to{', 130 | '-moz-transform:rotate(360deg);', 131 | 'transform:rotate(360deg);', 132 | '}', 133 | '}', 134 | '@-ms-keyframes spin{', 135 | 'from{', 136 | '-ms-transform:rotate(0deg);', 137 | 'transform:rotate(0deg);', 138 | '}', 139 | 'to{', 140 | '-ms-transform:rotate(360deg);', 141 | 'transform:rotate(360deg);', 142 | '}', 143 | '}', 144 | '@-o-keyframes spin{', 145 | 'from{', 146 | '-o-transform:rotate(0deg);', 147 | 'transform:rotate(0deg);', 148 | '}', 149 | 'to{', 150 | '-o-transform:rotate(360deg);', 151 | 'transform:rotate(360deg);', 152 | '}', 153 | '}', 154 | '@keyframes spin{', 155 | 'from{', 156 | '-webkit-transform:rotate(0deg);', 157 | '-moz-transform:rotate(0deg);', 158 | '-ms-transform:rotate(0deg);', 159 | '-o-transform:rotate(0deg);', 160 | 'transform:rotate(0deg);', 161 | '}', 162 | 'to{', 163 | '-webkit-transform:rotate(360deg);', 164 | '-moz-transform:rotate(360deg);', 165 | '-ms-transform:rotate(360deg);', 166 | '-o-transform:rotate(360deg);', 167 | 'transform:rotate(360deg);', 168 | '}', 169 | '}' 170 | )); 171 | setTimeout(wru.async(function(){ 172 | hasDOM && obj.remove(); 173 | wru.assert('it probably spinned'); 174 | }), 2000); 175 | } 176 | },{ 177 | name: 'camel to CSS', 178 | test: function() { 179 | var obj = restyle({ 180 | body: { 181 | marginLeft: 32 182 | } 183 | }, []); 184 | wru.assert(obj == ''.concat( 185 | 'body{', 186 | 'margin-left:32px;', 187 | '}' 188 | )); 189 | if (hasDOM) obj.remove(); 190 | } 191 | },{ 192 | name: 'single property as Array', 193 | test: function () { 194 | var obj = restyle({ 195 | '.flexbox': { 196 | display: [ 197 | '-webkit-box', 198 | '-moz-box', 199 | '-ms-flexbox', 200 | '-webkit-flex', 201 | 'flex' 202 | ] 203 | } 204 | }, []); 205 | wru.assert(obj == ''.concat( 206 | '.flexbox{', 207 | 'display:-webkit-box;', 208 | 'display:-moz-box;', 209 | 'display:-ms-flexbox;', 210 | 'display:-webkit-flex;', 211 | 'display:flex;', 212 | '}' 213 | )); 214 | if (hasDOM) obj.remove(); 215 | } 216 | },{ 217 | name: 'nested with nested with nested ...', 218 | test: function () { 219 | var obj = restyle({ 220 | body: { 221 | any: { 222 | thing: { 223 | i: { 224 | want: 'OK' 225 | } 226 | } 227 | }, 228 | background: { 229 | position: { 230 | x: 10, 231 | y: '50%' 232 | } 233 | } 234 | } 235 | }, []); 236 | wru.assert(obj == ''.concat( 237 | 'body{', 238 | 'any-thing-i-want:OK;', 239 | 'background-position-x:10px;', 240 | 'background-position-y:50%;', 241 | '}' 242 | )); 243 | if (hasDOM) obj.remove(); 244 | } 245 | },{ 246 | name: '@page and @font-face declarations', 247 | test: function () { 248 | var obj = restyle({ 249 | '@page :pseudo-class': { 250 | margin: '2in' 251 | }, 252 | '@font-face': { 253 | font: { 254 | family: 'restyled', 255 | weight: 'normal', 256 | style: 'bold' 257 | } 258 | } 259 | }, []); 260 | wru.assert(obj == ''.concat( 261 | '@page :pseudo-class{', 262 | 'margin:2in;', 263 | '}', 264 | '@font-face{', 265 | 'font-family:restyled;', 266 | 'font-weight:normal;', 267 | 'font-style:bold;', 268 | '}' 269 | )); 270 | if (hasDOM) obj.remove(); 271 | } 272 | },{ 273 | name: 'replace', 274 | test: function () { 275 | var o = restyle({i:{display:'none'}}, ['test']), 276 | node = o.node, 277 | css = o.css; 278 | if (typeof document !== 'undefined') { 279 | o.replace({i:{display:'block'}}); 280 | wru.assert('previous node removed', !node.parentNode); 281 | wru.assert('current node in place', !!o.node.parentNode); 282 | wru.assert('CSS is different', css !== o.css); 283 | wru.assert('same prefixes', o.prefixes.join(',') === 'test'); 284 | } 285 | } 286 | },{ 287 | name: 'component - basic', 288 | test: function () { 289 | var obj = restyle('x-component', {i:{display:'none'}}, []); 290 | wru.assert(obj == ''.concat( 291 | 'x-component i{', 292 | 'display:none;', 293 | '}' 294 | )); 295 | if (hasDOM) obj.remove(); 296 | } 297 | },{ 298 | name: 'component - advanced', 299 | test: function () { 300 | var obj = restyle( 301 | 'x-component', 302 | { 303 | 'div': { 304 | animation: { 305 | name: 'spin', 306 | duration: '4s' 307 | } 308 | }, 309 | '@media (max-width: 600px)': { 310 | 'div': { 311 | display: 'none' 312 | } 313 | }, 314 | '@keyframes spin': { 315 | from: { 316 | transform: 'rotate(0deg)' 317 | }, 318 | to: { 319 | transform: 'rotate(360deg)' 320 | } 321 | }, 322 | '@font-face': { 323 | font: { 324 | family: 'restyled', 325 | weight: 'normal', 326 | style: 'bold' 327 | } 328 | } 329 | }, 330 | ['webkit'] 331 | ); 332 | wru.assert(obj == ''.concat( 333 | 'x-component div{', 334 | '-webkit-animation-name:spin;', 335 | 'animation-name:spin;', 336 | '-webkit-animation-duration:4s;', 337 | 'animation-duration:4s;', 338 | '}', 339 | '@-webkit-media (max-width: 600px){', 340 | 'x-component div{-webkit-display:none;display:none;}', 341 | '}', 342 | '@media (max-width: 600px){', 343 | 'x-component div{-webkit-display:none;display:none;}', 344 | '}', 345 | '@-webkit-keyframes spin{', 346 | 'from{-webkit-transform:rotate(0deg);transform:rotate(0deg);}', 347 | 'to{-webkit-transform:rotate(360deg);transform:rotate(360deg);}', 348 | '}', 349 | '@keyframes spin{', 350 | 'from{-webkit-transform:rotate(0deg);transform:rotate(0deg);}', 351 | 'to{-webkit-transform:rotate(360deg);transform:rotate(360deg);}', 352 | '}', 353 | '@font-face{', 354 | '-webkit-font-family:restyled;', 355 | 'font-family:restyled;', 356 | '-webkit-font-weight:normal;', 357 | 'font-weight:normal;', 358 | '-webkit-font-style:bold;', 359 | 'font-style:bold;', 360 | '}' 361 | )); 362 | if (hasDOM) obj.remove(); 363 | } 364 | },{ 365 | name: 'same name for self description', 366 | test: function () { 367 | var obj = restyle('x-component', {'x-component':{display:'none'}}, []); 368 | // should not create x-component x-component 369 | wru.assert(obj == ''.concat( 370 | 'x-component{', 371 | 'display:none;', 372 | '}' 373 | )); 374 | if (hasDOM) obj.remove(); 375 | } 376 | },{ 377 | name: 'customElement', 378 | test: function () { 379 | var i = 0; 380 | if (typeof document === 'undefined') { 381 | document = {}; 382 | } 383 | document.registerElement = function (name, constructor, proto) { 384 | i++; 385 | }; 386 | if (!Object.create) Object.create = function(p){return p}; 387 | var obj = restyle.customElement('x-test', function () {}, {}); 388 | wru.assert(i === 1); 389 | } 390 | },{ 391 | name: 'customElement via is', 392 | test: function () { 393 | var prefixes = restyle.prefixes, 394 | args; 395 | restyle.prefixes = []; 396 | document.registerElement = function (name, descriptor) { 397 | args = [name, descriptor]; 398 | }; 399 | restyle.customElement( 400 | 'x-clock', 401 | function () {}, 402 | { 403 | 'extends': 'div', 404 | css: { 405 | 'input': {border: 0} 406 | } 407 | } 408 | ); 409 | wru.assert(args[0] === 'x-clock'); 410 | wru.assert( 411 | (args[1].prototype.css.valueOf()) === 412 | 'div[is=x-clock] input{border:0px;}' 413 | ); 414 | restyle.prefixes = prefixes; 415 | } 416 | }, { 417 | name: 'empty string for same component', 418 | test: function () { 419 | var prefixes = restyle.prefixes, 420 | args; 421 | restyle.prefixes = []; 422 | document.registerElement = function (name, descriptor) { 423 | args = [name, descriptor]; 424 | }; 425 | restyle.customElement( 426 | 'x-dafuq', 427 | function () {}, 428 | { 429 | css: { 430 | '': {fontSize: 37} 431 | } 432 | } 433 | ); 434 | wru.assert(args[0] === 'x-dafuq'); 435 | wru.assert( 436 | (args[1].prototype.css.valueOf()) === 437 | 'x-dafuq{font-size:37px;}' 438 | ); 439 | restyle.prefixes = prefixes; 440 | } 441 | }, { 442 | name: 'ampersand for same component', 443 | test: function () { 444 | var prefixes = restyle.prefixes, 445 | args; 446 | restyle.prefixes = []; 447 | document.registerElement = function (name, descriptor) { 448 | args = [name, descriptor]; 449 | }; 450 | restyle.customElement( 451 | 'x-dafuq', 452 | function () {}, 453 | { 454 | css: { 455 | '&': {fontSize: 37} 456 | } 457 | } 458 | ); 459 | wru.assert(args[0] === 'x-dafuq'); 460 | wru.assert( 461 | (args[1].prototype.css.valueOf()) === 462 | 'x-dafuq{font-size:37px;}' 463 | ); 464 | restyle.prefixes = prefixes; 465 | } 466 | }, { 467 | name: 'natural Array style', 468 | test: function () { 469 | wru.assert(restyle([ 470 | 'a', 471 | 'p', { 472 | height: 50 473 | } 474 | ]) === 'a{height:50px;}p{height:50px;}'); 475 | } 476 | }, { 477 | name: 'natural Array style with component', 478 | test: function () { 479 | wru.assert(restyle('custom-element', [ 480 | 'a', 481 | 'p', { 482 | height: 50 483 | } 484 | ]) === 'custom-element a{height:50px;}custom-element p{height:50px;}'); 485 | } 486 | }, { 487 | name: 'self reference with classes', 488 | test: function () { 489 | wru.assert( 490 | restyle('comp', {'&.test .a,&.test .b':{width:100}}) === 491 | 'comp.test .a,comp.test .b{width:100px;}' 492 | ); 493 | } 494 | } 495 | ]); 496 | --------------------------------------------------------------------------------