PageRenderTime 120ms CodeModel.GetById 93ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

/scalate-jruby/src/main/resources/haml-3.0.25/lib/sass/tree/prop_node.rb

http://github.com/scalate/scalate
Ruby | 220 lines | 116 code | 27 blank | 77 comment | 38 complexity | 93d0ce079153b3817540c694f7be9c8a MD5 | raw file
  1module Sass::Tree
  2  # A static node reprenting a CSS property.
  3  #
  4  # @see Sass::Tree
  5  class PropNode < Node
  6    # The name of the property,
  7    # interspersed with {Sass::Script::Node}s
  8    # representing `#{}`-interpolation.
  9    # Any adjacent strings will be merged together.
 10    #
 11    # @return [Array<String, Sass::Script::Node>]
 12    attr_accessor :name
 13
 14    # The name of the property
 15    # after any interpolated SassScript has been resolved.
 16    # Only set once \{Tree::Node#perform} has been called.
 17    #
 18    # @return [String]
 19    attr_accessor :resolved_name
 20
 21    # The value of the property.
 22    #
 23    # @return [Sass::Script::Node]
 24    attr_accessor :value
 25
 26    # The value of the property
 27    # after any interpolated SassScript has been resolved.
 28    # Only set once \{Tree::Node#perform} has been called.
 29    #
 30    # @return [String]
 31    attr_accessor :resolved_value
 32
 33    # How deep this property is indented
 34    # relative to a normal property.
 35    # This is only greater than 0 in the case that:
 36    #
 37    # * This node is in a CSS tree
 38    # * The style is :nested
 39    # * This is a child property of another property
 40    # * The parent property has a value, and thus will be rendered
 41    #
 42    # @return [Fixnum]
 43    attr_accessor :tabs
 44
 45    # @param name [Array<String, Sass::Script::Node>] See \{#name}
 46    # @param value [Sass::Script::Node] See \{#value}
 47    # @param prop_syntax [Symbol] `:new` if this property uses `a: b`-style syntax,
 48    #   `:old` if it uses `:a b`-style syntax
 49    def initialize(name, value, prop_syntax)
 50      @name = Haml::Util.strip_string_array(
 51        Haml::Util.merge_adjacent_strings(name))
 52      @value = value
 53      @tabs = 0
 54      @prop_syntax = prop_syntax
 55      super()
 56    end
 57
 58    # Compares the names and values of two properties.
 59    #
 60    # @param other [Object] The object to compare with
 61    # @return [Boolean] Whether or not this node and the other object
 62    #   are the same
 63    def ==(other)
 64      self.class == other.class && name == other.name && value == other.value && super
 65    end
 66
 67    # Returns a appropriate message indicating how to escape pseudo-class selectors.
 68    # This only applies for old-style properties with no value,
 69    # so returns the empty string if this is new-style.
 70    #
 71    # @return [String] The message
 72    def pseudo_class_selector_message
 73      return "" if @prop_syntax == :new || !value.is_a?(Sass::Script::String) || !value.value.empty?
 74      "\nIf #{declaration.dump} should be a selector, use \"\\#{declaration}\" instead."
 75    end
 76
 77    protected
 78
 79    # @see Node#to_src
 80    def to_src(tabs, opts, fmt)
 81      res = declaration(tabs, opts, fmt)
 82      return res + "#{semi fmt}\n" if children.empty?
 83      res + children_to_src(tabs, opts, fmt).rstrip + semi(fmt) + "\n"
 84    end
 85
 86    # Computes the CSS for the property.
 87    #
 88    # @param tabs [Fixnum] The level of indentation for the CSS
 89    # @return [String] The resulting CSS
 90    def _to_s(tabs)
 91      to_return = '  ' * (tabs - 1 + self.tabs) + resolved_name + ":" +
 92        (style == :compressed ? '' : ' ') + resolved_value + (style == :compressed ? "" : ";")
 93    end
 94
 95    # Converts nested properties into flat properties.
 96    #
 97    # @param extends [Haml::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
 98    #   The extensions defined for this tree
 99    # @param parent [PropNode, nil] The parent node of this node,
