Created
January 18, 2014 09:46
-
-
Save FWeinb/8488301 to your computer and use it in GitHub Desktop.
Controllable mixin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ---- | |
// Sass (v3.3.0.rc.2) | |
// Compass (v1.0.0.alpha.17) | |
// ---- | |
/*! sassyjson - v0.0.2 - 2014-01-17 */ | |
// Logs an error at `$pointer` with `$string` message | |
// -------------------------------------------------------------------------------- | |
// @param [string] $string: error message | |
// @param [number] $pointer: pointer position | |
// -------------------------------------------------------------------------------- | |
// @return [list] (pointer, false) | |
@function _throw($string, $pointer) { | |
@warn "ERROR::#{$pointer}::#{$string}"; | |
@return $pointer, false; | |
} | |
// Move pointer to position of token | |
// -------------------------------------------------------------------------------- | |
// @param [string] $source: JSON complete source | |
// @param [number] $pointer: current pointer | |
// @param [string] $token: token to reach | |
// -------------------------------------------------------------------------------- | |
// @throw "Expected $token; found {x}." | |
// @throw "Expected $token but reached end of stream." | |
// -------------------------------------------------------------------------------- | |
// @return [number|false] new pointer | |
@function _consume($source, $pointer, $token) { | |
$length: str-length($source); | |
@while $pointer <= $length { | |
$char: str-slice($source, $pointer, $pointer); | |
$pointer: $pointer + 1; | |
@if $char == $token { | |
@return $pointer; | |
} | |
@else if $char == " " { | |
// @continue; | |
} | |
@else { | |
@return _throw("Expected `" + $token + "; ` found `" + $char + "`.", $pointer); | |
} | |
} | |
@return _throw("Expected `" + $token + "` but reached end of stream.", $pointer); | |
} | |
// Delay parsing to type-specific function | |
// according to found character | |
// -------------------------------------------------------------------------------- | |
// @param [string] $source: JSON complete source | |
// @param [number] $pointer: current pointer | |
// -------------------------------------------------------------------------------- | |
// @throw "Unexpected token $token." | |
// @throw "Empty JSON string." | |
// -------------------------------------------------------------------------------- | |
// @return [list|false] (new pointer, parsed value) | |
@function _json-decode--value($source, $pointer) { | |
$length: str-length($source); | |
@while $pointer <= $length { | |
$token: str-slice($source, $pointer, $pointer); | |
$pointer: $pointer + 1; | |
@if $token == '{' { | |
@return _json-decode--map($source, $pointer); | |
} | |
@else if $token == '[' { | |
@return _json-decode--list($source, $pointer); | |
} | |
@else if $token == 't' { | |
@return _json-decode--true($source, $pointer); | |
} | |
@else if $token == 'f' { | |
@return _json-decode--false($source, $pointer); | |
} | |
@else if $token == '"' { | |
@return _json-decode--string($source, $pointer); | |
} | |
@else if $token == 'n' { | |
@return _json-decode--null($source, $pointer); | |
} | |
@else if index('1' '2' '3' '4' '5' '6' '7' '8' '9' '0' '-' '.', $token) { | |
@return _json-decode--number($source, $pointer); | |
} | |
@else if $token == ' ' { | |
// @continue; | |
} | |
@else { | |
@return _throw("Unexpected token `" + $token + "`.", $pointer); | |
} | |
} | |
@return _throw("Empty JSON string.", $pointer); | |
} | |
// Power function | |
// -------------------------------------------------------------------------------- | |
// @param [number] $x: number | |
// @param [number] $n: power | |
// -------------------------------------------------------------------------------- | |
// @return [number] $x ^ $n | |
@function _pow($x, $n) { | |
$ret: 1; | |
@if $n >= 0 { | |
@for $i from 1 through $n { | |
$ret: $ret * $x; | |
} | |
} @else { | |
@for $i from $n to 0 { | |
$ret: $ret / $x; | |
} | |
} | |
@return $ret; | |
} | |
// Parses a JSON encoded number to find the integer part | |
// -------------------------------------------------------------------------------- | |
// @param [string] $source: JSON complete source | |
// @param [number] $pointer: current pointer | |
// -------------------------------------------------------------------------------- | |
// @throw "Unexpected token $token." | |
// -------------------------------------------------------------------------------- | |
// @return [list|false] (new pointer, parsed number) | |
@function _find-integer($source, $pointer) { | |
$length: str-length($source); | |
$strings: '0' '1' '2' '3' '4' '5' '6' '7' '8' '9'; | |
$numbers: 0 1 2 3 4 5 6 7 8 9; | |
$result: 0; | |
@while $pointer <= $length { | |
$token: to-lower-case(str-slice($source, $pointer, $pointer)); | |
$index: index($strings, $token); | |
@if $token == '-' { | |
// do nothing | |
} | |
@else if $index { | |
$number: nth($numbers, $index); | |
$result: $result * 10 + $number; | |
} | |
@else { | |
@if index('e' '.' ',' ']' '}' ' ', $token) { | |
@return $pointer, $result; | |
} | |
@return _throw("Unexpected token `" + $token + "`.", $pointer); | |
} | |
$pointer: $pointer + 1; | |
} | |
@return $pointer, $result; | |
} | |
// Parses a JSON encoded number to find the digits | |
// -------------------------------------------------------------------------------- | |
// @param [string] $source: JSON complete source | |
// @param [number] $pointer: current pointer | |
// -------------------------------------------------------------------------------- | |
// @throw "Unexpected token $token." | |
// -------------------------------------------------------------------------------- | |
// @return [list|false] (new pointer, parsed number) | |
@function _find-digits($source, $pointer) { | |
$length: str-length($source); | |
$strings: '0' '1' '2' '3' '4' '5' '6' '7' '8' '9'; | |
$numbers: 0 1 2 3 4 5 6 7 8 9; | |
$result: null; | |
$runs: 1; | |
@while $pointer <= $length { | |
$token: to-lower-case(str-slice($source, $pointer, $pointer)); | |
$index: index($strings, $token); | |
@if $token == '.' { | |
// @continue; | |
} | |
@else if $index and $index > 0 { | |
$number: nth($numbers, $index); | |
$runs: $runs * 10; | |
$result: if($result == null, $number, $result * 10 + $number); | |
} | |
@else { | |
@if index('e' '.' ',' ']' '}' ' ', $token) { | |
@return $pointer, if($result != null, $result / $runs, $result); | |
} | |
@return _throw("Unexpected token `" + $token + "`.", $pointer); | |
} | |
$pointer: $pointer + 1; | |
} | |
@return $pointer, if($result != null, $result / $runs, $result); | |
} | |
// Parses a JSON encoded number to find the exponent part | |
// -------------------------------------------------------------------------------- | |
// @param [string] $source: JSON complete source | |
// @param [number] $pointer: current pointer | |
// -------------------------------------------------------------------------------- | |
// @throw "Unexpected token $token." | |
// -------------------------------------------------------------------------------- | |
// @return [list|false] (new pointer, parsed number) | |
@function _find-exponent($source, $pointer) { | |
$length: str-length($source); | |
$strings: '0' '1' '2' '3' '4' '5' '6' '7' '8' '9'; | |
$numbers: 0 1 2 3 4 5 6 7 8 9; | |
$result: null; | |
$minus: false; | |
@while $pointer <= $length { | |
$token: to-lower-case(str-slice($source, $pointer, $pointer)); | |
$index: index($strings, $token); | |
@if $token == 'e' { | |
// @continue; | |
} | |
@else if $token == '-' { | |
$minus: true; | |
} | |
@else if $token == '+' { | |
$minus: false; | |
} | |
@else if $index and $index > 0 { | |
$number: nth($numbers, $index); | |
$result: if($result == null, $number, $result * 10 + $number); | |
} | |
@else { | |
@if not index(" ", ", ", "]", "}", $token) { | |
@return _throw("Unexpected token `" + $token + "`.", $pointer); | |
} | |
@return $pointer, if($minus and $result != null, $result * -1, $result); | |
} | |
$pointer: $pointer + 1; | |
} | |
@return $pointer, if($minus and $result != null, $result * -1, $result); | |
} | |
// Parses a JSON encoded string | |
// -------------------------------------------------------------------------------- | |
// @param [string] $source: JSON complete source | |
// @param [number] $pointer: current pointer | |
// -------------------------------------------------------------------------------- | |
// @throw "Unterminated string." | |
// -------------------------------------------------------------------------------- | |
// @return [list|false] (new pointer, parsed string) | |
@function _json-decode--string($source, $pointer) { | |
// Check for the end of the string | |
$temp: str-slice($source, $pointer); | |
$end: str-index($temp, '"'); | |
$string: ""; | |
// If no end is found | |
@if $end == 0 { | |
@return _throw("Unterminated string.", $pointer); | |
} | |
@else if $end > 1 { | |
$string: str-slice($temp, 1, $end - 1); | |
$string: _strip-token($string, "\r"); | |
$string: _strip-token($string, "\n"); | |
} | |
@return ($pointer + $end, $string); | |
} | |
// Parses a JSON encoded `true` | |
// -------------------------------------------------------------------------------- | |
// @param [string] $source: JSON complete source | |
// @param [number] $pointer: current pointer | |
// -------------------------------------------------------------------------------- | |
// @throw "Unexpected token `t`." | |
// -------------------------------------------------------------------------------- | |
// @return [list|false] (new pointer, true) | |
@function _json-decode--true($source, $pointer) { | |
$length: str-length($source); | |
@if $length - $pointer < 2 | |
or str-slice($source, $pointer, $pointer) != 'r' | |
or str-slice($source, $pointer + 1, $pointer + 1) != 'u' | |
or str-slice($source, $pointer + 2, $pointer + 2) != 'e' { | |
@return _throw("Unexpected token: `t`.", $pointer); | |
} | |
@return ($pointer + 3, true); | |
} | |
// Parses a JSON encoded `false` | |
// -------------------------------------------------------------------------------- | |
// @param $source: JSON complete source | |
// @param $pointer: current pointer | |
// -------------------------------------------------------------------------------- | |
// @throw "Unexpected token `f`." | |
// -------------------------------------------------------------------------------- | |
// @return [list|false] (new pointer, false) | |
@function _json-decode--false($source, $pointer) { | |
$length: str-length($source); | |
@if $length - $pointer < 3 | |
or str-slice($source, $pointer, $pointer) != 'a' | |
or str-slice($source, $pointer + 1, $pointer + 1) != 'l' | |
or str-slice($source, $pointer + 2, $pointer + 2) != 's' | |
or str-slice($source, $pointer + 3, $pointer + 3) != 'e' { | |
@return _throw("Unexpected token: `f`.", $pointer); | |
} | |
@return ($pointer + 4, false); | |
} | |
// Parses a JSON encoded `null` | |
// -------------------------------------------------------------------------------- | |
// @param [string] $source: JSON complete source | |
// @param [number] $pointer: current pointer | |
// -------------------------------------------------------------------------------- | |
// @throw "Unexpected token `n`." | |
// -------------------------------------------------------------------------------- | |
// @return [list|false] (new pointer, null) | |
@function _json-decode--null($source, $pointer) { | |
$length: str-length($source); | |
@if $length - $pointer < 2 | |
or str-slice($source, $pointer, $pointer) != 'u' | |
or str-slice($source, $pointer + 1, $pointer + 1) != 'l' | |
or str-slice($source, $pointer + 2, $pointer + 2) != 'l' { | |
@return _throw("Unexpected token: `n`.", $pointer); | |
} | |
@return ($pointer + 3, null); | |
} | |
// Parses a JSON encoded number | |
// -------------------------------------------------------------------------------- | |
// @param [string] $source: JSON complete source | |
// @param [number] $pointer: current pointer | |
// -------------------------------------------------------------------------------- | |
// @throw "Unexpected token $token." | |
// @throw "Unexpected end of stream." | |
// -------------------------------------------------------------------------------- | |
// @return [list|false] (new pointer, parsed number) | |
@function _json-decode--number($source, $pointer) { | |
$pointer: $pointer - 1; // Move back pointer to begininng of number | |
$allowed: '-' '0' '1' '2' '3' '4' '5' '6' '7' '8' '9'; // Allowed characted to start with | |
$first: str-slice($source, $pointer, $pointer); // First character of the number | |
$minus: $first == '-'; // Is it negative? | |
// Early check for errors | |
@if not index($allowed, $first) { | |
@return _throw("Unexpected token `" + $first + "`.", $pointer); | |
} | |
// Find the integer part | |
$find-integer: _find-integer($source, $pointer); | |
$pointer: nth($find-integer, 1); | |
$result: nth($find-integer, 2); | |
@if not $result { // Error occured | |
@return $find-integer; | |
} | |
// Find digits | |
@if str-slice($source, $pointer, $pointer) == '.' { | |
$find-digits: _find-digits($source, $pointer); | |
$pointer: nth($find-digits, 1); | |
$digits: nth($find-digits, 2); | |
@if $digits == null { // Empty digits, throw error | |
@return _throw("Unexpected end of stream.", $pointer); | |
} | |
@else if $digits == false { // Error occured, return it | |
@return $find-digits; | |
} | |
$result: $result + $digits; | |
} | |
// Find exponent | |
@if to-lower-case(str-slice($source, $pointer, $pointer)) == 'e' { | |
$find-exponent: _find-exponent($source, $pointer); | |
$pointer: nth($find-exponent, 1); | |
$exponent: nth($find-exponent, 2); | |
@if $exponent == null { // Empty exponent, throw error | |
@return _throw("Unexpected end of stream.", $pointer); | |
} | |
@else if $exponent == false { // Error occured, return it | |
@return $find-exponent; | |
} | |
$result: $result * _pow(10, $exponent); | |
} | |
@return ($pointer, if($minus, $result * -1, $result)); | |
} | |
// Parses a JSON encoded array | |
// -------------------------------------------------------------------------------- | |
// @param [string] $source: JSON complete source | |
// @param [number] $pointer: current pointer | |
// -------------------------------------------------------------------------------- | |
// @throw "Unexpected comma in array literal." | |
// @throw "Missing comma in array literal." | |
// @throw "Unterminated array literal." | |
// -------------------------------------------------------------------------------- | |
// @return [list|false] (new pointer, parsed list) | |
@function _json-decode--list($source, $pointer) { | |
$length: str-length($source); | |
$list: (); | |
$needs-comma: false; | |
@if $pointer <= $length and str-slice($source, $pointer, $pointer) == "]" { | |
@return ($pointer + 1, $list); | |
} | |
@while $pointer <= $length { | |
$token: str-slice($source, $pointer, $pointer); | |
@if $token == "]" { | |
@if not $needs-comma and length($list) != 0 { | |
@return _throw("Unexpected comma in array literal.", $pointer); | |
} | |
// Do it the Sass way and destruct a single item array to an element. | |
@return ($pointer + 1, if(length($list) == 1, nth($list, 1), $list)); | |
} | |
@else if $token == " " { | |
$pointer: $pointer + 1; | |
} | |
@else if $token == "," { | |
@if not $needs-comma { | |
@return _throw("Unexpected comma in array literal.", $pointer); | |
} | |
$needs-comma: false; | |
$pointer: $pointer + 1; | |
} | |
@else { | |
@if $needs-comma { | |
@return _throw("Missing comma in array literal.", $pointer); | |
} | |
$read: _json-decode--value($source, $pointer); | |
$pointer: nth($read, 1); | |
$list: append($list, nth($read, 2)); | |
$needs-comma: true; | |
} | |
} | |
@return _throw("Unterminated array literal.", $pointer); | |
} | |
// Parses a JSON encoded object | |
// -------------------------------------------------------------------------------- | |
// @param [string] $source: JSON complete source | |
// @param [number] $pointer: current pointer | |
// -------------------------------------------------------------------------------- | |
// @throw "Unexpected comma in object literal." | |
// @throw "Unexpected token $token in object literal." | |
// @throw "Missing comma in object literal." | |
// @throw "Unterminated object literal." | |
// @throw "Consuming token `:` failed." | |
// -------------------------------------------------------------------------------- | |
// @return [list|false] (new pointer, map) | |
@function _json-decode--map($source, $pointer) { | |
$length: str-length($source); | |
$map: (); | |
$needs-comma: false; | |
// Deal with empty map | |
@if $pointer <= $length and str-slice($source, $pointer, $pointer) == "}" { | |
@return ($pointer + 1, $map); | |
} | |
@while $pointer <= $length { | |
$token: str-slice($source, $pointer, $pointer); | |
$pointer: $pointer + 1; | |
@if $token == "}" { | |
@if not $needs-comma and length($map) != 0 { | |
@return _throw("Unexpected comma in object literal.", $pointer); | |
} | |
@return ($pointer + 1, $map); | |
} | |
@else if $token == " " { | |
// @continue; | |
} | |
@else if $token == "," { | |
@if not $needs-comma { | |
@return _throw("Unexpected comma in object literal.", $pointer); | |
} | |
$needs-comma: false; | |
} | |
@else if $token == '"' { | |
@if $needs-comma { | |
@return _throw("Missing comma in object literal.", $pointer); | |
} | |
// Read key | |
$read-key: _json-decode--string($source, $pointer); | |
$pointer: nth($read-key, 1); | |
$key: nth($read-key, 2); | |
// Remove colon | |
$pointer: _consume($source, $pointer, ":"); | |
@if length($pointer) > 1 { // If consume has failed | |
@return _throw("Consuming token `:` failed.", 0); | |
} | |
// Read value | |
$read-value: _json-decode--value($source, $pointer); | |
$pointer: nth($read-value, 1); | |
$value: nth($read-value, 2); | |
// Add pair to map | |
$map: map-merge($map, ($key: $value)); | |
$needs-comma: true; | |
} | |
@else { | |
@return _throw("Unexpected token `" + $token + "` in object literal.", $pointer); | |
} | |
} | |
@return _throw("Unterminated object literal.", $pointer); | |
} | |
// Parse a JSON string | |
// -------------------------------------------------------------------------------- | |
// @param $json: JSON string to parse | |
// -------------------------------------------------------------------------------- | |
// @throw "Input string may not be null" | |
// -------------------------------------------------------------------------------- | |
// @return [literal|false] | |
@function json-decode($json) { | |
$length: str-length($json); | |
$pointer: 1; | |
$value: null; | |
@if $json == null { | |
@return _throw("Input string may not be null.", $pointer); | |
} | |
@while $value != false // Stop if error | |
and $pointer <= $length { | |
$read: _json-decode--value($json, $pointer); | |
$pointer: nth($read, 1); | |
$value: nth($read, 2); | |
} | |
@return $value; | |
} | |
// Proof quote a value | |
// -------------------------------------------------------------------------------- | |
// @param $value: value to be quoted | |
// -------------------------------------------------------------------------------- | |
// @return [string] quoted value | |
@function _proof-quote($value) { | |
@return '"' + $value + '"'; | |
} | |
// Encode a bool to JSON | |
// -------------------------------------------------------------------------------- | |
// @param $bool: bool to be encoded | |
// -------------------------------------------------------------------------------- | |
// @return [bool] boolean | |
@function _json-encode--bool($boolean) { | |
@return $boolean; | |
} | |
// Encode a color to JSON | |
// -------------------------------------------------------------------------------- | |
// @param $color: color to be encoded | |
// -------------------------------------------------------------------------------- | |
// @return [string] encoded color | |
@function _json-encode--color($color) { | |
@return _proof-quote($color); | |
} | |
// Encode a list to JSON | |
// -------------------------------------------------------------------------------- | |
// @param $list: list to be encoded | |
// -------------------------------------------------------------------------------- | |
// @return [string] encoded list | |
@function _json-encode--list($list) { | |
$str: ""; | |
@each $item in $list { | |
$str: $str + ', ' + json-encode($item); | |
} | |
@return '[' + str-slice($str, 3) + ']'; | |
} | |
// Encode a map to JSON | |
// -------------------------------------------------------------------------------- | |
// @param $map: map to be encoded | |
// -------------------------------------------------------------------------------- | |
// @return [string] encoded map | |
@function _json-encode--map($map) { | |
$str: ""; | |
@each $key, $value in $map { | |
$str: $str + ', ' + _proof-quote($key) + ': ' + json-encode($value); | |
} | |
@return '{' + str-slice($str, 3) + '}'; | |
} | |
// Encode a number to JSON | |
// -------------------------------------------------------------------------------- | |
// @param $number: number to be encoded | |
// -------------------------------------------------------------------------------- | |
// @return [string] encoded number | |
@function _json-encode--number($number) { | |
@return if(unitless($number), $number, _proof-quote($number)); | |
} | |
// Encode a string to JSON | |
// -------------------------------------------------------------------------------- | |
// @param $string: string to be encoded | |
// -------------------------------------------------------------------------------- | |
// @return [string] encoded string | |
@function _json-encode--string($string) { | |
@return _proof-quote($string); | |
} | |
// Encode `null` to JSON | |
// -------------------------------------------------------------------------------- | |
// @param $null: `null` | |
// -------------------------------------------------------------------------------- | |
// @return [string] `null` | |
@function _json-encode--null($null) { | |
@return "null"; | |
} | |
// JSON.stringify a value and pass it as a font-family of head element | |
// -------------------------------------------------------------------------------- | |
// @param $value: value to be stringified | |
@mixin json-encode($value) { | |
head { | |
font-family: json-encode($value); | |
} | |
} | |
// Delay the encoding of ta literal to JSON | |
// to a type-specific method | |
// -------------------------------------------------------------------------------- | |
// @param $value: value to be stringified | |
// -------------------------------------------------------------------------------- | |
// @throw "Unknown type for $value ({x})." | |
// -------------------------------------------------------------------------------- | |
// @return [string|false] JSON encoded string | |
@function json-encode($value) { | |
$types: list, map, number, string, bool, color; | |
$value-type: type-of($value); | |
@if $value-type != null or index($types, $value-type) { | |
@return call('_json-encode--#{$value-type}', $value); | |
} | |
@warn "Unknown type for #{$value} (#{type-of($value)})."; | |
@return false; | |
} | |
// SassyControles | |
@function strip-unit($number) { | |
@return $number / ($number * 0 + 1); | |
} | |
$controll-data-map : ( ); | |
@mixin controllable($property, $value, $options: ()){ | |
$selector: #{&}; | |
$value-type: type-of($value); | |
$options: map-merge($options, (type: $value-type)); | |
$type-options: (); | |
@if $value-type == number{ | |
$type-options: (value: strip-unit($value), unit: unit($value)); | |
} @else { | |
$type-options: (value: $value); | |
} | |
$options: map-merge($options, $type-options); | |
$property-options-map: (#{$property}: $options); | |
@if map-has-key($controll-data-map, $selector){ | |
$property-options-map : map-merge(map-get($controll-data-map, $selector), $property-options-map); | |
} | |
$controll-data-map: map-merge($controll-data-map, (#{$selector}: $property-options-map) ); | |
#{$property}: $value; | |
} | |
.test{ | |
@include controllable('width', 50px, (min: 0, max: 500)); | |
@include controllable('height', 50px, (min: 0, max: 100)); | |
@include controllable('background-color', #f00); | |
} | |
body:before{ | |
display:none; | |
content:json-encode($controll-data-map); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! sassyjson - v0.0.2 - 2014-01-17 */ | |
.test { | |
width: 50px; | |
height: 50px; | |
background-color: red; | |
} | |
body:before { | |
display: none; | |
content: '{".test": {"width": {"min": 0, "max": 500, "type": "number", "value": 50, "unit": "px"}, "height": {"min": 0, "max": 100, "type": "number", "value": 50, "unit": "px"}, "background-color": {"type": "color", "value": "red"}}}'; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment