task-list-1.0.2/0000755000076400007640000000000012545214714012466 5ustar pravipravitask-list-1.0.2/bower.json0000644000076400007640000000065412545214714014504 0ustar pravipravi{ "name": "task_list", "version": "1.0.2", "dependencies": { "jquery": ">= 1.9.1" }, "devDependencies": { "qunit": ">= 1.0" }, "main": [ "app/assets/javascripts/task_list.coffee", "app/assets/stylesheets/task_list.scss" ], "ignore": [ ".gitignore", ".travis.yml", "*.gemspec", "*.md", "config.ru", "Gemfile", "lib/", "Rakefile", "script/", "test/" ] } task-list-1.0.2/LICENSE0000644000076400007640000000206712545214714013500 0ustar pravipraviThe MIT License (MIT) Copyright (c) 2014 GitHub, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. task-list-1.0.2/.travis.yml0000644000076400007640000000020512545214714014574 0ustar pravipravilanguage: ruby install: ./script/bootstrap script: ./script/cibuild rvm: - 1.9.3 - 2.0.0 - 2.1.1 notifications: email: false task-list-1.0.2/script/0000755000076400007640000000000012545214714013772 5ustar pravipravitask-list-1.0.2/script/bootstrap0000755000076400007640000000042612545214714015737 0ustar pravipravi#!/bin/sh set -e 0 if ! bundle check 1>/dev/null 2>&1; then bundle install --no-color --binstubs --path vendor/gems fi if ! npm list bower 2>&1 | grep 0.8.5 >/dev/null; then # npm install bower npm install git://github.com/twitter/bower.git fi bower install --no-color task-list-1.0.2/script/cibuild0000755000076400007640000000014712545214714015335 0ustar pravipravi#!/bin/sh -e # Usage: script/cibuild # CI build script. ./script/testsuite 4018 bundle exec rake test task-list-1.0.2/script/testsuite0000755000076400007640000000062012545214714015747 0ustar pravipravi#!/usr/bin/env ruby root = File.expand_path("../..", __FILE__) Dir.chdir root port = ARGV[0] || 4000 pid = fork do $stderr.reopen "/dev/null" # silence WEBrick output exec 'bundle', 'exec', 'rackup', '-p', port.to_s end sleep 1 status = system('phantomjs', "#{root}/test/run-qunit.coffee", "http://localhost:#{port}/test/index.html") Process.kill 'SIGINT', pid Process.wait pid exit status task-list-1.0.2/task-lists.gemspec0000644000076400007640000000204712545214714016134 0ustar pravipravi# -*- encoding: utf-8 -*- lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'task_list/version' Gem::Specification.new do |gem| gem.name = "task_list" gem.version = TaskList::VERSION gem.authors = ["Matt Todd"] gem.email = ["matt@github.com"] gem.description = %q{GitHub-flavored-Markdown TaskList components} gem.summary = %q{GitHub-flavored-Markdown TaskList components} gem.files = `git ls-files`.split($/) gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ["lib"] gem.add_dependency "html-pipeline" gem.add_development_dependency "github-markdown" gem.add_development_dependency "rake" gem.add_development_dependency "coffee-script" gem.add_development_dependency "json" gem.add_development_dependency "rack" gem.add_development_dependency "sprockets" gem.add_development_dependency "minitest", "~> 5.3.2" end task-list-1.0.2/README.md0000644000076400007640000000347012545214714013751 0ustar pravipravi# Task Lists This package provides various components necessary for integrating [Task Lists](https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments) into your GitHub-flavored-Markdown user content. ## Components The Task List feature is actually made of several different components: * GitHub-flavored-Markdown Filter * Model: summarizes task list items * JavaScript: task list update behavior * CSS: styles Markdown task list items ## Installation Add this line to your application's Gemfile: gem 'task_list' And then execute: $ bundle ### Rails 3+: Railtie method ``` ruby # config/application.rb require 'task_list/railtie' ``` ### Rails 2.3: Manual method Wherever you have your Sprockets setup: ``` ruby Sprockets::Environment.new(Rails.root) do |env| # Load TaskList assets require 'task_list/railtie' TaskList.asset_paths.each do |path| env.append_path path end end ``` If you're not using Sprockets, you're on your own but it's pretty straight forward. `task_list/railtie` defines `TaskList.asset_paths` which you can use to manage building your asset bundles. ### CoffeeScript Requirements Aside from requiring CoffeeScript, jQuery is the only other requirement. ## Testing and Development JavaScript unit tests can be run with `script/testsuite`. Ruby unit tests can be run with `rake test`. Functional tests are more for manual testing in the browser. To run, install the necessary components with `script/bootstrap` then run the server: ``` rackup -p 4011 ``` Navigate to http://localhost:4011/test/functional/test_task_lists_behavior.html ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request task-list-1.0.2/config.ru0000644000076400007640000000112212545214714014277 0ustar pravipravi# Rack environment for testing purposes require 'coffee-script' require 'json' require 'sprockets' Root = File.expand_path("..", __FILE__) Assets = Sprockets::Environment.new(Root) do |env| env.append_path "bower_components" env.append_path "app/assets/javascripts" env.append_path "app/assets/stylesheets" env.append_path "test" end map "/assets" do run Assets end map "/update" do run lambda { |env| sleep 0.5 req = Rack::Request.new(env) [200, {'Content-Type' => 'application/json'}, [req.params.to_json]] } end map "/" do run Rack::Directory.new(Root) end task-list-1.0.2/test/0000755000076400007640000000000012545214714013445 5ustar pravipravitask-list-1.0.2/test/test_helper.rb0000644000076400007640000000010012545214714016277 0ustar pravipravi$:.unshift "lib" require 'minitest/autorun' require 'task_list' task-list-1.0.2/test/units.css0000644000076400007640000000004112545214714015314 0ustar pravipravi/*= require qunit/qunit/qunit */ task-list-1.0.2/test/units.coffee0000644000076400007640000000007112545214714015756 0ustar pravipravi#= require qunit/qunit/qunit #= require_directory ./unit task-list-1.0.2/test/index.html0000644000076400007640000000041712545214714015444 0ustar pravipravi
task-list-1.0.2/test/task_list/0000755000076400007640000000000012545214714015442 5ustar pravipravitask-list-1.0.2/test/task_list/filter_test.rb0000644000076400007640000000641312545214714020317 0ustar pravipravi# encoding: utf-8 require File.expand_path('../../test_helper', __FILE__) require 'task_list/filter' class TaskList::FilterTest < Minitest::Test def setup @pipeline = HTML::Pipeline.new [ HTML::Pipeline::MarkdownFilter, TaskList::Filter ], {}, {} @context = {} @item_selector = "input.task-list-item-checkbox[type=checkbox]" end def test_filters_items_in_a_list text = <<-md - [ ] incomplete - [x] complete md assert_equal 2, filter(text)[:output].css(@item_selector).size end def test_filters_items_with_HTML_contents text = <<-md - [ ] incomplete **with bold** text - [x] complete __with italic__ text md assert_equal 2, filter(text)[:output].css(@item_selector).size end def test_filters_items_in_a_list_wrapped_in_paras # See issue #7951 for details. text = <<-md - [ ] one - [ ] this one will be wrapped in a para - [ ] this one too, wtf md assert_equal 3, filter(text)[:output].css(@item_selector).size end def test_populates_result_with_task_list_items text = <<-md - [ ] incomplete - [x] complete md result = filter(text) assert !result[:task_list_items].empty? incomplete, complete = result[:task_list_items] assert incomplete assert !incomplete.complete? assert complete assert complete.complete? end def test_skips_lists_in_code_blocks code = <<-md ``` - [ ] incomplete - [x] complete ``` md assert filter(code)[:output].css(@item_selector).empty?, "should not have any task list items" end def test_handles_encoding_correctly unicode = "中文" text = <<-md - [ ] #{unicode} md assert item = filter(text)[:output].css('.task-list-item').pop assert_equal unicode, item.text.strip end def test_handles_nested_items text = <<-md - [ ] one - [ ] one.one md assert item = filter(text)[:output].css('.task-list-item .task-list-item').pop end def test_handles_complicated_nested_items text = <<-md - [ ] one - [ ] one.one - [x] one.two - [ ] one.two.one - [ ] one.two.two - [ ] one.three - [ ] one.four - [ ] two - [x] two.one - [ ] two.two - [ ] three md assert_equal 6 + 2, filter(text)[:output].css('.task-list-item .task-list-item').size assert_equal 2, filter(text)[:output].css('.task-list-item .task-list-item .task-list-item').size end # NOTE: This is an edge case experienced regularly by users using a Swiss # German keyboard. # See: https://github.com/github/github/pull/18362 def test_non_breaking_space_between_brackets text = "- [\xC2\xA0] ok" assert item = filter(text)[:output].css('.task-list-item').pop, "item expected" assert_equal 'ok', item.text.strip end # See: https://github.com/github/github/pull/18362 def test_non_breaking_space_between_brackets_in_paras text = <<-md - [\xC2\xA0] one - [\xC2\xA0] this one will be wrapped in a para - [\xC2\xA0] this one too, wtf md assert_equal 3, filter(text)[:output].css(@item_selector).size end def test_capital_X text = <<-md - [x] lower case - [X] capital md assert_equal 2, filter(text)[:output].css("[checked]").size end protected def filter(input, context = @context, result = nil) result ||= {} @pipeline.call(input, context, result) end end task-list-1.0.2/test/task_list/summary_test.rb0000644000076400007640000000164112545214714020525 0ustar pravipravi# encoding: utf-8 require File.expand_path('../../test_helper', __FILE__) require 'task_list/summary' class TaskList::SummaryTest < Minitest::Test def setup @complete = make_item "[x]", "complete" @incomplete = make_item "[ ]", "incomplete" @items = [@complete, @incomplete] @summary = make_summary @items end def test_no_items summary = make_summary [] assert !summary.items?, "no task list items are expected" end def test_items assert @summary.items?, "task list items are expected" assert_equal 2, @summary.item_count end def test_complete_count assert_equal 1, @summary.complete_count end def test_incomplete_count assert_equal 1, @summary.incomplete_count end protected def make_item(checkbox_text = "[ ]", source = "an item!") TaskList::Item.new(checkbox_text, source) end def make_summary(items) TaskList::Summary.new(items) end end task-list-1.0.2/test/functional/0000755000076400007640000000000012545214714015607 5ustar pravipravitask-list-1.0.2/test/functional/test_task_lists_behavior.html0000644000076400007640000000533212545214714023576 0ustar pravipravi
task-list-1.0.2/test/functional/helpers/0000755000076400007640000000000012545214714017251 5ustar pravipravitask-list-1.0.2/test/functional/helpers/remote.coffee0000644000076400007640000000011112545214714021706 0ustar pravipravi#= require jquery #= require rails-behaviors/remote #= require task_list task-list-1.0.2/test/unit/0000755000076400007640000000000012545214714014424 5ustar pravipravitask-list-1.0.2/test/unit/test_events.coffee0000644000076400007640000000373312545214714020146 0ustar pravipravi#= require task_list module "TaskList events", setup: -> @container = $ '
', class: 'js-task-list-container' @list = $ '
# # ### Specification # # TaskLists MUST be contained in a `(div).js-task-list-container`. # # TaskList Items SHOULD be an a list (`UL`/`OL`) element. # # Task list items MUST match `(input).task-list-item-checkbox` and MUST be # `disabled` by default. # # TaskLists MUST have a `(textarea).js-task-list-field` form element whose # `value` attribute is the source (Markdown) to be udpated. The source MUST # follow the syntax guidelines. # # TaskList updates trigger `tasklist:change` events. If the change is # successful, `tasklist:changed` is fired. The change can be canceled. # # jQuery is required. # # ### Methods # # `.taskList('enable')` or `.taskList()` # # Enables TaskList updates for the container. # # `.taskList('disable')` # # Disables TaskList updates for the container. # ## ### Events # # `tasklist:enabled` # # Fired when the TaskList is enabled. # # * **Synchronicity** Sync # * **Bubbles** Yes # * **Cancelable** No # * **Target** `.js-task-list-container` # # `tasklist:disabled` # # Fired when the TaskList is disabled. # # * **Synchronicity** Sync # * **Bubbles** Yes # * **Cancelable** No # * **Target** `.js-task-list-container` # # `tasklist:change` # # Fired before the TaskList item change takes affect. # # * **Synchronicity** Sync # * **Bubbles** Yes # * **Cancelable** Yes # * **Target** `.js-task-list-field` # # `tasklist:changed` # # Fired once the TaskList item change has taken affect. # # * **Synchronicity** Sync # * **Bubbles** Yes # * **Cancelable** No # * **Target** `.js-task-list-field` # # ### NOTE # # Task list checkboxes are rendered as disabled by default because rendered # user content is cached without regard for the viewer. incomplete = "[ ]" complete = "[x]" # Escapes the String for regular expression matching. escapePattern = (str) -> str. replace(/([\[\]])/g, "\\$1"). # escape square brackets replace(/\s/, "\\s"). # match all white space replace("x", "[xX]") # match all cases incompletePattern = /// #{escapePattern(incomplete)} /// completePattern = /// #{escapePattern(complete)} /// # Pattern used to identify all task list items. # Useful when you need iterate over all items. itemPattern = /// ^ (?: # prefix, consisting of \s* # optional leading whitespace (?:>\s*)* # zero or more blockquotes (?:[-+*]|(?:\d+\.)) # list item indicator ) \s* # optional whitespace prefix ( # checkbox #{escapePattern(complete)}| #{escapePattern(incomplete)} ) \s+ # is followed by whitespace (?! \(.*?\) # is not part of a [foo](url) link ) (?= # and is followed by zero or more links (?:\[.*?\]\s*(?:\[.*?\]|\(.*?\))\s*)* (?:[^\[]|$) # and either a non-link or the end of the string ) /// # Used to filter out code fences from the source for comparison only. # http://rubular.com/r/x5EwZVrloI # Modified slightly due to issues with JS codeFencesPattern = /// ^`{3} # ``` (?:\s*\w+)? # followed by optional language [\S\s] # whitespace .* # code [\S\s] # whitespace ^`{3}$ # ``` ///mg # Used to filter out potential mismatches (items not in lists). # http://rubular.com/r/OInl6CiePy itemsInParasPattern = /// ^ ( #{escapePattern(complete)}| #{escapePattern(incomplete)} ) .+ $ ///g # Given the source text, updates the appropriate task list item to match the # given checked value. # # Returns the updated String text. updateTaskListItem = (source, itemIndex, checked) -> clean = source.replace(/\r/g, '').replace(codeFencesPattern, ''). replace(itemsInParasPattern, '').split("\n") index = 0 result = for line in source.split("\n") if line in clean && line.match(itemPattern) index += 1 if index == itemIndex line = if checked line.replace(incompletePattern, complete) else line.replace(completePattern, incomplete) line result.join("\n") # Updates the $field value to reflect the state of $item. # Triggers the `tasklist:change` event before the value has changed, and fires # a `tasklist:changed` event once the value has changed. updateTaskList = ($item) -> $container = $item.closest '.js-task-list-container' $field = $container.find '.js-task-list-field' index = 1 + $container.find('.task-list-item-checkbox').index($item) checked = $item.prop 'checked' event = $.Event 'tasklist:change' $field.trigger event, [index, checked] unless event.isDefaultPrevented() $field.val updateTaskListItem($field.val(), index, checked) $field.trigger 'change' $field.trigger 'tasklist:changed', [index, checked] # When the task list item checkbox is updated, submit the change $(document).on 'change', '.task-list-item-checkbox', -> updateTaskList $(this) # Enables TaskList item changes. enableTaskList = ($container) -> if $container.find('.js-task-list-field').length > 0 $container. find('.task-list-item').addClass('enabled'). find('.task-list-item-checkbox').attr('disabled', null) $container.addClass('is-task-list-enabled'). trigger 'tasklist:enabled' # Enables a collection of TaskList containers. enableTaskLists = ($containers) -> for container in $containers enableTaskList $(container) # Disable TaskList item changes. disableTaskList = ($container) -> $container. find('.task-list-item').removeClass('enabled'). find('.task-list-item-checkbox').attr('disabled', 'disabled') $container.removeClass('is-task-list-enabled'). trigger 'tasklist:disabled' # Disables a collection of TaskList containers. disableTaskLists = ($containers) -> for container in $containers disableTaskList $(container) $.fn.taskList = (method) -> $container = $(this).closest('.js-task-list-container') methods = enable: enableTaskLists disable: disableTaskLists methods[method || 'enable']($container) task-list-1.0.2/Gemfile0000644000076400007640000000004612545214714013761 0ustar pravipravisource "https://rubygems.org" gemspec task-list-1.0.2/metadata.yml0000644000076400007640000001137212545214714014775 0ustar pravipravi--- !ruby/object:Gem::Specification name: task_list version: !ruby/object:Gem::Version version: 1.0.2 platform: ruby authors: - Matt Todd autorequire: bindir: bin cert_chain: [] date: 2014-09-26 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: html-pipeline requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: github-markdown requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: coffee-script requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: json requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: rack requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: sprockets requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: minitest requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 5.3.2 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 5.3.2 description: GitHub-flavored-Markdown TaskList components email: - matt@github.com executables: [] extensions: [] extra_rdoc_files: [] files: - ".gitignore" - ".travis.yml" - Gemfile - LICENSE - README.md - Rakefile - app/assets/javascripts/task_list.coffee - app/assets/stylesheets/task_list.scss - bower.json - config.ru - lib/task_list.rb - lib/task_list/filter.rb - lib/task_list/railtie.rb - lib/task_list/summary.rb - lib/task_list/version.rb - script/bootstrap - script/cibuild - script/testsuite - task-lists.gemspec - test/functional/helpers/remote.coffee - test/functional/test_task_lists_behavior.html - test/index.html - test/run-qunit.coffee - test/task_list/filter_test.rb - test/task_list/summary_test.rb - test/task_list_test.rb - test/test_helper.rb - test/unit/test_events.coffee - test/unit/test_updates.coffee - test/units.coffee - test/units.css homepage: licenses: [] metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.2.2 signing_key: specification_version: 4 summary: GitHub-flavored-Markdown TaskList components test_files: - test/functional/helpers/remote.coffee - test/functional/test_task_lists_behavior.html - test/index.html - test/run-qunit.coffee - test/task_list/filter_test.rb - test/task_list/summary_test.rb - test/task_list_test.rb - test/test_helper.rb - test/unit/test_events.coffee - test/unit/test_updates.coffee - test/units.coffee - test/units.css task-list-1.0.2/lib/0000755000076400007640000000000012545214714013234 5ustar pravipravitask-list-1.0.2/lib/task_list.rb0000644000076400007640000000132112545214714015553 0ustar pravipravirequire 'task_list/summary' require 'task_list/version' # encoding: utf-8 class TaskList attr_reader :record # `record` is the resource with the Markdown source text with task list items # following this syntax: # # - [ ] a task list item # - [ ] another item # - [x] a completed item # def initialize(record) @record = record end # Public: return the TaskList::Summary for this task list. # # Returns a TaskList::Summary. def summary @summary ||= TaskList::Summary.new(record.task_list_items) end class Item < Struct.new(:checkbox_text, :source) Complete = /\[[xX]\]/.freeze # see TaskList::Filter def complete? checkbox_text =~ Complete end end end task-list-1.0.2/lib/task_list/0000755000076400007640000000000012545214714015231 5ustar pravipravitask-list-1.0.2/lib/task_list/summary.rb0000644000076400007640000000134712545214714017260 0ustar pravipravi# encoding: utf-8 require 'html/pipeline' require 'task_list' class TaskList # Provides a summary of provided TaskList `items`. # # `items` is an Array of TaskList::Item objects. class Summary < Struct.new(:items) # Public: returns true if there are any TaskList::Item objects. def items? item_count > 0 end # Public: returns the number of TaskList::Item objects. def item_count items.size end # Public: returns the number of complete TaskList::Item objects. def complete_count items.select{ |i| i.complete? }.size end # Public: returns the number of incomplete TaskList::Item objects. def incomplete_count items.select{ |i| !i.complete? }.size end end end task-list-1.0.2/lib/task_list/railtie.rb0000644000076400007640000000065712545214714017217 0ustar pravipraviclass TaskList def self.root_path @root_path ||= Pathname.new(File.expand_path("../../../", __FILE__)) end def self.asset_paths @paths ||= Dir[root_path.join("app/assets/*")] end if defined? ::Rails::Railtie class Railtie < ::Rails::Railtie initializer "task_list" do |app| TaskList.asset_paths.each do |path| app.config.assets.paths << path end end end end end task-list-1.0.2/lib/task_list/version.rb0000644000076400007640000000006312545214714017242 0ustar pravipraviclass TaskList VERSION = [1, 0, 2].join('.') end task-list-1.0.2/lib/task_list/filter.rb0000644000076400007640000001043612545214714017047 0ustar pravipravi# encoding: utf-8 require 'html/pipeline' require 'task_list' class TaskList # Returns a `Nokogiri::DocumentFragment` object. def self.filter(*args) Filter.call(*args) end # TaskList filter replaces task list item markers (`[ ]` and `[x]`) 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 # ``` # # Results # ------- # # The following keys are written to the result hash: # :task_list_items - An array of TaskList::Item objects. class Filter < HTML::Pipeline::Filter Incomplete = "[ ]".freeze Complete = "[x]".freeze IncompletePattern = /\[[[:space:]]\]/.freeze # matches all whitespace CompletePattern = /\[[xX]\]/.freeze # matches any capitalization # Pattern used to identify all task list items. # Useful when you need iterate over all items. ItemPattern = / ^ (?:\s*[-+*]|(?:\d+\.))? # optional list prefix \s* # optional whitespace prefix ( # checkbox #{CompletePattern}| #{IncompletePattern} ) (?=\s) # followed by whitespace /x ListItemSelector = ".//li[task_list_item(.)]".freeze class XPathSelectorFunction def self.task_list_item(nodes) nodes if nodes.text =~ ItemPattern end end # Selects first P tag of an LI, if present ItemParaSelector = "./p[1]".freeze # List of `TaskList::Item` objects that were recognized in the document. # This is available in the result hash as `:task_list_items`. # # Returns an Array of TaskList::Item objects. def task_list_items result[:task_list_items] ||= [] end # Renders the item checkbox in a span including the item state. # # Returns an HTML-safe String. def render_item_checkbox(item) %() end # Public: Marks up the task list item checkbox with metadata and behavior. # # NOTE: produces a string that, when assigned to a Node's `inner_html`, # will corrupt the string contents' encodings. Instead, we parse the # rendered HTML and explicitly set its encoding so that assignment will # not change the encodings. # # See [this pull](https://github.com/github/github/pull/8505) for details. # # Returns the marked up task list item Nokogiri::XML::NodeSet object. def render_task_list_item(item) Nokogiri::HTML.fragment \ item.source.sub(ItemPattern, render_item_checkbox(item)), 'utf-8' end # Public: Select all task lists from the `doc`. # # Returns an Array of Nokogiri::XML::Element objects for ordered and # unordered lists. def list_items doc.xpath(ListItemSelector, XPathSelectorFunction) end # Filters the source for task list items. # # Each item is wrapped in HTML to identify, style, and layer # useful behavior on top of. # # Modifications apply to the parsed document directly. # # Returns nothing. def filter! list_items.reverse.each do |li| add_css_class(li.parent, 'task-list') outer, inner = if p = li.xpath(ItemParaSelector)[0] [p, p.inner_html] else [li, li.inner_html] end if match = (inner.chomp =~ ItemPattern && $1) item = TaskList::Item.new(match, inner) # prepend because we're iterating in reverse task_list_items.unshift item add_css_class(li, 'task-list-item') outer.inner_html = render_task_list_item(item) end end end def call filter! doc end # Private: adds a CSS class name to a node, respecting existing class # names. def add_css_class(node, *new_class_names) class_names = (node['class'] || '').split(' ') return if new_class_names.all? { |klass| class_names.include?(klass) } class_names.concat(new_class_names) node['class'] = class_names.uniq.join(' ') end end end task-list-1.0.2/Rakefile0000644000076400007640000000027612545214714014140 0ustar pravipravirequire "bundler/gem_tasks" require "rake/testtask" task :default => :test Rake::TestTask.new do |t| t.libs << "lib" t.test_files = FileList['test/**/*_test.rb'] t.verbose = true end task-list-1.0.2/.gitignore0000644000076400007640000000031212545214714014452 0ustar pravipravi*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc bin/ coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp bower_components node_modules vendor/gems/