2019-03-02 22:35:43 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
# Finds the correct checkbox in the passed in markdown/html and toggles it's state,
|
|
|
|
# returning the updated markdown/html.
|
|
|
|
# We don't care if the text has changed above or below the specific checkbox, as long
|
|
|
|
# the checkbox still exists at exactly the same line number and the text is equal.
|
|
|
|
# If successful, new values are available in `updated_markdown` and `updated_markdown_html`
|
|
|
|
class TaskListToggleService
|
|
|
|
attr_reader :updated_markdown, :updated_markdown_html
|
|
|
|
|
|
|
|
def initialize(markdown, markdown_html, line_source:, line_number:, toggle_as_checked:)
|
2021-04-29 21:17:54 +05:30
|
|
|
@markdown = markdown
|
|
|
|
@markdown_html = markdown_html
|
|
|
|
@line_source = line_source
|
|
|
|
@line_number = line_number
|
|
|
|
@toggle_as_checked = toggle_as_checked
|
2019-03-02 22:35:43 +05:30
|
|
|
|
|
|
|
@updated_markdown, @updated_markdown_html = nil
|
|
|
|
end
|
|
|
|
|
|
|
|
def execute
|
|
|
|
return false unless markdown && markdown_html
|
|
|
|
|
|
|
|
toggle_markdown && toggle_markdown_html
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
attr_reader :markdown, :markdown_html, :toggle_as_checked
|
|
|
|
attr_reader :line_source, :line_number
|
|
|
|
|
|
|
|
def toggle_markdown
|
|
|
|
source_lines = markdown.split("\n")
|
|
|
|
source_line_index = line_number - 1
|
|
|
|
markdown_task = source_lines[source_line_index]
|
|
|
|
|
|
|
|
# The source in the DB could be using either \n or \r\n line endings
|
2019-07-07 11:18:12 +05:30
|
|
|
return unless markdown_task.chomp == line_source
|
2019-03-02 22:35:43 +05:30
|
|
|
return unless source_checkbox = Taskable::ITEM_PATTERN.match(markdown_task)
|
|
|
|
|
2022-04-04 11:22:00 +05:30
|
|
|
currently_checked = TaskList::Item.new(source_checkbox[2]).complete?
|
2019-03-02 22:35:43 +05:30
|
|
|
|
|
|
|
# Check `toggle_as_checked` to make sure we don't accidentally replace
|
|
|
|
# any `[ ]` or `[x]` in the middle of the text
|
|
|
|
if currently_checked
|
|
|
|
markdown_task.sub!(Taskable::COMPLETE_PATTERN, '[ ]') unless toggle_as_checked
|
2023-03-04 22:38:38 +05:30
|
|
|
elsif toggle_as_checked
|
|
|
|
markdown_task.sub!(Taskable::INCOMPLETE_PATTERN, '[x]')
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
source_lines[source_line_index] = markdown_task
|
|
|
|
@updated_markdown = source_lines.join("\n")
|
|
|
|
end
|
|
|
|
|
|
|
|
def toggle_markdown_html
|
|
|
|
html = Nokogiri::HTML.fragment(markdown_html)
|
|
|
|
html_checkbox = get_html_checkbox(html)
|
|
|
|
return unless html_checkbox
|
|
|
|
|
|
|
|
if toggle_as_checked
|
|
|
|
html_checkbox[:checked] = 'checked'
|
|
|
|
else
|
|
|
|
html_checkbox.remove_attribute('checked')
|
|
|
|
end
|
|
|
|
|
|
|
|
@updated_markdown_html = html.to_html
|
|
|
|
end
|
|
|
|
|
|
|
|
# When using CommonMark, we should be able to use the embedded `sourcepos` attribute to
|
|
|
|
# target the exact line in the DOM.
|
|
|
|
def get_html_checkbox(html)
|
|
|
|
html.css(".task-list-item[data-sourcepos^='#{line_number}:'] input.task-list-item-checkbox").first
|
|
|
|
end
|
|
|
|
end
|