# # This module provides a set of Sass functions for working with Sass::Script::Value::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 (!idx || helpers.is_null(idx) || idx.value == false) separator ||= list.separator if list.is_a?(Sass::Script::Value::List) # cast and zero-index $idx idx = (idx.value) - 1 # cast list to a ruby array list = list.to_a.dup # remove or replace the given value if value.nil? or value == null list.delete_at(idx) else list[idx] = value end return Sass::Script::Value::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 to 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 Sass::Script::Functions.declare :list_add, [:list, :values] # # subtract values(s) from a list # # *Parameters*: # - $list {List} the list operate to 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 Sass::Script::Functions.declare :list_subtract, [:list, :values] # # multiply values(s) into a list # # *Parameters*: # - $list {List} the list operate to 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 Sass::Script::Functions.declare :list_multiply, [:list, :values] # # divide values(s) into a list # # *Parameters*: # - $list {List} the list operate to 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 Sass::Script::Functions.declare :list_divide, [:list, :values] # # list modulus value(s) # # *Parameters*: # - $list {List} the list to 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 Sass::Script::Functions.declare :list_mod, [:list, :values] # # joins a list into a string with the separator given # # *Parameters*: # - $list {List} the list operate to on # - $separator {String} the separator to insert between each item # *Returns*: # - {String} string conversions of all list item joined into one string # def list_join(list, separator = ', ') list = list.to_a separator = (separator.respond_to?(:value) ? separator.value : separator).to_s return identifier(list.join(separator)) end Sass::Script::Functions.declare :list_join, [:list, :separator] # # 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|Boolean} 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 number(index + 1) else return Sass::Script::Bool.new(false) end end Sass::Script::Functions.declare :index2, [:haystack, :needle] # # 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 = 1 if helpers.is_null(n) n = n.to_i if n.is_a?(Sass::Script::Value::Number) list = list.to_a return list[(n - 1) % list.size] end Sass::Script::Functions.declare :nth_cyclic, [:list] Sass::Script::Functions.declare :nth_cyclic, [:list, :n] # # 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 {Boolean} 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::Value::List) list = helpers.list_to_hash(list) item = list[helpers.to_str(key, ' ' , :quotes)] item ||= list.first[1] if not strict return Sass::Script::Value::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::Value::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] # # map collection items to conform to a well defined collection # this is primarily used to convert shorthand notations into symmetrical longhand notations # # *Parameters*: # - $list {List} input list # - $components {List} list of components # - $min {List} the minimum length of the collection # *Returns*: # - {List} formatted collection # def get_collection(list = bool(false), components = [], min = number(1)) list = list.value ? list.to_a : components.to_a while(list.length < min.value) list = list.concat(list) end return list(list, :space) end Sass::Script::Functions.declare :get_collection, [:list] Sass::Script::Functions.declare :get_collection, [:components] Sass::Script::Functions.declare :get_collection, [:list, :min] Sass::Script::Functions.declare :get_collection, [:components, :min] Sass::Script::Functions.declare :get_collection, [:list, :components, :min] # Returns a list object from a value that was passed. # This can be used to unpack a space separated list that got turned # into a string by sass before it was passed to a mixin. # this is shamelessly stolen from Compass :) # # *Parameters*: # - $arg {*} the item to cast to a list # *Returns*: # - $list {List} the item as a list # def _archetype_list(arg) # if it's already a list, just return it return arg if arg.is_a?(Sass::Script::Value::List) # otherwise, we'll try to cast it return list(arg, :space) end # # 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 identifier(str) end styles.push Sass::Script::Value::List.new(kvp, :comma) end # then recompose the list return Sass::Script::Value::List.new(styles, :comma) end private # # perform math operations on a list # # *Parameters*: # - list {Sass::Script::Value::List} the list operate to on # - values {Sass::Script::Value::List|Sass::Script::Value::Number|Sass::Script::Value::String} the value(s) perform with # - method {Symbol} the method to perform [:plus|:minus|:times|:div|:mod] # *Returns*: # - {Sass::Script::Value::List} the final list # def list_math(list, values, method = :plus) separator = list.separator if list.is_a?(Sass::Script::Value::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| x[0].method(method).call(x[1]) end return Sass::Script::Value::List.new(list, separator) end end