lib/under_os/page/matcher.rb in under-os-1.1.0 vs lib/under_os/page/matcher.rb in under-os-1.2.0
- old
+ new
@@ -4,55 +4,92 @@
@cache ||= {}
@cache[css_rule] ||= super
end
def initialize(css_rule)
- css_rule = css_rule.strip
- rules = css_rule.scan(/([\S]+)/).map(&:first)
- @rule = parse(rules.pop)
- @parent = rules.size == 0 ? false : self.class.new(rules.join(' '))
+ css_rules = css_rule.strip.split(/\s*,\s*/)
+
+ if css_rules.size == 1
+ rules = css_rules[0].scan(/([\S]+)/).map(&:first)
+ @rule = parse(rules.pop)
+ @parent = rules.size == 0 ? false : self.class.new(rules.join(' '))
+ else
+ @multiple_matchers = css_rules.map{ |rule| self.class.new(rule) }
+ end
end
def match(view)
score_for(view) != 0
end
def score_for(view)
+ return summary_score_for(view) if @multiple_matchers
+
id_score = id_score_for(view)
tag_score = tag_score_for(view)
class_score = class_score_for(view)
+ attrs_score = attrs_score_for(view)
+ total_score = id_score + tag_score + class_score + attrs_score
- return 0 if @rule[:id] && id_score == 0
- return 0 if @rule[:tag] && tag_score == 0
- return 0 if @rule[:classes].size > 0 && class_score == 0
- return 0 if (this_score = id_score + tag_score + class_score) == 0
+ total_score = 0 if id_score == 0 && @rule[:id]
+ total_score = 0 if tag_score == 0 && @rule[:tag]
+ total_score = 0 if class_score == 0 && @rule[:classes].size != 0
+ total_score = 0 if attrs_score == 0 && @rule[:attrs].size != 0
- parent_score = parent_score_for(view)
+ if @parent && total_score != 0
+ parent_score = parent_score_for(view)
+ total_score = parent_score == 0 ? 0 : total_score + parent_score
+ end
- return 0 if @parent && parent_score == 0
-
- parent_score + this_score
+ total_score
end
private
+ def summary_score_for(view)
+ @multiple_matchers.map{ |matcher| matcher.score_for(view) }.max
+ end
+
def id_score_for(view)
@rule[:id] == view.id ? 1 : 0
end
def tag_score_for(view)
- @rule[:tag] == view.tagName ? 1 : 0
+ @rule[:tag] == '*' || @rule[:tag] == view.tagName ? 1 : 0
end
def class_score_for(view)
+ return 0 if @rule[:classes].size == 0
match = @rule[:classes] & view.classNames
match.size == @rule[:classes].size ? match.size : 0
end
- def parent_score_for(view)
- return 0 if ! @parent
+ def attrs_score_for(view)
+ score = 0; return 0 if @rule[:attrs].size == 0
+ @rule[:attrs].each do |key, value, operand|
+ attr = view.respond_to?(key) && view.__send__(key)
+ attr = attr.to_s if attr.is_a?(Symbol) || attr.is_a?(Numeric)
+
+ if attr && attr.is_a?(String)
+ matches = case operand
+ when '=' then attr == value
+ when '*=' then attr.include?(value)
+ when '^=' then attr.starts_with?(value)
+ when '$=' then attr.ends_with?(value)
+ when '~=' then attr.split(' ').include?(value)
+ when '|=' then attr.split('-').include?(value)
+ end
+
+ score += 1 if matches
+ end
+ end
+
+ score
+ end
+
+ def parent_score_for(view)
parent = view; score = 0
while parent = parent.parent
score = @parent.score_for(parent)
break if score > 0
@@ -61,11 +98,11 @@
score
end
def parse(string)
{}.tap do |rule|
- if m = string.match(/^([a-z]+)/)
+ if m = string.match(/^([a-z\*]+)/)
rule[:tag] = m[1].upcase
else
rule[:tag] = false # so it wouldn't match nil
end
@@ -75,9 +112,17 @@
rule[:id] = false # so it wouldn't match nil
end
if m = string.scan(/(\.)([a-z_\-0-9]+)/)
rule[:classes] = m.map{|c| c[1]}
+ else
+ rule[:classes] = []
+ end
+
+ if m = string.scan(/\[\s*([a-z]+)\s*([*^$~|]{0,1}=)\s*('|")?(.+?)(\3)?\s*\]/)
+ rule[:attrs] = m.map{|c| [c[0], c[3], c[1]] }
+ else
+ rule[:attrs] = []
end
end
end
end