html-pipeline-task_list 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7f7c8e80e9c4e81c8f3e5664b8c4169c9058d1fc3e1f18b73717f5b4290ee717
4
+ data.tar.gz: 959db5129b1a159383a730d02ca2084880c9c88449bd6912971fcf5106186eb8
5
+ SHA512:
6
+ metadata.gz: a40d56aadbe7e1a92361e7bd40a032cf42ec15def24d4de044099b81c14e7d00801c6f6918ddd603f078d9db258e97207afe02db8c9d3c77e6495e5bd106e7a8
7
+ data.tar.gz: '091d7c01b62a2ae4dae0698b44331ec811160452a48cf0e633bf9948307ac1d9c58a834af194479cd4d8fa4508f651e300a6966b755ddbcfbad05dee6bdf5e84'
@@ -0,0 +1,91 @@
1
+ version: 2.1
2
+
3
+ environment: &environment
4
+ LOG_LEVEL: WARN
5
+ TEST_PORT: 3000
6
+ TEST_HOST: http://localhost:3000
7
+
8
+ jobs:
9
+ functional_tests:
10
+ working_directory: ~/repos/html-pipeline-task_list
11
+
12
+ docker:
13
+ - image: circleci/ruby:2.6.4-buster-node-browsers
14
+ environment:
15
+ <<: *environment
16
+
17
+ steps:
18
+ - checkout
19
+
20
+ - run:
21
+ name: install bundler
22
+ command: gem install bundler:2.1.4
23
+
24
+ - run:
25
+ name: check dependencies
26
+ command: |
27
+ bundle -v
28
+ bundle exec ruby -v
29
+
30
+ - restore_cache:
31
+ keys:
32
+ - html-pipeline-task_list-cache-v2-{{ checksum "html-pipeline-task_list.gemspec" }}
33
+ - html-pipeline-task_list-cache-v2-
34
+
35
+ - run:
36
+ name: install gems
37
+ command: bundle install && bundle clean
38
+
39
+ - run:
40
+ name: install packages
41
+ command: npm install
42
+
43
+ - save_cache:
44
+ key: html-pipeline-task_list-cache-v2-{{ checksum "html-pipeline-task_list.gemspec" }}
45
+ paths:
46
+ - vendor
47
+ - node_modules
48
+
49
+ - run:
50
+ name: run functional tests
51
+ command: bundle exec rake test
52
+
53
+ - run:
54
+ name: run javascript unit tests
55
+ command: npm test
56
+
57
+ - store_test_results:
58
+ path: test_results
59
+
60
+ publish_to_rubygems:
61
+ working_directory: ~/repos/html-pipeline-task_list
62
+
63
+ docker:
64
+ - image: circleci/ruby:2.6.4-buster-node
65
+ environment:
66
+ <<: *environment
67
+
68
+ steps:
69
+ - checkout
70
+
71
+ - run:
72
+ name: setup ubygems
73
+ command: bash .circleci/setup-rubygems.sh
74
+
75
+ - run:
76
+ name: publish to Rubygems
77
+ command: |
78
+ gem build html-pipeline-task_list.gemspec
79
+ gem push "html-pipeline-task_list-$(git describe --abbrev=0 --tags | cut -c 2-).gem"
80
+
81
+ workflows:
82
+ version: 2
83
+ build_and_test:
84
+ jobs:
85
+ - functional_tests
86
+ - publish_to_rubygems:
87
+ filters:
88
+ tags:
89
+ only: /.*/
90
+ branches:
91
+ ignore: /.*/
@@ -0,0 +1,3 @@
1
+ mkdir ~/.gem
2
+ echo -e "---\r\n:rubygems_api_key: $RUBYGEMS_API_KEY" > ~/.gem/credentials
3
+ chmod 0600 /home/circleci/.gem/credentials
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ Gemfile.lock
4
+ /_yardoc/
5
+ /bin/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ /node_modules/
12
+ /public/
13
+ /vendor/
14
+ .DS_Store
15
+ *.gem
@@ -0,0 +1,45 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+ Exclude:
4
+ - 'bin/**/*'
5
+ - 'vendor/**/*'
6
+ - 'config.ru'
7
+
8
+ Layout/LineLength:
9
+ Max: 100
10
+
11
+ Layout/ParameterAlignment:
12
+ Exclude:
13
+ - 'bin/**/*'
14
+
15
+ Metrics/AbcSize:
16
+ Max: 20
17
+
18
+ Metrics/BlockLength:
19
+ Max: 30
20
+
21
+ Metrics/ClassLength:
22
+ Exclude:
23
+ - 'test/**/*.rb'
24
+
25
+ Metrics/MethodLength:
26
+ Max: 15
27
+ Exclude:
28
+ - 'test/**/*.rb'
29
+
30
+ Style/ClassAndModuleChildren:
31
+ Exclude:
32
+ - 'test/**/*.rb'
33
+
34
+ Style/Documentation:
35
+ Exclude:
36
+ - 'test/**/*'
37
+
38
+ Style/HashEachMethods:
39
+ Enabled: true
40
+
41
+ Style/HashTransformKeys:
42
+ Enabled: true
43
+
44
+ Style/HashTransformValues:
45
+ Enabled: true
@@ -0,0 +1,11 @@
1
+ # Contributions Welcome!
2
+
3
+ Hi there! We're thrilled that you'd like to contribute to this project. Before you do, would you mind reading [this license agreement](CLA.md)? If you open a PR, we'll assume you agree to it. If you have any hesitation or disagreement, please do open a PR still, but note your concerns as well.
4
+
5
+ Once done, follow these steps:
6
+
7
+ 1. Fork this repository
8
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
9
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
10
+ 4. Push to the branch (`git push origin my-new-feature`)
11
+ 5. Create a Pull Request
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rspec configuration
4
+ minitest_opts = {
5
+ cmd: 'rake test',
6
+ autorun: false,
7
+ all_on_start: true
8
+ }
9
+
10
+ rubocop_opts = {
11
+ all_on_start: true,
12
+ cli: ['--display-cop-names']
13
+ }
14
+
15
+ group :rails, halt_on_fail: true do
16
+ guard :minitest, minitest_opts do
17
+ directories %w[test lib]
18
+ watch %r{^test/(.+)_test\.rb$}
19
+ watch(%r{^lib/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
20
+ end
21
+
22
+ guard :rubocop, rubocop_opts do
23
+ directories %w[test lib]
24
+ watch(%r{^test/.+\.rb$})
25
+ watch(%r{^lib/(.+)\.rb$})
26
+ end
27
+ end
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.
@@ -0,0 +1,104 @@
1
+ # Task Lists
2
+
3
+ This Gem 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
+ `HTML::Pipeline::TaskList` is a successor to [TaskList](https://github.com/github/task_list) which GitHub stopped supporting and updating in 2016. This gem updates key components to support changes to underlying gems.
8
+
9
+ ## Components
10
+
11
+ The Task List feature is made of several different components:
12
+
13
+ * GitHub-flavored-Markdown Ruby Filter
14
+ * Summary Ruby Model: summarizes task list items
15
+ * JavaScript: frontend task list update behavior
16
+ * CSS: styles Markdown task list items
17
+
18
+ ## Usage & Integration
19
+
20
+ 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).
21
+
22
+ ### Backend: Markdown pipeline filter
23
+
24
+ Rendering Task List item checkboxes from source Markdown depends on the `HTML::Pipeline::TaskList::Filter`, designed to integrate with the [`html-pipeline`](https://github.com/jch/html-pipeline) gem. For example:
25
+
26
+ ``` ruby
27
+ require 'html/pipeline'
28
+ require 'html/pipeline/task_list/filter'
29
+
30
+ pipeline = HTML::Pipeline.new [
31
+ HTML::Pipeline::MarkdownFilter,
32
+ HTML::Pipeline::TaskList::Filter
33
+ ]
34
+
35
+ pipeline.call "- [ ] task list item"
36
+ ```
37
+
38
+ ### Frontend: Markdown Updates
39
+
40
+ Task List updates on the frontend require specific HTML markup structure, and must be enabled with JavaScript.
41
+
42
+ 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.
43
+
44
+ ``` markdown
45
+ - [ ] text
46
+ ```
47
+
48
+ ``` html
49
+ <div class="js-task-list-container">
50
+ <ul class="task-list">
51
+ <li class="task-list-item">
52
+ <input type="checkbox" class="js-task-list-item-checkbox" disabled />
53
+ text
54
+ </li>
55
+ </ul>
56
+ <form>
57
+ <textarea class="js-task-list-field">- [ ] text</textarea>
58
+ </form>
59
+ </div>
60
+ ```
61
+
62
+ Enable Task List updates with:
63
+
64
+ ``` javascript
65
+ $('.js-task-list-container').taskList('enable')
66
+ ```
67
+
68
+ 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.
69
+
70
+ Read through the documented behaviors and samples [in the source][frontend_behaviors] for more detail, including documented events.
71
+
72
+ [frontend_behaviors]: https://github.com/github/task_list/blob/master/app/assets/javascripts/task_list.coffee
73
+
74
+ ## Installation
75
+
76
+ ### Backend: RubyGem
77
+
78
+ For the backend Ruby components, add this line to your application's Gemfile:
79
+
80
+ gem 'html-pipeline-task_list', '~> 0.0'
81
+
82
+ And then execute `bundle install`
83
+
84
+ ## Development
85
+ AAfter checking out the repo, run `bundle install && npm install` to install dependencies.
86
+
87
+ To install this gem onto your local machine, run `bundle exec rake install`
88
+
89
+ To release a new version, update run `bundle exec rake release` to create a git tag for the version. push the git commits and tags and CI will automatically push to [RubyGems.org](https://rubygems.org).
90
+
91
+ ## Testing
92
+ before beginning testing, be sure to run `bundle install && npm install`
93
+
94
+ Ruby unit tests can be run with `rake test`.
95
+ Javascript unit tests can be run with `npm test`
96
+
97
+ Functional tests can be run manually in the browser. To do so:
98
+ 1. run `npm run server`
99
+ 2. open a browser to http://localhost:3000/test/behavior.html
100
+ 3. you can also see the QUnit tests at http://localhost:4011/test/qunit.html
101
+
102
+ ## Contributing
103
+
104
+ Read the [Contributing Guidelines](CONTRIBUTING.md) and open a Pull Request!
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+ require 'rubocop/rake_task'
6
+
7
+ task default: :test
8
+ Rake::TestTask.new do |t|
9
+ t.libs << 'lib'
10
+ t.test_files = FileList['test/**/*_test.rb']
11
+ t.verbose = true
12
+ end
13
+
14
+ RuboCop::RakeTask.new
15
+
16
+ Rake::Task['test'].enhance ['rubocop']
@@ -0,0 +1,107 @@
1
+ // HTML::Pipeline::TaskList
2
+
3
+ var codeFencesPattern, complete, completePattern, disableTaskList, disableTaskLists, enableTaskList, enableTaskLists, escapePattern, incomplete, incompletePattern, itemPattern, itemsInParasPattern, updateTaskList, updateTaskListItem,
4
+ indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
5
+
6
+ incomplete = "[ ]";
7
+
8
+ complete = "[x]";
9
+
10
+ escapePattern = function(str) {
11
+ return str.replace(/([\[\]])/g, "\\$1").replace(/\s/, "\\s").replace("x", "[xX]");
12
+ };
13
+
14
+ incompletePattern = RegExp("" + (escapePattern(incomplete)));
15
+
16
+ completePattern = RegExp("" + (escapePattern(complete)));
17
+
18
+ itemPattern = RegExp("^(?:\\s*(?:>\\s*)*(?:[-+*]|(?:\\d+\\.)))\\s*(" + (escapePattern(complete)) + "|" + (escapePattern(incomplete)) + ")\\s+(?!\\(.*?\\))(?=(?:\\[.*?\\]\\s*(?:\\[.*?\\]|\\(.*?\\))\\s*)*(?:[^\\[]|$))");
19
+
20
+ codeFencesPattern = /^`{3}(?:\s*\w+)?[\S\s].*[\S\s]^`{3}$/mg;
21
+
22
+ itemsInParasPattern = RegExp("^(" + (escapePattern(complete)) + "|" + (escapePattern(incomplete)) + ").+$", "g");
23
+
24
+ updateTaskListItem = function(source, itemIndex, checked) {
25
+ var clean, index, line, result;
26
+ clean = source.replace(/\r/g, '').replace(codeFencesPattern, '').replace(itemsInParasPattern, '').split("\n");
27
+ index = 0;
28
+ result = (function() {
29
+ var i, len, ref, results;
30
+ ref = source.split("\n");
31
+ results = [];
32
+ for (i = 0, len = ref.length; i < len; i++) {
33
+ line = ref[i];
34
+ if (indexOf.call(clean, line) >= 0 && line.match(itemPattern)) {
35
+ index += 1;
36
+ if (index === itemIndex) {
37
+ line = checked ? line.replace(incompletePattern, complete) : line.replace(completePattern, incomplete);
38
+ }
39
+ }
40
+ results.push(line);
41
+ }
42
+ return results;
43
+ })();
44
+ return result.join("\n");
45
+ };
46
+
47
+ updateTaskList = function($item) {
48
+ var $container, $field, checked, event, index;
49
+ $container = $item.closest('.js-task-list-container');
50
+ $field = $container.find('.js-task-list-field');
51
+ index = 1 + $container.find('.task-list-item-checkbox').index($item);
52
+ checked = $item.prop('checked');
53
+ event = $.Event('tasklist:change');
54
+ $field.trigger(event, [index, checked]);
55
+ if (!event.isDefaultPrevented()) {
56
+ $field.val(updateTaskListItem($field.val(), index, checked));
57
+ $field.trigger('change');
58
+ return $field.trigger('tasklist:changed', [index, checked]);
59
+ }
60
+ };
61
+
62
+ $(document).on('change', '.task-list-item-checkbox', function() {
63
+ return updateTaskList($(this));
64
+ });
65
+
66
+ enableTaskList = function($container) {
67
+ if ($container.find('.js-task-list-field').length > 0) {
68
+ $container.find('.task-list-item').addClass('enabled').find('.task-list-item-checkbox').attr('disabled', null);
69
+ return $container.addClass('is-task-list-enabled').trigger('tasklist:enabled');
70
+ }
71
+ };
72
+
73
+ enableTaskLists = function($containers) {
74
+ var container, i, len, results;
75
+ results = [];
76
+ for (i = 0, len = $containers.length; i < len; i++) {
77
+ container = $containers[i];
78
+ results.push(enableTaskList($(container)));
79
+ }
80
+ return results;
81
+ };
82
+
83
+ disableTaskList = function($container) {
84
+ $container.find('.task-list-item').removeClass('enabled').find('.task-list-item-checkbox').attr('disabled', 'disabled');
85
+ return $container.removeClass('is-task-list-enabled').trigger('tasklist:disabled');
86
+ };
87
+
88
+ disableTaskLists = function($containers) {
89
+ var container, i, len, results;
90
+ results = [];
91
+ for (i = 0, len = $containers.length; i < len; i++) {
92
+ container = $containers[i];
93
+ results.push(disableTaskList($(container)));
94
+ }
95
+ return results;
96
+ };
97
+
98
+ $.fn.taskList = function(method) {
99
+ var $container, methods;
100
+ $container = $(this).closest('.js-task-list-container');
101
+ methods = {
102
+ enable: enableTaskLists,
103
+ disable: disableTaskLists
104
+ };
105
+ return methods[method || 'enable']($container);
106
+ };
107
+