task_list 1.0.2
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 +9 -0
- data/Gemfile +2 -0
- data/LICENSE +21 -0
- data/README.md +76 -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 +26 -0
- data/config.ru +30 -0
- data/lib/task_list.rb +32 -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-lists.gemspec +28 -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 +198 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c880856dc7b8d07412ca13a34e76735b76fe3a89
|
4
|
+
data.tar.gz: 12d60a0027b513fd71d955155a9a93f800ac3ff3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: db517c764d14e7d36d1a7528d54ca01d302c0d42525fa19451cddaf2ef0ef798a3e53ffcaefb5e3d7671c92a9c37ee1369e07f1f54235f9367153c6d0983fd04
|
7
|
+
data.tar.gz: c949fcc3518569cd16fefec6332dd79773ad927ee1043b039a15a00f1854051485a949aa746b380b114fe95c8e8e531bce1a67d7a31df9710849b0827b2200bf
|
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,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 GitHub, Inc.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# Task Lists
|
2
|
+
|
3
|
+
This package provides various components necessary for integrating
|
4
|
+
[Task Lists](https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments)
|
5
|
+
into your GitHub-flavored-Markdown user content.
|
6
|
+
|
7
|
+
## Components
|
8
|
+
|
9
|
+
The Task List feature is actually made of several different components:
|
10
|
+
|
11
|
+
* GitHub-flavored-Markdown Filter
|
12
|
+
* Model: summarizes task list items
|
13
|
+
* JavaScript: task list update behavior
|
14
|
+
* CSS: styles Markdown task list items
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
gem 'task_list'
|
21
|
+
|
22
|
+
And then execute:
|
23
|
+
|
24
|
+
$ bundle
|
25
|
+
|
26
|
+
### Rails 3+: Railtie method
|
27
|
+
|
28
|
+
``` ruby
|
29
|
+
# config/application.rb
|
30
|
+
require 'task_list/railtie'
|
31
|
+
```
|
32
|
+
|
33
|
+
### Rails 2.3: Manual method
|
34
|
+
|
35
|
+
Wherever you have your Sprockets setup:
|
36
|
+
|
37
|
+
``` ruby
|
38
|
+
Sprockets::Environment.new(Rails.root) do |env|
|
39
|
+
# Load TaskList assets
|
40
|
+
require 'task_list/railtie'
|
41
|
+
TaskList.asset_paths.each do |path|
|
42
|
+
env.append_path path
|
43
|
+
end
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
If you're not using Sprockets, you're on your own but it's pretty straight
|
48
|
+
forward. `task_list/railtie` defines `TaskList.asset_paths` which you can use
|
49
|
+
to manage building your asset bundles.
|
50
|
+
|
51
|
+
### CoffeeScript Requirements
|
52
|
+
|
53
|
+
Aside from requiring CoffeeScript, jQuery is the only other requirement.
|
54
|
+
|
55
|
+
## Testing and Development
|
56
|
+
|
57
|
+
JavaScript unit tests can be run with `script/testsuite`.
|
58
|
+
|
59
|
+
Ruby unit tests can be run with `rake test`.
|
60
|
+
|
61
|
+
Functional tests are more for manual testing in the browser. To run, install
|
62
|
+
the necessary components with `script/bootstrap` then run the server:
|
63
|
+
|
64
|
+
```
|
65
|
+
rackup -p 4011
|
66
|
+
```
|
67
|
+
|
68
|
+
Navigate to http://localhost:4011/test/functional/test_task_lists_behavior.html
|
69
|
+
|
70
|
+
## Contributing
|
71
|
+
|
72
|
+
1. Fork it
|
73
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
74
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
75
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
76
|
+
5. Create new Pull Request
|
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)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
// Requires use of `.markdown-body` to override default markdown list styles
|
2
|
+
.markdown-body .task-list {
|
3
|
+
list-style-type: none;
|
4
|
+
padding-left: 10px;
|
5
|
+
}
|
6
|
+
|
7
|
+
.task-list-item {
|
8
|
+
padding-left: 20px;
|
9
|
+
}
|
10
|
+
.task-list-item label {
|
11
|
+
font-weight: normal;
|
12
|
+
}
|
13
|
+
.task-list-item + .task-list-item {
|
14
|
+
margin-top: 3px;
|
15
|
+
}
|
16
|
+
.task-list-item-checkbox {
|
17
|
+
float: left;
|
18
|
+
margin-left: -20px;
|
19
|
+
margin-top: 4px;
|
20
|
+
}
|
data/bower.json
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
{
|
2
|
+
"name": "task_list",
|
3
|
+
"version": "1.0.2",
|
4
|
+
"dependencies": {
|
5
|
+
"jquery": ">= 1.9.1"
|
6
|
+
},
|
7
|
+
"devDependencies": {
|
8
|
+
"qunit": ">= 1.0"
|
9
|
+
},
|
10
|
+
"main": [
|
11
|
+
"app/assets/javascripts/task_list.coffee",
|
12
|
+
"app/assets/stylesheets/task_list.scss"
|
13
|
+
],
|
14
|
+
"ignore": [
|
15
|
+
".gitignore",
|
16
|
+
".travis.yml",
|
17
|
+
"*.gemspec",
|
18
|
+
"*.md",
|
19
|
+
"config.ru",
|
20
|
+
"Gemfile",
|
21
|
+
"lib/",
|
22
|
+
"Rakefile",
|
23
|
+
"script/",
|
24
|
+
"test/"
|
25
|
+
]
|
26
|
+
}
|
data/config.ru
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Rack environment for testing purposes
|
2
|
+
|
3
|
+
require 'coffee-script'
|
4
|
+
require 'json'
|
5
|
+
require 'sprockets'
|
6
|
+
|
7
|
+
Root = File.expand_path("..", __FILE__)
|
8
|
+
|
9
|
+
Assets = Sprockets::Environment.new(Root) do |env|
|
10
|
+
env.append_path "bower_components"
|
11
|
+
env.append_path "app/assets/javascripts"
|
12
|
+
env.append_path "app/assets/stylesheets"
|
13
|
+
env.append_path "test"
|
14
|
+
end
|
15
|
+
|
16
|
+
map "/assets" do
|
17
|
+
run Assets
|
18
|
+
end
|
19
|
+
|
20
|
+
map "/update" do
|
21
|
+
run lambda { |env|
|
22
|
+
sleep 0.5
|
23
|
+
req = Rack::Request.new(env)
|
24
|
+
[200, {'Content-Type' => 'application/json'}, [req.params.to_json]]
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
map "/" do
|
29
|
+
run Rack::Directory.new(Root)
|
30
|
+
end
|