/* inStyle (v1.6.1) github.com/salsita/inStyle 2016 | MIT ============================== */ // Configuration $__inTagAppend: '<' !default $__inTagInsert: '^' !default $__inTagReplace: '@' !default // String helpers @function __trimString($string) $index: str-index($string, ' ') @if $index == 1 @return __trimString(str-slice($string, $index + 1, -1)) @else if $index == str-length($string) @return __trimString(str-slice($string, 1, $index - 1)) @return $string @function __stringToList($string, $delimiter: ',', $separator: comma) $list: () $sum: str-length($string) @for $i from 1 through $sum $str: str-index($string, $delimiter) @if str-length($string) >= 1 and $str == null $list: append($list, unquote(__trimString($string)), $separator) $string: '' @if type-of($str) == number $each: str-slice($string, 0, ($str - 1)) $list: append($list, unquote(__trimString($each)), $separator) $string: str-slice($string, ($str + 1), $sum) @return $list // List helpers @function __insertInList($list, $index, $value) $result: null @if $index > length($list) @warn "List index is #{$index} but list is only #{length($list)} items long for __insertInList()." @else $result: () @for $i from 1 through length($list) @if $i == $index $result: append($result, $value) $result: append($result, nth($list, $i)) @return $result @function __removeFromList($list, $value, $recursive: false) $result: () @for $i from 1 through length($list) @if type-of(nth($list, $i)) == list and $recursive $result: append($result, remove(nth($list, $i), $value, $recursive)) @else if nth($list, $i) != $value $result: append($result, nth($list, $i)) @return $result @function __reverseList($list, $recursive: false) $result: () @for $i from length($list)*-1 through -1 @if type-of(nth($list, abs($i))) == list and $recursive $result: append($result, __reverseList(nth($list, abs($i)), $recursive)) @else $result: append($result, nth($list, abs($i))) @return $result @function __listToString($list, $glue: '', $is-nested: false) $result: null @for $i from 1 through length($list) $e: nth($list, $i) @if type-of($e) == list $result: unquote("#{$result}#{to-string($e, $glue, true)}") @else $result: if($i != length($list) or $is-nested, unquote("#{$result}#{$e}#{$glue}"), unquote("#{$result}#{$e}")) @return $result @function __removeDuplicatesFromList($list, $recursive: false, $separator: comma) $result: () @each $item in $list @if not index($result, $item) @if length($item) > 1 and $recursive $result: append($result, __removeDuplicatesFromList($item, $recursive), $separator) @else $result: append($result, $item, $separator) @return $result // Custom helpers @function __tagIndex($string, $tag) $index: 0 @if str-index($string, $tag) == 1 $index: 1 $sum: str-length($string) @for $i from 2 through $sum @if str-slice($string, $i, $i) == $tag $index: $index + 1 @else @return $index @return $index @function __getDepthMap($selector, $current) $depthMap: () @each $parent in $current // Save maximum length of matched compound to compare relevancy $parentIndex: index($current, $parent) $depthMap: append($depthMap, 0, comma) @each $compound in $selector // Check only for specific compound @if max(__tagIndex($compound, $__inTagInsert), __tagIndex($compound, $__inTagAppend), __tagIndex($compound, $__inTagReplace)) == 0 $simple: simple-selectors($compound) // Test all matches starting with full compound and reducing for each step @for $i from 1 through length($simple) @if $i > 1 $simple: __removeFromList($simple, nth($simple, length($simple))) @if index($parent, __listToString($simple)) and length($simple) > nth($depthMap, $parentIndex) // Relevancy scoring $depthIndex: index(__reverseList($parent), __listToString($simple)) + (length($simple) / 1000) $depthMap: set-nth($depthMap, $parentIndex, $depthIndex) @return $depthMap // In mixin =in($selectors) $final: () $current: & $selectors: __stringToList($selectors) $startIndex: 1 // Start indexing above current element $checkDupes: false @each $selector in $selectors // Trim all extra empty spaces $selector: __removeFromList(__stringToList($selector, ' ', space), '') // Render only best matching parents in multiselectors $depthMap: if(length($current) == 1, 0, __getDepthMap($selector, $current)) @for $n from 1 through length($current) @if nth($depthMap, $n) == max($depthMap...) $parent: nth($current, $n) $newParent: __reverseList($parent) $insertQueue: () // Save insertions to process later // Process modifications RTL @each $compound in __reverseList($selector) $appendIndex: __tagIndex($compound, $__inTagAppend) $insertIndex: __tagIndex($compound, $__inTagInsert) $replaceIndex: __tagIndex($compound, $__inTagReplace) $changeIndex: max($appendIndex, $insertIndex, $replaceIndex) + $startIndex // OUT_OF_BOUNDS check @if $changeIndex > length($parent) @error 'OUT_OF_BOUNDS: \'#{$compound}\' modification outside of \'#{$parent} {}\' (#{$changeIndex} vs #{length($parent)}).' @if $insertIndex > 0 // Add to insertion queue $insertQueue: append($insertQueue, $compound) @else if $replaceIndex > 0 // Replace $validatedCompound: __listToString(simple-selectors(str-slice($compound, $replaceIndex + 1))) $newParent: set-nth($newParent, $startIndex + $replaceIndex, $validatedCompound) $checkDupes: true @else if $appendIndex > 0 // Append with tag $validatedCompound: nth($newParent, $startIndex + $appendIndex) + __listToString(simple-selectors(str-slice($compound, $appendIndex + 1))) $newParent: set-nth($newParent, $startIndex + $appendIndex, $validatedCompound) @else // Append with selector $simple: simple-selectors($compound) $state: () $matched: false // Loop compound variants @for $i from 1 through length($simple) @if $i > 1 $state: join(nth($simple, length($simple)), $state) $simple: __removeFromList($simple, nth($simple, length($simple))) $trySimple: __listToString($simple) $tryState: if($i > 1, __listToString($state), '') // Match base to reversed parent list @for $n from ($startIndex + 1) through length($newParent) @if $n <= length($newParent) and $trySimple == nth($newParent, $n) // Append to matched parent selector $matched: true $newParent: set-nth($newParent, $n, unquote($trySimple + $tryState)) @if not $matched @error 'OUT_OF_BOUNDS: \'#{nth($simple, 1)}\' not found in \'#{$parent}\'.' // Process insertion queue @if length($insertQueue) > 0 $insertIndexes: () @each $insert in $insertQueue $insertIndex: __tagIndex($insert, $__inTagInsert) $validatedCompound: __listToString(simple-selectors(str-slice($insert, $insertIndex + 1))) // Find how many previous inserts were lower than current $addIndex: 0 @each $index in $insertIndexes @if $insertIndex >= $index $addIndex: $addIndex + 1 $insertIndexes: append($insertIndexes, $insertIndex) // Insert in selector $newIndex: $startIndex + $insertIndex + $addIndex $newParent: __insertInList($newParent, $newIndex, $validatedCompound) // Save modified selectors $final: append($final, __reverseList($newParent), comma) // Remove possible duplicates from multiselector replacement $final: if(length($final) > 1 and $checkDupes, __removeDuplicatesFromList($final), $final) // Render final selectors @at-root #{$final} codepen: debug @content