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