summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/application.js.coffee2
-rw-r--r--vendor/assets/javascripts/task_list.js.coffee258
2 files changed, 259 insertions, 1 deletions
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index 18c1aa0d4e2..0bf99bb1f4a 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -4,7 +4,7 @@
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file.
#
-#= require jquery
+#= require jquery2
#= require jquery-ui/autocomplete
#= require jquery-ui/datepicker
#= require jquery-ui/draggable
diff --git a/vendor/assets/javascripts/task_list.js.coffee b/vendor/assets/javascripts/task_list.js.coffee
new file mode 100644
index 00000000000..584751af8ea
--- /dev/null
+++ b/vendor/assets/javascripts/task_list.js.coffee
@@ -0,0 +1,258 @@
+# The 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.
+
+# TaskList Behavior
+#
+#= provides tasklist:enabled
+#= provides tasklist:disabled
+#= provides tasklist:change
+#= provides tasklist:changed
+#
+#
+# Enables Task List update behavior.
+#
+# ### Example Markup
+#
+# <div class="js-task-list-container">
+# <ul class="task-list">
+# <li class="task-list-item">
+# <input type="checkbox" class="js-task-list-item-checkbox" disabled />
+# text
+# </li>
+# </ul>
+# <form>
+# <textarea class="js-task-list-field">- [ ] text</textarea>
+# </form>
+# </div>
+#
+# ### 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)