Fetching contributors…
Cannot retrieve contributors at this time
217 lines (193 sloc) 8.59 KB
inStyle (v1.6.3)
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()."
$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))
$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)}")
$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)
$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
@return $index
@return $index
@function __getDepthMap($selector, $current)
$depthMap: ()
// Make sure the $current list is correctly parsed while using libsass.
$currentList: ()
@each $parent in $current
$currentList: append($currentList, $parent, comma)
@if length($currentList) > 0
$current: $currentList
@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
$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)
// 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}