@@ -47,20 +47,16 @@ class Filter < HTML::Pipeline::Filter
4747 (?=\s ) # followed by whitespace
4848 /x
4949
50- ListSelector = [
51- # select UL/OL
52- ".//li[starts-with(text(),'[ ]')]/.." ,
53- ".//li[starts-with(text(),'[x]')]/.." ,
54- # and those wrapped in Ps
55- ".//li/p[1][starts-with(text(),'[ ]')]/../.." ,
56- ".//li/p[1][starts-with(text(),'[x]')]/../.."
57- ] . join ( ' | ' ) . freeze
58-
59- # Selects all LIs from a TaskList UL/OL
60- ItemSelector = ".//li" . freeze
50+ ListItemSelector = ".//li[task_list_item(.)]" . freeze
51+
52+ class XPathSelectorFunction
53+ def self . task_list_item ( nodes )
54+ nodes if nodes . text =~ ItemPattern
55+ end
56+ end
6157
6258 # Selects first P tag of an LI, if present
63- ItemParaSelector = ".// p[1]" . freeze
59+ ItemParaSelector = "./p[1]" . freeze
6460
6561 # List of `TaskList::Item` objects that were recognized in the document.
6662 # This is available in the result hash as `:task_list_items`.
@@ -100,20 +96,22 @@ def render_task_list_item(item)
10096 #
10197 # Returns an Array of Nokogiri::XML::Element objects for ordered and
10298 # unordered lists.
103- def task_lists
104- doc . xpath ( ListSelector )
99+ def list_items
100+ doc . xpath ( ListItemSelector , XPathSelectorFunction )
105101 end
106102
107- # Public: filters a Nokogiri::XML::Element ordered/unordered list, marking
108- # up the list items in order to add behavior and include metadata.
103+ # Filters the source for task list items.
109104 #
110- # Modifies the provided node.
105+ # Each item is wrapped in HTML to identify, style, and layer
106+ # useful behavior on top of.
107+ #
108+ # Modifications apply to the parsed document directly.
111109 #
112110 # Returns nothing.
113- def filter_list ( node )
114- add_css_class ( node , 'task-list' )
111+ def filter!
112+ list_items . reverse . each do |li |
113+ add_css_class ( li . parent , 'task-list' )
115114
116- node . xpath ( ItemSelector ) . each do |li |
117115 outer , inner =
118116 if p = li . xpath ( ItemParaSelector ) [ 0 ]
119117 [ p , p . inner_html ]
@@ -122,28 +120,15 @@ def filter_list(node)
122120 end
123121 if match = ( inner . chomp =~ ItemPattern && $1)
124122 item = TaskList ::Item . new ( match , inner )
125- task_list_items << item
123+ # prepend because we're iterating in reverse
124+ task_list_items . unshift item
126125
127126 add_css_class ( li , 'task-list-item' )
128127 outer . inner_html = render_task_list_item ( item )
129128 end
130129 end
131130 end
132131
133- # Filters the source for task list items.
134- #
135- # Each item is wrapped in HTML to identify, style, and layer
136- # useful behavior on top of.
137- #
138- # Modifications apply to the parsed document directly.
139- #
140- # Returns nothing.
141- def filter!
142- task_lists . each do |node |
143- filter_list node
144- end
145- end
146-
147132 def call
148133 filter!
149134 doc
@@ -153,6 +138,7 @@ def call
153138 # names.
154139 def add_css_class ( node , *new_class_names )
155140 class_names = ( node [ 'class' ] || '' ) . split ( ' ' )
141+ return if new_class_names . all? { |klass | class_names . include? ( klass ) }
156142 class_names . concat ( new_class_names )
157143 node [ 'class' ] = class_names . uniq . join ( ' ' )
158144 end
0 commit comments