require 'archetype/functions/helpers'
#
# This module provides a set of Sass functions for working with Sass::List
#
module Archetype::SassExtensions::Lists
#
# replace an index in a list
#
# *Parameters*:
# - $list {List} the list to replace from
# - $value {\*} the value to replace (if nil, it's a removal)
# - $idx {Number} the index to replace
# - $separator {String} the separator to use [auto|comma|space]
# *Returns*:
# - {List} the list with replaced index
#
def list_replace(list, idx = false, value = nil, separator = nil)
# return early if the index is invalid (no operation)
return list if (not idx or idx == Sass::Script::Bool.new(false))
separator ||= list.separator if list.is_a?(Sass::Script::List)
# if $value is `nil`, make sure we can use it
value = nil if value == Sass::Script::String.new('nil')
# cast and zero-index $idx
idx = (idx.value) - 1
# cast list to a ruby array
list = list.to_a
# remove or replace the given value
list.delete_at(idx) if value.nil?
list[idx,1] = value if not value.nil?
return Sass::Script::List.new(list, separator)
end
Sass::Script::Functions.declare :list_replace, [:list, :idx]
Sass::Script::Functions.declare :list_replace, [:list, :idx, :value]
Sass::Script::Functions.declare :list_replace, [:list, :idx, :value, :separator]
#
# remove an index from a list
#
# *Parameters*:
# - $list {List} the list to remove from
# - $idx {Number} the index to remove
# - $separator {String} the separator to use [auto|comma|space]
# *Returns*:
# - {List} the list with removed index
#
def list_remove(list, idx = false, separator = nil)
return list_replace(list, idx, nil, separator)
end
Sass::Script::Functions.declare :list_remove, [:list, :idx]
Sass::Script::Functions.declare :list_remove, [:list, :idx, :separator]
#
# insert an item into a list
#
# *Parameters*:
# - $list {List} the list to insert into
# - $idx {Number} the index to insert at
# - $value {\*} the value to insert
# - $separator {String} the separator to use [auto|comma|space]
# *Returns*:
# - {List} the list with inserted value
#
def list_insert(list, idx = false, value = nil, separator = nil)
value = nil if value == Sass::Script::String.new('nil')
# return early if the index is invalid (no operation) or $value is `nil`
return list if (not idx or idx == Sass::Script::Bool.new(false)) or value.nil?
return list_replace(list, idx, value, separator, -1)
end
Sass::Script::Functions.declare :list_insert, [:list, :idx]
Sass::Script::Functions.declare :list_insert, [:list, :idx, :value]
Sass::Script::Functions.declare :list_insert, [:list, :idx, :value, :separator]
#
# add values(s) to a list
#
# *Parameters*:
# - $list {List} the list operate on
# - $values {List|Number|String} the value(s) to add to the list
# *Returns*:
# - {List} the final list
#
def list_add(list, values)
return list_math(list, values, :plus)
end
#
# subtract values(s) from a list
#
# *Parameters*:
# - $list {List} the list operate on
# - $values {List|Number|String} the value(s) to subtract from the list
# *Returns*:
# - {List} the final list
#
def list_subtract(list, values)
return list_math(list, values, :minus)
end
#
# multiply values(s) into a list
#
# *Parameters*:
# - $list {List} the list operate on
# - $values {List|Number|String} the value(s) to multiply into the list
# *Returns*:
# - {List} the final list
#
def list_multiply(list, values)
return list_math(list, values, :times)
end
#
# divide values(s) into a list
#
# *Parameters*:
# - $list {List} the list operate on
# - $values {List|Number|String} the value(s) to divide into the list
# *Returns*:
# - {List} the final list
#
def list_divide(list, values)
return list_math(list, values, :div)
end
#
# list modulus value(s)
#
# *Parameters*:
# - $list {List} the list operate on
# - $values {List|Number|String} the value(s) to modulus into the list
# *Returns*:
# - {List} the final list
#
def list_mod(list, values)
return list_math(list, values, :mod)
end
#
# find if any set of values is in a list
# this is similar to `index()`, but allows $values to contain multiple values to test against
#
# *Parameters*:
# - $haystack {List} input list
# - $needle {List} the value(s) to search for
# *Returns*:
# - {Number|Bool} if an item is found, returns the index, otherwise returns false
#
def index2(haystack, needle)
haystack = haystack.to_a
needle = needle.to_a
index = haystack.index(haystack.detect { |i| needle.include?(i) })
if index
return Sass::Script::Number.new(index + 1)
else
return Sass::Script::Bool.new(false)
end
end
#
# treats a list cyclically (never out of bounds, just wraps around)
#
# *Parameters*:
# - $list {List} the list
# - $idx {Number} the index
# *Returns*:
# - {*} the nth item in the List
#
def nth_cyclic(list, n = 1)
n = n.to_i if n.is_a?(Sass::Script::Number)
list = list.to_a
return list[(n - 1) % list.size]
end
#
# find a key within a nested list of ordered pairs
#
# *Parameters*:
# - $list {List} the list to search in
# - $key {String} the key identifier (name)
# - $strict {Bool} if true, does a strict match against the key
# *Returns*:
# - {*} the data associated with $key
#
def associative(list, key, strict = false)
separator = list.separator if list.is_a?(Sass::Script::List)
list = helpers.list_to_hash(list)
item = list[helpers.to_str(key, ' ' , :quotes)]
item ||= list.first[1] if not strict
return Sass::Script::List.new([], separator) if item.nil?
return helpers.hash_to_list(item, 0, separator) if item.is_a?(Array) or item.is_a?(Hash)
# no conversion needed, so just return
return item
end
Sass::Script::Functions.declare :associative, [:list, :key]
Sass::Script::Functions.declare :associative, [:list, :key, :strict]
#
# extend a key-value paired list with another
#
# *Parameters*:
# - $list {List} the list to extend to
# - $extender {List} the list to extend with
# *Returns*:
# - $list {List} the extended list
#
def associative_merge(list, extender, kwargs = {})
separator = list.separator if list.is_a?(Sass::Script::List)
list = helpers.list_to_hash(list)
extender = helpers.list_to_hash(extender)
list = list.rmerge(extender)
return helpers.hash_to_list(list, 0, separator)
end
Sass::Script::Functions.declare :associative_merge, [:list, :extender]
#
# given a string of styles, convert it into a key-value pair list
#
# *Parameters*:
# - $string {String} the string to convert
# *Returns*:
# - $list {List} the converted list of styles
#
def _style_string_to_list(string = '')
# convert to string and strip all comments
string = helpers.to_str(string, ' ').gsub(/\/\*[^\*\/]*\*\//, '')
# then split it on each rule
tmp = string.split(';')
styles = []
# and for each rule break it into it's key-value pairs
tmp.each do |rule|
kvp = []
rule.split(':').each do |str|
kvp.push Sass::Script::String.new(str)
end
styles.push Sass::Script::List.new(kvp, :comma)
end
# the recompose the list
return Sass::Script::List.new(styles, :comma)
end
private
def helpers
@helpers ||= Archetype::Functions::Helpers
end
#
# perform math operations on a list
#
# *Parameters*:
# - list {Sass::List} the list operate on
# - values {Sass::List|Sass::Number|Sass::String} the value(s) perform with
# - method {Symbol} the method to perform [:plus|:minus|:times|:div|:mod]
# *Returns*:
# - {Sass::List} the final list
#
def list_math(list, values, method = :plus)
separator = list.separator if list.is_a?(Sass::Script::List)
values = values.to_a
list = list.to_a
values.fill(values[0], 0..(list.size - 1)) if values.size < list.size
list = [list, values].transpose.map do |x|
case method
when :plus
x[0].plus(x[1])
when :minus
x[0].minus(x[1])
when :times
x[0].times(x[1])
when :div
x[0].div(x[1])
when :mod
x[0].mod(x[1])
end
end
return Sass::Script::List.new(list, separator)
end
end