100    #   or nil if the parent isn't a {PropNode}
101    # @raise [Sass::SyntaxError] if the property uses invalid syntax
102    def _cssize(extends, parent)
103      node = super
104      result = node.children.dup
105      if !node.resolved_value.empty? || node.children.empty?
106        node.send(:check!)
107        result.unshift(node)
108      end
109      result
110    end
111
112    # Updates the name and indentation of this node based on the parent name
113    # and nesting level.
114    #
115    # @param extends [Haml::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
116    #   The extensions defined for this tree
117    # @param parent [PropNode, nil] The parent node of this node,
118    #   or nil if the parent isn't a {PropNode}
119    def cssize!(extends, parent)
120      self.resolved_name = "#{parent.resolved_name}-#{resolved_name}" if parent
121      self.tabs = parent.tabs + (parent.resolved_value.empty? ? 0 : 1) if parent && style == :nested
122      super
123    end
124
125    # Runs any SassScript that may be embedded in the property,
126    # and invludes the parent property, if any.
127    #
128    # @param environment [Sass::Environment] The lexical environment containing
129    #   variable and mixin values
130    def perform!(environment)
131      @resolved_name = run_interp(@name, environment)
132      val = @value.perform(environment)
133      @resolved_value =
134        if @value.context == :equals && val.is_a?(Sass::Script::String)
135          val.value
136        else
137          val.to_s
138        end
139      super
140    end
141
142    # Returns an error message if the given child node is invalid,
143    # and false otherwise.
144    #
145    # {PropNode} only allows other {PropNode}s and {CommentNode}s as children.
146    # @param child [Tree::Node] A potential child node
147    # @return [String] An error message if the child is invalid, or nil otherwise
148    def invalid_child?(child)
149      if !child.is_a?(PropNode) && !child.is_a?(CommentNode)
150        "Illegal nesting: Only properties may be nested beneath properties."
151      end
152    end
153
154    private
155
156    def check!
157      if @options[:property_syntax] == :old && @prop_syntax == :new
158        raise Sass::SyntaxError.new("Illegal property syntax: can't use new syntax when :property_syntax => :old is set.")
159      elsif @options[:property_syntax] == :new && @prop_syntax == :old
160        raise Sass::SyntaxError.new("Illegal property syntax: can't use old syntax when :property_syntax => :new is set.")
161      elsif resolved_value.empty?
162        raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no value)." +
163          pseudo_class_selector_message)
164      end
165    end
166
167    def declaration(tabs = 0, opts = {:old => @prop_syntax == :old}, fmt = :sass)
168      name = self.name.map {|n| n.is_a?(String) ? n : "\#{#{n.to_sass(opts)}}"}.join
169      if name[0] == ?:
170        raise Sass::SyntaxError.new("The \"#{name}: #{self.class.val_to_sass(value, opts)}\" hack is not allowed in the Sass indented syntax")
171      end
172
173      old = opts[:old] && fmt == :sass
174      initial = old ? ':' : ''
175      mid = old ? '' : ':'
176      "#{'  ' * tabs}#{initial}#{name}#{mid} #{self.class.val_to_sass(value, opts)}".rstrip
177    end
178
179    class << self
180      # @private
181      def val_to_sass(value, opts)
182        val_to_sass_comma(value, opts).to_sass(opts)
183      end
184
185      private
186
187      def val_to_sass_comma(node, opts)
188        return node unless node.is_a?(Sass::Script::Operation)
189        return val_to_sass_concat(node, opts) unless node.operator == :comma
190
191        Sass::Script::Operation.new(
192          val_to_sass_concat(node.operand1, opts),
193          val_to_sass_comma(node.operand2, opts),
194          node.operator)
195      end
196
197      def val_to_sass_concat(node, opts)
198        return node unless node.is_a?(Sass::Script::Operation)
199        return val_to_sass_div(node, opts) unless node.operator == :concat
200
201        Sass::Script::Operation.new(
202          val_to_sass_div(node.operand1, opts),
203          val_to_sass_concat(node.operand2, opts),
204          node.operator)
205      end
206
207      def val_to_sass_div(node, opts)
208        unless node.is_a?(Sass::Script::Operation) && node.operator == :div &&
209            node.operand1.is_a?(Sass::Script::Number) &&
210            node.operand2.is_a?(Sass::Script::Number) &&
211            (node.context == :equals || !node.operand1.original || !node.operand2.original)
212          return node
213        end
214
215        Sass::Script::String.new("(#{node.to_sass(opts)})")
216      end
217
218    end
219  end
220end