deckar01-task_list 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.travis.yml +11 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +142 -0
- data/Rakefile +9 -0
- data/app/assets/javascripts/task_list.coffee +237 -0
- data/app/assets/stylesheets/task_list.scss +20 -0
- data/bower.json +28 -0
- data/config.ru +30 -0
- data/lib/task_list.rb +44 -0
- data/lib/task_list/filter.rb +149 -0
- data/lib/task_list/railtie.rb +20 -0
- data/lib/task_list/summary.rb +30 -0
- data/lib/task_list/version.rb +3 -0
- data/script/bootstrap +13 -0
- data/script/cibuild +6 -0
- data/script/testsuite +19 -0
- data/task_list.gemspec +30 -0
- data/test/functional/helpers/remote.coffee +3 -0
- data/test/functional/test_task_lists_behavior.html +103 -0
- data/test/index.html +12 -0
- data/test/run-qunit.coffee +50 -0
- data/test/task_list/filter_test.rb +142 -0
- data/test/task_list/summary_test.rb +40 -0
- data/test/task_list_test.rb +33 -0
- data/test/test_helper.rb +3 -0
- data/test/unit/test_events.coffee +96 -0
- data/test/unit/test_updates.coffee +566 -0
- data/test/units.coffee +2 -0
- data/test/units.css +1 -0
- metadata +227 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0ede80f64a73125eacfa02592290df623f5fba44
|
4
|
+
data.tar.gz: a68b79a9b6aa1f8a616ea4a56b68751f404d4dc0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5605f90ee454095d54491ca31cd53da4e03910400615474760e7c87acb590a9437a89d8ca6d4e4376a94b506bf30476856962625240ee260abee2d4e36e1ab8e
|
7
|
+
data.tar.gz: aa65c07c23206eb84d93b0286eeaf573e7ff6c2d75acd7f5a5735e6c93d3efd7b6c8389dd22a41f3a2be23e5b2a5d6c9bf00b07a19e65c385defde7c1daf3762
|
data/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
bin/
|
10
|
+
coverage
|
11
|
+
doc/
|
12
|
+
lib/bundler/man
|
13
|
+
pkg
|
14
|
+
rdoc
|
15
|
+
spec/reports
|
16
|
+
test/tmp
|
17
|
+
test/version_tmp
|
18
|
+
tmp
|
19
|
+
bower_components
|
20
|
+
node_modules
|
21
|
+
vendor/gems/
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Jared Deckard
|
4
|
+
Copyright (c) 2014 GitHub, Inc.
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
14
|
+
copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
# Task Lists
|
2
|
+
|
3
|
+
[![Build Status](http://img.shields.io/travis/deckar01/task_list.svg)][travis]
|
4
|
+
|
5
|
+
[travis]: https://travis-ci.org/deckar01/task_list
|
6
|
+
|
7
|
+
This package provides various components necessary for integrating
|
8
|
+
[Task Lists](https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments)
|
9
|
+
into your Markdown user content.
|
10
|
+
|
11
|
+
## Components
|
12
|
+
|
13
|
+
The Task List feature is made of several different components:
|
14
|
+
|
15
|
+
* Markdown Ruby Filter
|
16
|
+
* Summary Ruby Model: summarizes task list items
|
17
|
+
* JavaScript: frontend task list update behavior
|
18
|
+
* CSS: styles Markdown task list items
|
19
|
+
|
20
|
+
## Usage & Integration
|
21
|
+
|
22
|
+
The backend components are designed for rendering the Task List item checkboxes, and the frontend components handle updating the Markdown source (embedded in the markup).
|
23
|
+
|
24
|
+
### Backend: Markdown pipeline filter
|
25
|
+
|
26
|
+
Rendering Task List item checkboxes from source Markdown depends on the `TaskList::Filter`, designed to integrate with the [`html-pipeline`](https://github.com/jch/html-pipeline) gem. For example:
|
27
|
+
|
28
|
+
``` ruby
|
29
|
+
require 'html/pipeline'
|
30
|
+
require 'deckar01-task_list/filter'
|
31
|
+
|
32
|
+
pipeline = HTML::Pipeline.new [
|
33
|
+
HTML::Pipeline::MarkdownFilter,
|
34
|
+
TaskList::Filter
|
35
|
+
]
|
36
|
+
|
37
|
+
pipeline.call "- [ ] task list item"
|
38
|
+
```
|
39
|
+
|
40
|
+
### Frontend: Markdown Updates
|
41
|
+
|
42
|
+
Task List updates on the frontend require specific HTML markup structure, and must be enabled with JavaScript.
|
43
|
+
|
44
|
+
Rendered HTML (the `<ul>` element below) should be contained in a `js-task-list-container` container element and include a sibling `textarea.js-task-list-field` element that is updated when checkboxes are changed.
|
45
|
+
|
46
|
+
``` markdown
|
47
|
+
- [ ] text
|
48
|
+
```
|
49
|
+
|
50
|
+
``` html
|
51
|
+
<div class="js-task-list-container">
|
52
|
+
<ul class="task-list">
|
53
|
+
<li class="task-list-item">
|
54
|
+
<input type="checkbox" class="js-task-list-item-checkbox" disabled />
|
55
|
+
text
|
56
|
+
</li>
|
57
|
+
</ul>
|
58
|
+
<form>
|
59
|
+
<textarea class="js-task-list-field">- [ ] text</textarea>
|
60
|
+
</form>
|
61
|
+
</div>
|
62
|
+
```
|
63
|
+
|
64
|
+
Enable Task List updates with:
|
65
|
+
|
66
|
+
``` javascript
|
67
|
+
$('.js-task-list-container').taskList('enable')
|
68
|
+
```
|
69
|
+
|
70
|
+
NOTE: Updates are not persisted to the server automatically. Persistence is the responsibility of the integrating application, accomplished by hooking into the `tasklist:change` JavaScript event. For instance, we use AJAX to submit a hidden form on update.
|
71
|
+
|
72
|
+
Read through the documented behaviors and samples [in the source][frontend_behaviors] for more detail, including documented events.
|
73
|
+
|
74
|
+
[frontend_behaviors]: https://github.com/deckar01/task_list/blob/master/app/assets/javascripts/task_list.coffee
|
75
|
+
|
76
|
+
## Installation
|
77
|
+
|
78
|
+
Task Lists are packaged as both a RubyGem with both backend and frontend behavior, and a Bower package with just the frontend behavior.
|
79
|
+
|
80
|
+
### Backend: RubyGem
|
81
|
+
|
82
|
+
For the backend Ruby components, add this line to your application's Gemfile:
|
83
|
+
|
84
|
+
gem 'deckar01-task_list'
|
85
|
+
|
86
|
+
And then execute:
|
87
|
+
|
88
|
+
$ bundle
|
89
|
+
|
90
|
+
### Frontend: Bower
|
91
|
+
|
92
|
+
For the frontend components, add `deckar01-task_list` to your Bower dependencies config.
|
93
|
+
|
94
|
+
This is the preferred method for including the frontend assets in your application. Alternatively, for Rails methods using `Sprockets`, see below.
|
95
|
+
|
96
|
+
### Frontend: Rails 3+ Railtie method
|
97
|
+
|
98
|
+
``` ruby
|
99
|
+
# config/application.rb
|
100
|
+
require 'deckar01-task_list/railtie'
|
101
|
+
```
|
102
|
+
|
103
|
+
### Frontend: Rails 2.3 Manual method
|
104
|
+
|
105
|
+
Wherever you have your Sprockets setup:
|
106
|
+
|
107
|
+
``` ruby
|
108
|
+
Sprockets::Environment.new(Rails.root) do |env|
|
109
|
+
# Load TaskList assets
|
110
|
+
require 'deckar01-task_list/railtie'
|
111
|
+
TaskList.asset_paths.each do |path|
|
112
|
+
env.append_path path
|
113
|
+
end
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
117
|
+
If you're not using Sprockets, you're on your own but it's pretty straight
|
118
|
+
forward. `deckar01-task_list/railtie` defines `TaskList.asset_paths` which you can use
|
119
|
+
to manage building your asset bundles.
|
120
|
+
|
121
|
+
### Dependencies
|
122
|
+
|
123
|
+
At a high level, the Ruby components integrate with the [`html-pipeline`](https://github.com/jch/html-pipeline) library, and the frontend components depend on the jQuery library. The frontend components are written in CoffeeScript and need to be preprocessed for production use.
|
124
|
+
|
125
|
+
## Testing and Development
|
126
|
+
|
127
|
+
JavaScript unit tests can be run with `script/testsuite`.
|
128
|
+
|
129
|
+
Ruby unit tests can be run with `rake test`.
|
130
|
+
|
131
|
+
Functional tests are useful for manual testing in the browser. To run, install
|
132
|
+
the necessary components with `script/bootstrap` then run the server:
|
133
|
+
|
134
|
+
```
|
135
|
+
rackup -p 4011
|
136
|
+
```
|
137
|
+
|
138
|
+
Navigate to http://localhost:4011/test/functional/test_task_lists_behavior.html
|
139
|
+
|
140
|
+
## Community Integration
|
141
|
+
- [Waffle.io](http://waffle.io)
|
142
|
+
- [HuBoard](https://huboard.com/)
|
data/Rakefile
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
# TaskList Behavior
|
2
|
+
#
|
3
|
+
#= provides tasklist:enabled
|
4
|
+
#= provides tasklist:disabled
|
5
|
+
#= provides tasklist:change
|
6
|
+
#= provides tasklist:changed
|
7
|
+
#
|
8
|
+
#= require jquery
|
9
|
+
#
|
10
|
+
# Enables Task List update behavior.
|
11
|
+
#
|
12
|
+
# ### Example Markup
|
13
|
+
#
|
14
|
+
# <div class="js-task-list-container">
|
15
|
+
# <ul class="task-list">
|
16
|
+
# <li class="task-list-item">
|
17
|
+
# <input type="checkbox" class="js-task-list-item-checkbox" disabled />
|
18
|
+
# text
|
19
|
+
# </li>
|
20
|
+
# </ul>
|
21
|
+
# <form>
|
22
|
+
# <textarea class="js-task-list-field">- [ ] text</textarea>
|
23
|
+
# </form>
|
24
|
+
# </div>
|
25
|
+
#
|
26
|
+
# ### Specification
|
27
|
+
#
|
28
|
+
# TaskLists MUST be contained in a `(div).js-task-list-container`.
|
29
|
+
#
|
30
|
+
# TaskList Items SHOULD be an a list (`UL`/`OL`) element.
|
31
|
+
#
|
32
|
+
# Task list items MUST match `(input).task-list-item-checkbox` and MUST be
|
33
|
+
# `disabled` by default.
|
34
|
+
#
|
35
|
+
# TaskLists MUST have a `(textarea).js-task-list-field` form element whose
|
36
|
+
# `value` attribute is the source (Markdown) to be udpated. The source MUST
|
37
|
+
# follow the syntax guidelines.
|
38
|
+
#
|
39
|
+
# TaskList updates trigger `tasklist:change` events. If the change is
|
40
|
+
# successful, `tasklist:changed` is fired. The change can be canceled.
|
41
|
+
#
|
42
|
+
# jQuery is required.
|
43
|
+
#
|
44
|
+
# ### Methods
|
45
|
+
#
|
46
|
+
# `.taskList('enable')` or `.taskList()`
|
47
|
+
#
|
48
|
+
# Enables TaskList updates for the container.
|
49
|
+
#
|
50
|
+
# `.taskList('disable')`
|
51
|
+
#
|
52
|
+
# Disables TaskList updates for the container.
|
53
|
+
#
|
54
|
+
## ### Events
|
55
|
+
#
|
56
|
+
# `tasklist:enabled`
|
57
|
+
#
|
58
|
+
# Fired when the TaskList is enabled.
|
59
|
+
#
|
60
|
+
# * **Synchronicity** Sync
|
61
|
+
# * **Bubbles** Yes
|
62
|
+
# * **Cancelable** No
|
63
|
+
# * **Target** `.js-task-list-container`
|
64
|
+
#
|
65
|
+
# `tasklist:disabled`
|
66
|
+
#
|
67
|
+
# Fired when the TaskList is disabled.
|
68
|
+
#
|
69
|
+
# * **Synchronicity** Sync
|
70
|
+
# * **Bubbles** Yes
|
71
|
+
# * **Cancelable** No
|
72
|
+
# * **Target** `.js-task-list-container`
|
73
|
+
#
|
74
|
+
# `tasklist:change`
|
75
|
+
#
|
76
|
+
# Fired before the TaskList item change takes affect.
|
77
|
+
#
|
78
|
+
# * **Synchronicity** Sync
|
79
|
+
# * **Bubbles** Yes
|
80
|
+
# * **Cancelable** Yes
|
81
|
+
# * **Target** `.js-task-list-field`
|
82
|
+
#
|
83
|
+
# `tasklist:changed`
|
84
|
+
#
|
85
|
+
# Fired once the TaskList item change has taken affect.
|
86
|
+
#
|
87
|
+
# * **Synchronicity** Sync
|
88
|
+
# * **Bubbles** Yes
|
89
|
+
# * **Cancelable** No
|
90
|
+
# * **Target** `.js-task-list-field`
|
91
|
+
#
|
92
|
+
# ### NOTE
|
93
|
+
#
|
94
|
+
# Task list checkboxes are rendered as disabled by default because rendered
|
95
|
+
# user content is cached without regard for the viewer.
|
96
|
+
|
97
|
+
incomplete = "[ ]"
|
98
|
+
complete = "[x]"
|
99
|
+
|
100
|
+
# Escapes the String for regular expression matching.
|
101
|
+
escapePattern = (str) ->
|
102
|
+
str.
|
103
|
+
replace(/([\[\]])/g, "\\$1"). # escape square brackets
|
104
|
+
replace(/\s/, "\\s"). # match all white space
|
105
|
+
replace("x", "[xX]") # match all cases
|
106
|
+
|
107
|
+
incompletePattern = ///
|
108
|
+
#{escapePattern(incomplete)}
|
109
|
+
///
|
110
|
+
completePattern = ///
|
111
|
+
#{escapePattern(complete)}
|
112
|
+
///
|
113
|
+
|
114
|
+
# Pattern used to identify all task list items.
|
115
|
+
# Useful when you need iterate over all items.
|
116
|
+
itemPattern = ///
|
117
|
+
^
|
118
|
+
(?: # prefix, consisting of
|
119
|
+
\s* # optional leading whitespace
|
120
|
+
(?:>\s*)* # zero or more blockquotes
|
121
|
+
(?:[-+*]|(?:\d+\.)) # list item indicator
|
122
|
+
)
|
123
|
+
\s* # optional whitespace prefix
|
124
|
+
( # checkbox
|
125
|
+
#{escapePattern(complete)}|
|
126
|
+
#{escapePattern(incomplete)}
|
127
|
+
)
|
128
|
+
\s+ # is followed by whitespace
|
129
|
+
(?!
|
130
|
+
\(.*?\) # is not part of a [foo](url) link
|
131
|
+
)
|
132
|
+
(?= # and is followed by zero or more links
|
133
|
+
(?:\[.*?\]\s*(?:\[.*?\]|\(.*?\))\s*)*
|
134
|
+
(?:[^\[]|$) # and either a non-link or the end of the string
|
135
|
+
)
|
136
|
+
///
|
137
|
+
|
138
|
+
# Used to filter out code fences from the source for comparison only.
|
139
|
+
# http://rubular.com/r/x5EwZVrloI
|
140
|
+
# Modified slightly due to issues with JS
|
141
|
+
codeFencesPattern = ///
|
142
|
+
^`{3} # ```
|
143
|
+
(?:\s*\w+)? # followed by optional language
|
144
|
+
[\S\s] # whitespace
|
145
|
+
.* # code
|
146
|
+
[\S\s] # whitespace
|
147
|
+
^`{3}$ # ```
|
148
|
+
///mg
|
149
|
+
|
150
|
+
# Used to filter out potential mismatches (items not in lists).
|
151
|
+
# http://rubular.com/r/OInl6CiePy
|
152
|
+
itemsInParasPattern = ///
|
153
|
+
^
|
154
|
+
(
|
155
|
+
#{escapePattern(complete)}|
|
156
|
+
#{escapePattern(incomplete)}
|
157
|
+
)
|
158
|
+
.+
|
159
|
+
$
|
160
|
+
///g
|
161
|
+
|
162
|
+
# Given the source text, updates the appropriate task list item to match the
|
163
|
+
# given checked value.
|
164
|
+
#
|
165
|
+
# Returns the updated String text.
|
166
|
+
updateTaskListItem = (source, itemIndex, checked) ->
|
167
|
+
clean = source.replace(/\r/g, '').replace(codeFencesPattern, '').
|
168
|
+
replace(itemsInParasPattern, '').split("\n")
|
169
|
+
index = 0
|
170
|
+
result = for line in source.split("\n")
|
171
|
+
if line in clean && line.match(itemPattern)
|
172
|
+
index += 1
|
173
|
+
if index == itemIndex
|
174
|
+
line =
|
175
|
+
if checked
|
176
|
+
line.replace(incompletePattern, complete)
|
177
|
+
else
|
178
|
+
line.replace(completePattern, incomplete)
|
179
|
+
line
|
180
|
+
result.join("\n")
|
181
|
+
|
182
|
+
# Updates the $field value to reflect the state of $item.
|
183
|
+
# Triggers the `tasklist:change` event before the value has changed, and fires
|
184
|
+
# a `tasklist:changed` event once the value has changed.
|
185
|
+
updateTaskList = ($item) ->
|
186
|
+
$container = $item.closest '.js-task-list-container'
|
187
|
+
$field = $container.find '.js-task-list-field'
|
188
|
+
index = 1 + $container.find('.task-list-item-checkbox').index($item)
|
189
|
+
checked = $item.prop 'checked'
|
190
|
+
|
191
|
+
event = $.Event 'tasklist:change'
|
192
|
+
$field.trigger event, [index, checked]
|
193
|
+
|
194
|
+
unless event.isDefaultPrevented()
|
195
|
+
$field.val updateTaskListItem($field.val(), index, checked)
|
196
|
+
$field.trigger 'change'
|
197
|
+
$field.trigger 'tasklist:changed', [index, checked]
|
198
|
+
|
199
|
+
# When the task list item checkbox is updated, submit the change
|
200
|
+
$(document).on 'change', '.task-list-item-checkbox', ->
|
201
|
+
updateTaskList $(this)
|
202
|
+
|
203
|
+
# Enables TaskList item changes.
|
204
|
+
enableTaskList = ($container) ->
|
205
|
+
if $container.find('.js-task-list-field').length > 0
|
206
|
+
$container.
|
207
|
+
find('.task-list-item').addClass('enabled').
|
208
|
+
find('.task-list-item-checkbox').attr('disabled', null)
|
209
|
+
$container.addClass('is-task-list-enabled').
|
210
|
+
trigger 'tasklist:enabled'
|
211
|
+
|
212
|
+
# Enables a collection of TaskList containers.
|
213
|
+
enableTaskLists = ($containers) ->
|
214
|
+
for container in $containers
|
215
|
+
enableTaskList $(container)
|
216
|
+
|
217
|
+
# Disable TaskList item changes.
|
218
|
+
disableTaskList = ($container) ->
|
219
|
+
$container.
|
220
|
+
find('.task-list-item').removeClass('enabled').
|
221
|
+
find('.task-list-item-checkbox').attr('disabled', 'disabled')
|
222
|
+
$container.removeClass('is-task-list-enabled').
|
223
|
+
trigger 'tasklist:disabled'
|
224
|
+
|
225
|
+
# Disables a collection of TaskList containers.
|
226
|
+
disableTaskLists = ($containers) ->
|
227
|
+
for container in $containers
|
228
|
+
disableTaskList $(container)
|
229
|
+
|
230
|
+
$.fn.taskList = (method) ->
|
231
|
+
$container = $(this).closest('.js-task-list-container')
|
232
|
+
|
233
|
+
methods =
|
234
|
+
enable: enableTaskLists
|
235
|
+
disable: disableTaskLists
|
236
|
+
|
237
|
+
methods[method || 'enable']($container)
|