Files
postcss/lib/node.coffee
T

153 lines
3.3 KiB
CoffeeScript

Raw = require('./raw')
# Recursivly clone objects
clone = (obj, parent) ->
return obj unless typeof(obj) == 'object'
cloned = new obj.constructor()
for own name, value of obj
if name == 'parent' and typeof(value) == 'object'
cloned[name] = parent if parent
else if value instanceof Array
cloned[name] = value.map (i) -> clone(i, cloned)
else
cloned[name] = clone(value, cloned)
cloned
# Is `obj` has all keys from `keys`. Return `false` of object with keys from
# `keys` and values from `obj`.
keys = (obj, keys) ->
all = { }
for key of keys
if obj[key]?
all[key] = obj[key]
else
return false
all
# Some common methods for all CSS nodes
class Node
constructor: (defaults = { }) ->
for name, value of defaults
@[name] = value
# Syntax sugar to create getter/setter
@prop: (name, params) ->
Object.defineProperty(@prototype, name, params)
# Create property with trimmed and raw value (with comments and spaces)
@raw: (name) ->
hidden = '_' + name
@prop name,
get: ->
prop = @[hidden]
if prop instanceof Raw
prop.value
else
prop
set: (value) ->
if value instanceof Raw
@[hidden] = value
else
@[hidden] = value
# Remove this node from parent.
#
# decl.removeSelf()
#
# Note, that removing by index is faster:
#
# rule.each (decl, i) ->
# rule.remove(i)
removeSelf: ->
return unless @parent
@parent.remove(@)
this
# Return CSS string of current node.
#
# decl.toString() #=> " color: black"
toString: ->
result = ''
builder = (str) -> result += str
@stringify(builder)
result
# Clone current node.
#
# rule.append decl.clone()
#
# You can override properties while cloning:
#
# rule.append decl.clone(value: '0')
clone: (overrides = { }) ->
cloned = clone(@)
for name, value of overrides
cloned[name] = value
cloned
# Remove `parent` node on cloning to fix circular structures
toJSON: ->
fixed = { }
for own name, value of this
continue if name == 'parent'
fixed[name] = if value instanceof Array
value.map (i) ->
if typeof(i) == 'object' and i.toJSON then i.toJSON() else i
else if typeof(value) == 'object' and value.toJSON
value.toJSON()
else
value
fixed
# Default code style
defaultStyle: -> { }
# Allow to split node with same type by other critera.
# For example, to use different style for bodiless at-rules.
styleType: -> @type
# Copy code style from first node with same type
style: ->
type = @styleType()
defaults = @defaultStyle(type)
all = keys(@, defaults)
return all if all
style = defaults
if @parent
root = @
root = root.parent while root.parent
root.styleCache ||= { }
if root.styleCache[type]
style = root.styleCache[type]
else
root.eachInside (another) ->
return if another.styleType() != type
return if @ == another
all = keys(another, style)
if all
style = all
return false
root.styleCache[type] = style
merge = { }
for key of style
merge[key] = if @[key]? then @[key] else style[key]
merge
module.exports = Node