102 lines
3.2 KiB
Ruby
102 lines
3.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'task_list/filter'
|
|
|
|
# Generated HTML is transformed back to GFM by:
|
|
# - app/assets/javascripts/behaviors/markdown/nodes/ordered_task_list.js
|
|
# - app/assets/javascripts/behaviors/markdown/nodes/task_list.js
|
|
# - app/assets/javascripts/behaviors/markdown/nodes/task_list_item.js
|
|
module Banzai
|
|
module Filter
|
|
# TaskList filter replaces task list item markers (`[ ]`, `[x]`, and `[~]`)
|
|
# with checkboxes, marked up with metadata and behavior.
|
|
#
|
|
# This should be run on the HTML generated by the Markdown filter, after the
|
|
# SanitizationFilter.
|
|
#
|
|
# Syntax
|
|
# ------
|
|
#
|
|
# Task list items must be in a list format:
|
|
#
|
|
# ```
|
|
# - [ ] incomplete
|
|
# - [x] complete
|
|
# - [~] inapplicable
|
|
# ```
|
|
#
|
|
# This class overrides TaskList::Filter in the `deckar01-task_list` gem
|
|
# to add support for inapplicable task items
|
|
class TaskListFilter < TaskList::Filter
|
|
extend ::Gitlab::Utils::Override
|
|
|
|
XPATH = 'descendant-or-self::li[input[@data-inapplicable]] | descendant-or-self::li[p[input[@data-inapplicable]]]'
|
|
INAPPLICABLE = '[~]'
|
|
INAPPLICABLEPATTERN = /\[~\]/.freeze
|
|
|
|
# Pattern used to identify all task list items.
|
|
# Useful when you need iterate over all items.
|
|
NEWITEMPATTERN = /
|
|
^
|
|
(?:\s*[-+*]|(?:\d+\.))? # optional list prefix
|
|
\s* # optional whitespace prefix
|
|
( # checkbox
|
|
#{CompletePattern}|
|
|
#{IncompletePattern}|
|
|
#{INAPPLICABLEPATTERN}
|
|
)
|
|
(?=\s) # followed by whitespace
|
|
/x.freeze
|
|
|
|
# Force the gem's constant to use our new one
|
|
superclass.send(:remove_const, :ItemPattern) # rubocop: disable GitlabSecurity/PublicSend
|
|
superclass.const_set(:ItemPattern, NEWITEMPATTERN)
|
|
|
|
def inapplicable?(item)
|
|
!!(item.checkbox_text =~ INAPPLICABLEPATTERN)
|
|
end
|
|
|
|
override :render_item_checkbox
|
|
def render_item_checkbox(item)
|
|
%(<task-button></task-button><input type="checkbox"
|
|
class="task-list-item-checkbox"
|
|
#{'checked="checked"' if item.complete?}
|
|
#{'data-inapplicable' if inapplicable?(item)}
|
|
disabled="disabled"/>)
|
|
end
|
|
|
|
override :render_task_list_item
|
|
def render_task_list_item(item)
|
|
source = item.source
|
|
|
|
if inapplicable?(item)
|
|
# Add a `<s>` tag around the list item text. However because of the
|
|
# way tasks are built, the source can include an embedded sublist, like
|
|
# `[~] foobar\n<ol><li....`
|
|
# The `<s>` should only be added to the main text.
|
|
source = source.partition("#{INAPPLICABLE} ")
|
|
text = source.last.partition(/\<(ol|ul)/)
|
|
text[0] = "<s>#{text[0]}</s>"
|
|
source[-1] = text.join
|
|
source = source.join
|
|
end
|
|
|
|
Nokogiri::HTML.fragment \
|
|
source.sub(ItemPattern, render_item_checkbox(item)), 'utf-8'
|
|
end
|
|
|
|
override :call
|
|
def call
|
|
super
|
|
|
|
# add class to li for any inapplicable checkboxes
|
|
doc.xpath(XPATH).each do |li|
|
|
li.add_class('inapplicable')
|
|
end
|
|
|
|
doc
|
|
end
|
|
end
|
|
end
|
|
end
|