Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
gitlab-ce
Commits
7f6474b2
Commit
7f6474b2
authored
Jul 26, 2016
by
Jared Deckard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Restore comments lost when converting CoffeeScript to JavaScript
parent
4c833a1d
Changes
89
Show whitespace changes
Inline
Side-by-side
Showing
89 changed files
with
958 additions
and
142 deletions
+958
-142
app/assets/javascripts/LabelManager.js
app/assets/javascripts/LabelManager.js
+5
-0
app/assets/javascripts/api.js
app/assets/javascripts/api.js
+6
-0
app/assets/javascripts/application.js
app/assets/javascripts/application.js
+21
-0
app/assets/javascripts/autosave.js
app/assets/javascripts/autosave.js
+3
-3
app/assets/javascripts/awards_handler.js
app/assets/javascripts/awards_handler.js
+5
-0
app/assets/javascripts/behaviors/autosize.js
app/assets/javascripts/behaviors/autosize.js
+0
-2
app/assets/javascripts/behaviors/details_behavior.js
app/assets/javascripts/behaviors/details_behavior.js
+6
-0
app/assets/javascripts/behaviors/quick_submit.js
app/assets/javascripts/behaviors/quick_submit.js
+19
-1
app/assets/javascripts/behaviors/requires_input.js
app/assets/javascripts/behaviors/requires_input.js
+18
-1
app/assets/javascripts/behaviors/toggler_behavior.js
app/assets/javascripts/behaviors/toggler_behavior.js
+7
-0
app/assets/javascripts/blob/blob_file_dropzone.js
app/assets/javascripts/blob/blob_file_dropzone.js
+3
-0
app/assets/javascripts/blob/template_selector.js
app/assets/javascripts/blob/template_selector.js
+3
-0
app/assets/javascripts/blob_edit/edit_blob.js
app/assets/javascripts/blob_edit/edit_blob.js
+2
-0
app/assets/javascripts/breakpoints.js
app/assets/javascripts/breakpoints.js
+2
-0
app/assets/javascripts/build.js
app/assets/javascripts/build.js
+8
-0
app/assets/javascripts/commit/image-file.js
app/assets/javascripts/commit/image-file.js
+2
-0
app/assets/javascripts/commits.js
app/assets/javascripts/commits.js
+1
-0
app/assets/javascripts/copy_to_clipboard.js
app/assets/javascripts/copy_to_clipboard.js
+6
-1
app/assets/javascripts/diff.js
app/assets/javascripts/diff.js
+3
-0
app/assets/javascripts/dispatcher.js
app/assets/javascripts/dispatcher.js
+4
-0
app/assets/javascripts/due_date_select.js
app/assets/javascripts/due_date_select.js
+3
-0
app/assets/javascripts/extensions/jquery.js
app/assets/javascripts/extensions/jquery.js
+2
-0
app/assets/javascripts/gfm_auto_complete.js.es6
app/assets/javascripts/gfm_auto_complete.js.es6
+24
-0
app/assets/javascripts/gl_dropdown.js
app/assets/javascripts/gl_dropdown.js
+64
-0
app/assets/javascripts/gl_form.js
app/assets/javascripts/gl_form.js
+6
-0
app/assets/javascripts/graphs/graphs_bundle.js
app/assets/javascripts/graphs/graphs_bundle.js
+6
-2
app/assets/javascripts/graphs/stat_graph_contributors_graph.js
...ssets/javascripts/graphs/stat_graph_contributors_graph.js
+1
-0
app/assets/javascripts/groups_select.js
app/assets/javascripts/groups_select.js
+1
-0
app/assets/javascripts/issuable.js.es6
app/assets/javascripts/issuable.js.es6
+2
-0
app/assets/javascripts/issue.js
app/assets/javascripts/issue.js
+5
-4
app/assets/javascripts/issues-bulk-assignment.js
app/assets/javascripts/issues-bulk-assignment.js
+6
-0
app/assets/javascripts/labels.js
app/assets/javascripts/labels.js
+3
-0
app/assets/javascripts/labels_select.js
app/assets/javascripts/labels_select.js
+9
-0
app/assets/javascripts/lib/chart.js
app/assets/javascripts/lib/chart.js
+0
-1
app/assets/javascripts/lib/cropper.js
app/assets/javascripts/lib/cropper.js
+0
-1
app/assets/javascripts/lib/d3.js
app/assets/javascripts/lib/d3.js
+0
-1
app/assets/javascripts/lib/raphael.js
app/assets/javascripts/lib/raphael.js
+0
-5
app/assets/javascripts/lib/utils/datetime_utility.js
app/assets/javascripts/lib/utils/datetime_utility.js
+1
-0
app/assets/javascripts/lib/utils/emoji_aliases.js.coffee.erb
app/assets/javascripts/lib/utils/emoji_aliases.js.coffee.erb
+0
-2
app/assets/javascripts/lib/utils/emoji_aliases.js.erb
app/assets/javascripts/lib/utils/emoji_aliases.js.erb
+6
-0
app/assets/javascripts/lib/utils/notify.js
app/assets/javascripts/lib/utils/notify.js
+5
-0
app/assets/javascripts/lib/utils/text_utility.js
app/assets/javascripts/lib/utils/text_utility.js
+3
-2
app/assets/javascripts/lib/utils/url_utility.js
app/assets/javascripts/lib/utils/url_utility.js
+6
-0
app/assets/javascripts/line_highlighter.js
app/assets/javascripts/line_highlighter.js
+68
-1
app/assets/javascripts/merge_request.js
app/assets/javascripts/merge_request.js
+11
-4
app/assets/javascripts/merge_request_tabs.js
app/assets/javascripts/merge_request_tabs.js
+86
-1
app/assets/javascripts/merge_request_widget.js
app/assets/javascripts/merge_request_widget.js
+8
-0
app/assets/javascripts/milestone.js
app/assets/javascripts/milestone.js
+1
-0
app/assets/javascripts/milestone_select.js
app/assets/javascripts/milestone_select.js
+1
-0
app/assets/javascripts/network/branch-graph.js
app/assets/javascripts/network/branch-graph.js
+13
-0
app/assets/javascripts/network/network_bundle.js
app/assets/javascripts/network/network_bundle.js
+6
-1
app/assets/javascripts/notes.js
app/assets/javascripts/notes.js
+62
-12
app/assets/javascripts/preview_markdown.js
app/assets/javascripts/preview_markdown.js
+10
-0
app/assets/javascripts/profile/gl_crop.js
app/assets/javascripts/profile/gl_crop.js
+10
-2
app/assets/javascripts/profile/profile.js
app/assets/javascripts/profile/profile.js
+4
-0
app/assets/javascripts/profile/profile_bundle.js
app/assets/javascripts/profile/profile_bundle.js
+0
-1
app/assets/javascripts/project.js
app/assets/javascripts/project.js
+6
-0
app/assets/javascripts/project_find_file.js
app/assets/javascripts/project_find_file.js
+9
-0
app/assets/javascripts/project_show.js
app/assets/javascripts/project_show.js
+2
-0
app/assets/javascripts/projects_list.js
app/assets/javascripts/projects_list.js
+1
-0
app/assets/javascripts/protected_branch_dropdown.js.es6
app/assets/javascripts/protected_branch_dropdown.js.es6
+1
-0
app/assets/javascripts/search_autocomplete.js
app/assets/javascripts/search_autocomplete.js
+22
-0
app/assets/javascripts/shortcuts.js
app/assets/javascripts/shortcuts.js
+1
-0
app/assets/javascripts/shortcuts_find_file.js
app/assets/javascripts/shortcuts_find_file.js
+2
-0
app/assets/javascripts/shortcuts_issuable.js
app/assets/javascripts/shortcuts_issuable.js
+4
-2
app/assets/javascripts/syntax_highlight.js
app/assets/javascripts/syntax_highlight.js
+11
-0
app/assets/javascripts/todos.js
app/assets/javascripts/todos.js
+6
-0
app/assets/javascripts/tree.js
app/assets/javascripts/tree.js
+3
-0
app/assets/javascripts/u2f/authenticate.js
app/assets/javascripts/u2f/authenticate.js
+18
-0
app/assets/javascripts/u2f/register.js
app/assets/javascripts/u2f/register.js
+7
-0
app/assets/javascripts/user_tabs.js
app/assets/javascripts/user_tabs.js
+69
-0
app/assets/javascripts/users/calendar.js
app/assets/javascripts/users/calendar.js
+7
-0
app/assets/javascripts/users/users_bundle.js
app/assets/javascripts/users/users_bundle.js
+0
-1
app/assets/javascripts/users_select.js
app/assets/javascripts/users_select.js
+8
-0
app/assets/javascripts/zen_mode.js
app/assets/javascripts/zen_mode.js
+26
-11
spec/javascripts/awards_handler_spec.js
spec/javascripts/awards_handler_spec.js
+1
-6
spec/javascripts/behaviors/quick_submit_spec.js
spec/javascripts/behaviors/quick_submit_spec.js
+3
-0
spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
.../javascripts/graphs/stat_graph_contributors_graph_spec.js
+4
-4
spec/javascripts/issue_spec.js
spec/javascripts/issue_spec.js
+0
-2
spec/javascripts/new_branch_spec.js
spec/javascripts/new_branch_spec.js
+0
-2
spec/javascripts/project_title_spec.js
spec/javascripts/project_title_spec.js
+0
-12
spec/javascripts/right_sidebar_spec.js
spec/javascripts/right_sidebar_spec.js
+0
-4
spec/javascripts/search_autocomplete_spec.js
spec/javascripts/search_autocomplete_spec.js
+4
-10
spec/javascripts/shortcuts_issuable_spec.js
spec/javascripts/shortcuts_issuable_spec.js
+1
-0
spec/javascripts/spec_helper.js
spec/javascripts/spec_helper.js
+32
-12
spec/javascripts/u2f/authenticate_spec.js
spec/javascripts/u2f/authenticate_spec.js
+0
-8
spec/javascripts/u2f/register_spec.js
spec/javascripts/u2f/register_spec.js
+0
-8
spec/javascripts/zen_mode_spec.js
spec/javascripts/zen_mode_spec.js
+3
-1
vendor/assets/javascripts/task_list.js
vendor/assets/javascripts/task_list.js
+150
-11
No files found.
app/assets/javascripts/LabelManager.js
View file @
7f6474b2
...
...
@@ -3,6 +3,7 @@
LabelManager
.
prototype
.
errorMessage
=
'
Unable to update label prioritization at this time
'
;
function
LabelManager
(
opts
)
{
// Defaults
var
ref
,
ref1
,
ref2
;
if
(
opts
==
null
)
{
opts
=
{};
...
...
@@ -28,6 +29,7 @@
$btn
=
$
(
e
.
currentTarget
);
$label
=
$
(
"
#
"
+
(
$btn
.
data
(
'
domId
'
)));
action
=
$btn
.
parents
(
'
.js-prioritized-labels
'
).
length
?
'
remove
'
:
'
add
'
;
// Make sure tooltip will hide
$tooltip
=
$
(
"
#
"
+
(
$btn
.
find
(
'
.has-tooltip:visible
'
).
attr
(
'
aria-describedby
'
)));
$tooltip
.
tooltip
(
'
destroy
'
);
return
_this
.
toggleLabelPriority
(
$label
,
action
);
...
...
@@ -42,6 +44,7 @@
url
=
$label
.
find
(
'
.js-toggle-priority
'
).
data
(
'
url
'
);
$target
=
this
.
prioritizedLabels
;
$from
=
this
.
otherLabels
;
// Optimistic update
if
(
action
===
'
remove
'
)
{
$target
=
this
.
otherLabels
;
$from
=
this
.
prioritizedLabels
;
...
...
@@ -53,6 +56,7 @@
$target
.
find
(
'
.empty-message
'
).
addClass
(
'
hidden
'
);
}
$label
.
detach
().
appendTo
(
$target
);
// Return if we are not persisting state
if
(
!
persistState
)
{
return
;
}
...
...
@@ -61,6 +65,7 @@
url
:
url
,
type
:
'
DELETE
'
});
// Restore empty message
if
(
!
$from
.
find
(
'
li
'
).
length
)
{
$from
.
find
(
'
.empty-message
'
).
removeClass
(
'
hidden
'
);
}
...
...
app/assets/javascripts/api.js
View file @
7f6474b2
...
...
@@ -24,6 +24,8 @@
return
callback
(
group
);
});
},
// Return groups list. Filtered by query
// Only active groups retrieved
groups
:
function
(
query
,
skip_ldap
,
callback
)
{
var
url
=
Api
.
buildUrl
(
Api
.
groupsPath
);
return
$
.
ajax
({
...
...
@@ -38,6 +40,7 @@
return
callback
(
groups
);
});
},
// Return namespaces list. Filtered by query
namespaces
:
function
(
query
,
callback
)
{
var
url
=
Api
.
buildUrl
(
Api
.
namespacesPath
);
return
$
.
ajax
({
...
...
@@ -52,6 +55,7 @@
return
callback
(
namespaces
);
});
},
// Return projects list. Filtered by query
projects
:
function
(
query
,
order
,
callback
)
{
var
url
=
Api
.
buildUrl
(
Api
.
projectsPath
);
return
$
.
ajax
({
...
...
@@ -82,6 +86,7 @@
return
callback
(
message
.
responseJSON
);
});
},
// Return group projects list. Filtered by query
groupProjects
:
function
(
group_id
,
query
,
callback
)
{
var
url
=
Api
.
buildUrl
(
Api
.
groupProjectsPath
)
.
replace
(
'
:id
'
,
group_id
);
...
...
@@ -97,6 +102,7 @@
return
callback
(
projects
);
});
},
// Return text for a specific license
licenseText
:
function
(
key
,
data
,
callback
)
{
var
url
=
Api
.
buildUrl
(
Api
.
licensePath
)
.
replace
(
'
:key
'
,
key
);
...
...
app/assets/javascripts/application.js
View file @
7f6474b2
// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// 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 jquery2 */
/*= require jquery-ui/autocomplete */
/*= require jquery-ui/datepicker */
...
...
@@ -76,6 +82,7 @@
}
};
// Disable button if text field is empty
window
.
disableButtonIfEmptyField
=
function
(
field_selector
,
button_selector
)
{
var
closest_submit
,
field
;
field
=
$
(
field_selector
);
...
...
@@ -92,6 +99,7 @@
});
};
// Disable button if any input field with given selector is empty
window
.
disableButtonIfAnyEmptyField
=
function
(
form
,
form_selector
,
button_selector
)
{
var
closest_submit
,
updateButtons
;
closest_submit
=
form
.
find
(
button_selector
);
...
...
@@ -128,6 +136,8 @@
window
.
addEventListener
(
"
hashchange
"
,
shiftWindow
);
window
.
onload
=
function
()
{
// Scroll the window to avoid the topnav bar
// https://github.com/twitter/bootstrap/issues/1768
if
(
location
.
hash
)
{
return
setTimeout
(
shiftWindow
,
100
);
}
...
...
@@ -149,6 +159,8 @@
return
$
(
this
).
select
().
one
(
'
mouseup
'
,
function
(
e
)
{
return
e
.
preventDefault
();
});
// Click a .js-select-on-focus field, select the contents
// Prevent a mouseup event from deselecting the input
});
$
(
'
.remove-row
'
).
bind
(
'
ajax:success
'
,
function
()
{
$
(
this
).
tooltip
(
'
destroy
'
)
...
...
@@ -163,6 +175,7 @@
});
$
(
'
select.select2
'
).
select2
({
width
:
'
resolve
'
,
// Initialize select2 selects
dropdownAutoWidth
:
true
});
$
(
'
.js-select2
'
).
bind
(
'
select2-close
'
,
function
()
{
...
...
@@ -170,7 +183,9 @@
$
(
'
.select2-container-active
'
).
removeClass
(
'
select2-container-active
'
);
return
$
(
'
:focus
'
).
blur
();
}),
1
);
// Close select2 on escape
});
// Initialize tooltips
$body
.
tooltip
({
selector
:
'
.has-tooltip, [data-toggle="tooltip"]
'
,
placement
:
function
(
_
,
el
)
{
...
...
@@ -179,14 +194,17 @@
});
$
(
'
.trigger-submit
'
).
on
(
'
change
'
,
function
()
{
return
$
(
this
).
parents
(
'
form
'
).
submit
();
// Form submitter
});
gl
.
utils
.
localTimeAgo
(
$
(
'
abbr.timeago, .js-timeago
'
),
true
);
// Flash
if
((
flash
=
$
(
"
.flash-container
"
)).
length
>
0
)
{
flash
.
click
(
function
()
{
return
$
(
this
).
fadeOut
();
});
flash
.
show
();
}
// Disable form buttons while a form is submitting
$body
.
on
(
'
ajax:complete, ajax:beforeSend, submit
'
,
'
form
'
,
function
(
e
)
{
var
buttons
;
buttons
=
$
(
'
[type="submit"]
'
,
this
);
...
...
@@ -207,6 +225,7 @@
}
});
$
(
'
.account-box
'
).
hover
(
function
()
{
// Show/Hide the profile menu when hovering the account box
return
$
(
this
).
toggleClass
(
'
hover
'
);
});
$document
.
on
(
'
click
'
,
'
.diff-content .js-show-suppressed-diff
'
,
function
()
{
...
...
@@ -214,6 +233,7 @@
$container
=
$
(
this
).
parent
();
$container
.
next
(
'
table
'
).
show
();
return
$container
.
remove
();
// Commit show suppressed diff
});
$
(
'
.navbar-toggle
'
).
on
(
'
click
'
,
function
()
{
$
(
'
.header-content .title
'
).
toggle
();
...
...
@@ -221,6 +241,7 @@
$
(
'
.header-content .navbar-collapse
'
).
toggle
();
return
$
(
'
.navbar-toggle
'
).
toggleClass
(
'
active
'
);
});
// Show/hide comments on diff
$body
.
on
(
"
click
"
,
"
.js-toggle-diff-comments
"
,
function
(
e
)
{
var
$this
=
$
(
this
);
$this
.
toggleClass
(
'
active
'
);
...
...
app/assets/javascripts/autosave.js
View file @
7f6474b2
...
...
@@ -16,7 +16,7 @@
}
Autosave
.
prototype
.
restore
=
function
()
{
var
e
,
error
,
text
;
var
e
,
text
;
if
(
window
.
localStorage
==
null
)
{
return
;
}
...
...
@@ -41,7 +41,7 @@
if
((
text
!=
null
?
text
.
length
:
void
0
)
>
0
)
{
try
{
return
window
.
localStorage
.
setItem
(
this
.
key
,
text
);
}
catch
(
undefined
)
{}
}
catch
(
error
)
{}
}
else
{
return
this
.
reset
();
}
...
...
@@ -53,7 +53,7 @@
}
try
{
return
window
.
localStorage
.
removeItem
(
this
.
key
);
}
catch
(
undefined
)
{}
}
catch
(
error
)
{}
};
return
Autosave
;
...
...
app/assets/javascripts/awards_handler.js
View file @
7f6474b2
...
...
@@ -86,6 +86,8 @@
AwardsHandler
.
prototype
.
positionMenu
=
function
(
$menu
,
$addBtn
)
{
var
css
,
position
;
position
=
$addBtn
.
data
(
'
position
'
);
// The menu could potentially be off-screen or in a hidden overflow element
// So we position the element absolute in the body
css
=
{
top
:
(
$addBtn
.
offset
().
top
+
$addBtn
.
outerHeight
())
+
"
px
"
};
...
...
@@ -284,6 +286,7 @@
if
(
emojiIcon
.
length
>
0
)
{
unicodeName
=
emojiIcon
.
data
(
'
unicode-name
'
);
}
else
{
// Find by alias
unicodeName
=
$
(
"
.emoji-menu-content [data-aliases*=':
"
+
emoji
+
"
:']
"
).
data
(
'
unicode-name
'
);
}
return
"
emoji-
"
+
unicodeName
;
...
...
@@ -350,8 +353,10 @@
return
function
(
ev
)
{
var
found_emojis
,
h5
,
term
,
ul
;
term
=
$
(
ev
.
target
).
val
();
// Clean previous search results
$
(
'
ul.emoji-menu-search, h5.emoji-search
'
).
remove
();
if
(
term
)
{
// Generate a search result block
h5
=
$
(
'
<h5>
'
).
text
(
'
Search results
'
);
found_emojis
=
_this
.
searchEmojis
(
term
).
show
();
ul
=
$
(
'
<ul>
'
).
addClass
(
'
emoji-menu-list emoji-menu-search
'
).
append
(
found_emojis
);
...
...
app/assets/javascripts/behaviors/autosize.js
View file @
7f6474b2
/*= require jquery.ba-resize */
/*= require autosize */
(
function
()
{
...
...
app/assets/javascripts/behaviors/details_behavior.js
View file @
7f6474b2
...
...
@@ -5,6 +5,12 @@
container
=
$
(
this
).
closest
(
"
.js-details-container
"
);
return
container
.
toggleClass
(
"
open
"
);
});
// Show details content. Hides link after click.
//
// %div
// %a.js-details-expand
// %div.js-details-content
//
return
$
(
"
body
"
).
on
(
"
click
"
,
"
.js-details-expand
"
,
function
(
e
)
{
$
(
this
).
next
(
'
.js-details-content
'
).
removeClass
(
"
hide
"
);
$
(
this
).
hide
();
...
...
app/assets/javascripts/behaviors/quick_submit.js
View file @
7f6474b2
// Quick Submit behavior
//
// When a child field of a form with a `js-quick-submit` class receives a
// "Meta+Enter" (Mac) or "Ctrl+Enter" (Linux/Windows) key combination, the form
// is submitted.
//
/*= require extensions/jquery */
//
// ### Example Markup
//
// <form action="/foo" class="js-quick-submit">
// <input type="text" />
// <textarea></textarea>
// <input type="submit" value="Submit" />
// </form>
//
(
function
()
{
var
isMac
,
keyCodeIs
;
...
...
@@ -17,6 +31,7 @@
$
(
document
).
on
(
'
keydown.quick_submit
'
,
'
.js-quick-submit
'
,
function
(
e
)
{
var
$form
,
$submit_button
;
// Enter
if
(
!
keyCodeIs
(
e
,
13
))
{
return
;
}
...
...
@@ -33,8 +48,11 @@
return
$form
.
submit
();
});
// If the user tabs to a submit button on a `js-quick-submit` form, display a
// tooltip to let them know they could've used the hotkey
$
(
document
).
on
(
'
keyup.quick_submit
'
,
'
.js-quick-submit input[type=submit], .js-quick-submit button[type=submit]
'
,
function
(
e
)
{
var
$this
,
title
;
// Tab
if
(
!
keyCodeIs
(
e
,
9
))
{
return
;
}
...
...
app/assets/javascripts/behaviors/requires_input.js
View file @
7f6474b2
// Requires Input behavior
//
// When called on a form with input fields with the `required` attribute, the
// form's submit button will be disabled until all required fields have values.
//
/*= require extensions/jquery */
//
// ### Example Markup
//
// <form class="js-requires-input">
// <input type="text" required="required">
// <input type="submit" value="Submit">
// </form>
//
(
function
()
{
$
.
fn
.
requiresInput
=
function
()
{
var
$button
,
$form
,
fieldSelector
,
requireInput
,
required
;
...
...
@@ -11,14 +23,17 @@
requireInput
=
function
()
{
var
values
;
values
=
_
.
map
(
$
(
fieldSelector
,
$form
),
function
(
field
)
{
// Collect the input values of *all* required fields
return
field
.
value
;
});
// Disable the button if any required fields are empty
if
(
values
.
length
&&
_
.
any
(
values
,
_
.
isEmpty
))
{
return
$button
.
disable
();
}
else
{
return
$button
.
enable
();
}
};
// Set initial button state
requireInput
();
return
$form
.
on
(
'
change input
'
,
fieldSelector
,
requireInput
);
};
...
...
@@ -27,6 +42,8 @@
var
$form
,
hideOrShowHelpBlock
;
$form
=
$
(
'
form.js-requires-input
'
);
$form
.
requiresInput
();
// Hide or Show the help block when creating a new project
// based on the option selected
hideOrShowHelpBlock
=
function
(
form
)
{
var
selected
;
selected
=
$
(
'
.js-select-namespace option:selected
'
);
...
...
app/assets/javascripts/behaviors/toggler_behavior.js
View file @
7f6474b2
(
function
(
w
)
{
$
(
function
()
{
// Toggle button. Show/hide content inside parent container.
// Button does not change visibility. If button has icon - it changes chevron style.
//
// %div.js-toggle-container
// %a.js-toggle-button
// %div.js-toggle-content
//
$
(
'
body
'
).
on
(
'
click
'
,
'
.js-toggle-button
'
,
function
(
e
)
{
e
.
preventDefault
();
$
(
this
)
...
...
app/assets/javascripts/blob/blob_file_dropzone.js
View file @
7f6474b2
...
...
@@ -8,6 +8,8 @@
autoDiscover
:
false
,
autoProcessQueue
:
false
,
url
:
form
.
attr
(
'
action
'
),
// Rails uses a hidden input field for PUT
// http://stackoverflow.com/questions/21056482/how-to-set-method-put-in-form-tag-in-rails
method
:
method
,
clickable
:
true
,
uploadMultiple
:
false
,
...
...
@@ -36,6 +38,7 @@
formData
.
append
(
'
commit_message
'
,
form
.
find
(
'
.js-commit-message
'
).
val
());
});
},
// Override behavior of adding error underneath preview
error
:
function
(
file
,
errorMessage
)
{
var
stripped
;
stripped
=
$
(
"
<div/>
"
).
html
(
errorMessage
).
text
();
...
...
app/assets/javascripts/blob/template_selector.js
View file @
7f6474b2
...
...
@@ -66,6 +66,9 @@
// be added by all subclasses.
};
// To be implemented on the extending class
// e.g.
// Api.gitignoreText item.name, @requestFileSuccess.bind(@)
TemplateSelector
.
prototype
.
requestFileSuccess
=
function
(
file
,
skipFocus
)
{
this
.
editor
.
setValue
(
file
.
content
,
1
);
if
(
!
skipFocus
)
this
.
editor
.
focus
();
...
...
app/assets/javascripts/blob_edit/edit_blob.js
View file @
7f6474b2
...
...
@@ -18,6 +18,8 @@
return
function
()
{
return
$
(
"
#file-content
"
).
val
(
_this
.
editor
.
getValue
());
};
// Before a form submission, move the content from the Ace editor into the
// submitted textarea
})(
this
));
this
.
initModePanesAndLinks
();
new
BlobLicenseSelectors
({
...
...
app/assets/javascripts/breakpoints.js
View file @
7f6474b2
...
...
@@ -23,6 +23,7 @@
if
(
$
(
allDeviceSelector
.
join
(
"
,
"
)).
length
)
{
return
;
}
// Create all the elements
els
=
$
.
map
(
BREAKPOINTS
,
function
(
breakpoint
)
{
return
"
<div class='device-
"
+
breakpoint
+
"
visible-
"
+
breakpoint
+
"
'></div>
"
;
});
...
...
@@ -40,6 +41,7 @@
BreakpointInstance
.
prototype
.
getBreakpointSize
=
function
()
{
var
$visibleDevice
;
$visibleDevice
=
this
.
visibleDevice
;
// the page refreshed via turbolinks
if
(
!
$visibleDevice
().
length
)
{
this
.
setup
();
}
...
...
app/assets/javascripts/build.js
View file @
7f6474b2
...
...
@@ -16,6 +16,7 @@
this
.
toggleSidebar
=
bind
(
this
.
toggleSidebar
,
this
);
this
.
updateDropdown
=
bind
(
this
.
updateDropdown
,
this
);
clearInterval
(
Build
.
interval
);
// Init breakpoint checker
this
.
bp
=
Breakpoints
.
get
();
$
(
'
.js-build-sidebar
'
).
niceScroll
();
...
...
@@ -42,6 +43,9 @@
$
(
this
).
data
(
"
state
"
,
"
enabled
"
);
return
$
(
this
).
text
(
"
disable autoscroll
"
);
}
//
// Bind autoscroll button to follow build output
//
});
Build
.
interval
=
setInterval
((
function
(
_this
)
{
return
function
()
{
...
...
@@ -49,6 +53,10 @@
return
_this
.
getBuildTrace
();
}
};
//
// Check for new build output if user still watching build page
// Only valid for runnig build when output changes during time
//
})(
this
),
4000
);
}
}
...
...
app/assets/javascripts/commit/image-file.js
View file @
7f6474b2
...
...
@@ -2,6 +2,7 @@
this
.
ImageFile
=
(
function
()
{
var
prepareFrames
;
// Width where images must fits in, for 2-up this gets divided by 2
ImageFile
.
availWidth
=
900
;
ImageFile
.
viewModes
=
[
'
two-up
'
,
'
swipe
'
];
...
...
@@ -9,6 +10,7 @@
function
ImageFile
(
file
)
{
this
.
file
=
file
;
this
.
requestImageInfo
(
$
(
'
.two-up.view .frame.deleted img
'
,
this
.
file
),
(
function
(
_this
)
{
// Determine if old and new file has same dimensions, if not show 'two-up' view
return
function
(
deletedWidth
,
deletedHeight
)
{
return
_this
.
requestImageInfo
(
$
(
'
.two-up.view .frame.added img
'
,
_this
.
file
),
function
(
width
,
height
)
{
if
(
width
===
deletedWidth
&&
height
===
deletedHeight
)
{
...
...
app/assets/javascripts/commits.js
View file @
7f6474b2
...
...
@@ -45,6 +45,7 @@
CommitsList
.
content
.
html
(
data
.
html
);
return
history
.
replaceState
({
page
:
commitsUrl
// Change url so if user reload a page - search results are saved
},
document
.
title
,
commitsUrl
);
},
dataType
:
"
json
"
...
...
app/assets/javascripts/copy_to_clipboard.js
View file @
7f6474b2
...
...
@@ -6,14 +6,19 @@
genericSuccess
=
function
(
e
)
{
showTooltip
(
e
.
trigger
,
'
Copied!
'
);
// Clear the selection and blur the trigger so it loses its border
e
.
clearSelection
();
return
$
(
e
.
trigger
).
blur
();
};
// Safari doesn't support `execCommand`, so instead we inform the user to
// copy manually.
//
// See http://clipboardjs.com/#browser-support
genericError
=
function
(
e
)
{
var
key
;
if
(
/Mac/i
.
test
(
navigator
.
userAgent
))
{
key
=
'
⌘
'
;
key
=
'
⌘
'
;
// Command
}
else
{
key
=
'
Ctrl
'
;
}
...
...
app/assets/javascripts/diff.js
View file @
7f6474b2
...
...
@@ -39,6 +39,9 @@
bottom
:
unfoldBottom
,
offset
:
offset
,
unfold
:
unfold
,
// indent is used to compensate for single space indent to fit
// '+' and '-' prepended to diff lines,
// see https://gitlab.com/gitlab-org/gitlab-ce/issues/707
indent
:
1
,
view
:
file
.
data
(
'
view
'
)
};
...
...
app/assets/javascripts/dispatcher.js
View file @
7f6474b2
...
...
@@ -164,6 +164,8 @@
}
break
;
case
'
projects:network:show
'
:
// Ensure we don't create a particular shortcut handler here. This is
// already created, where the network graph is created.
shortcut_handler
=
true
;
break
;
case
'
projects:forks:new
'
:
...
...
@@ -260,12 +262,14 @@
shortcut_handler
=
new
ShortcutsNavigation
();
}
}
// If we haven't installed a custom shortcut handler, install the default one
if
(
!
shortcut_handler
)
{
return
new
Shortcuts
();
}
};
Dispatcher
.
prototype
.
initSearch
=
function
()
{
// Only when search form is present
if
(
$
(
'
.search
'
).
length
)
{
return
new
SearchAutocomplete
();
}
...
...
app/assets/javascripts/due_date_select.js
View file @
7f6474b2
...
...
@@ -2,6 +2,7 @@
this
.
DueDateSelect
=
(
function
()
{
function
DueDateSelect
()
{
var
$datePicker
,
$dueDate
,
$loading
;
// Milestone edit/new form
$datePicker
=
$
(
'
.datepicker
'
);
if
(
$datePicker
.
length
)
{
$dueDate
=
$
(
'
#milestone_due_date
'
);
...
...
@@ -16,6 +17,7 @@
e
.
preventDefault
();
return
$
.
datepicker
.
_clearDate
(
$datePicker
);
});
// Issuable sidebar
$loading
=
$
(
'
.js-issuable-update .due_date
'
).
find
(
'
.block-loading
'
).
hide
();
$
(
'
.js-due-date-select
'
).
each
(
function
(
i
,
dropdown
)
{
var
$block
,
$dropdown
,
$dropdownParent
,
$selectbox
,
$sidebarValue
,
$value
,
$valueContent
,
abilityName
,
addDueDate
,
fieldName
,
issueUpdateURL
;
...
...
@@ -38,6 +40,7 @@
});
addDueDate
=
function
(
isDropdown
)
{
var
data
,
date
,
mediumDate
,
value
;
// Create the post date
value
=
$
(
"
input[name='
"
+
fieldName
+
"
']
"
).
val
();
if
(
value
!==
''
)
{
date
=
new
Date
(
value
.
replace
(
new
RegExp
(
'
-
'
,
'
g
'
),
'
,
'
));
...
...
app/assets/javascripts/extensions/jquery.js
View file @
7f6474b2
// Disable an element and add the 'disabled' Bootstrap class
(
function
()
{
$
.
fn
.
extend
({
disable
:
function
()
{
...
...
@@ -5,6 +6,7 @@
}
});
// Enable an element and remove the 'disabled' Bootstrap class
$
.
fn
.
extend
({
enable
:
function
()
{
return
$
(
this
).
removeAttr
(
'
disabled
'
).
removeClass
(
'
disabled
'
);
...
...
app/assets/javascripts/gfm_auto_complete.js.es6
View file @
7f6474b2
// Creates the variables for setting up GFM auto-completion
(function() {
if (window.GitLab == null) {
window.GitLab = {};
...
...
@@ -8,18 +9,22 @@
dataLoaded: false,
cachedData: {},
dataSource: '',
// Emoji
Emoji: {
template: '<li>${name} <img alt="${name}" height="20" src="${path}" width="20" /></li>'
},
// Team Members
Members: {
template: '<li>${username} <small>${title}</small></li>'
},
Labels: {
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>'
},
// Issues and MergeRequests
Issues: {
template: '<li><small>${id}</small> ${title}</li>'
},
// Milestones
Milestones: {
template: '<li>${title}</li>'
},
...
...
@@ -48,8 +53,11 @@
}
},
setup: function(input) {
// Add GFM auto-completion to all input fields, that accept GFM input.
this.input = input || $('.js-gfm-input');
// destroy previous instances
this.destroyAtWho();
// set up instances
this.setupAtWho();
if (this.dataSource) {
if (!this.dataLoading && !this.cachedData) {
...
...
@@ -63,6 +71,11 @@
return _this.loadData(data);
});
};
// We should wait until initializations are done
// and only trigger the last .setup since
// The previous .dataSource belongs to the previous issuable
// and the last one will have the **proper** .dataSource property
// TODO: Make this a singleton and turn off events when moving to another page
})(this), 1000);
}
if (this.cachedData != null) {
...
...
@@ -71,6 +84,7 @@
}
},
setupAtWho: function() {
// Emoji
this.input.atwho({
at: ':',
displayTpl: (function(_this) {
...
...
@@ -90,6 +104,7 @@
beforeInsert: this.DefaultOptions.beforeInsert
}
});
// Team Members
this.input.atwho({
at: '@',
displayTpl: (function(_this) {
...
...
@@ -321,13 +336,22 @@
loadData: function(data) {
this.cachedData = data;
this.dataLoaded = true;
// load members
this.input.atwho('load', '@', data.members);
// load issues
this.input.atwho('load', 'issues', data.issues);
// load milestones
this.input.atwho('load', 'milestones', data.milestones);
// load merge requests
this.input.atwho('load', 'mergerequests', data.mergerequests);
// load emojis
this.input.atwho('load', ':', data.emojis);
// load labels
this.input.atwho('load', '~', data.labels);
// load commands
this.input.atwho('load', '/', data.commands);
// This trigger at.js again
// otherwise we would be stuck with loading until the user types
return $(':focus').trigger('keyup');
}
};
...
...
app/assets/javascripts/gl_dropdown.js
View file @
7f6474b2
...
...
@@ -21,12 +21,14 @@
$clearButton
=
$inputContainer
.
find
(
'
.js-dropdown-input-clear
'
);
this
.
indeterminateIds
=
[];
$clearButton
.
on
(
'
click
'
,
(
function
(
_this
)
{
// Clear click
return
function
(
e
)
{
e
.
preventDefault
();
e
.
stopPropagation
();
return
_this
.
input
.
val
(
''
).
trigger
(
'
keyup
'
).
focus
();
};
})(
this
));
// Key events
timeout
=
""
;
this
.
input
.
on
(
'
keydown
'
,
function
(
e
)
{
...
...
@@ -49,6 +51,7 @@
if
(
keyCode
===
13
&&
!
options
.
elIsInput
)
{
return
false
;
}
// Only filter asynchronously only if option remote is set
if
(
this
.
options
.
remote
)
{
clearTimeout
(
timeout
);
return
timeout
=
setTimeout
(
function
()
{
...
...
@@ -79,11 +82,27 @@
if
((
data
!=
null
)
&&
!
this
.
options
.
filterByText
)
{
results
=
data
;
if
(
search_text
!==
''
)
{
// When data is an array of objects therefore [object Array] e.g.
// [
// { prop: 'foo' },
// { prop: 'baz' }
// ]
if
(
_
.
isArray
(
data
))
{
results
=
fuzzaldrinPlus
.
filter
(
data
,
search_text
,
{
key
:
this
.
options
.
keys
});
}
else
{
// If data is grouped therefore an [object Object]. e.g.
// {
// groupName1: [
// { prop: 'foo' },
// { prop: 'baz' }
// ],
// groupName2: [
// { prop: 'abc' },
// { prop: 'def' }
// ]
// }
if
(
gl
.
utils
.
isObject
(
data
))
{
results
=
{};
for
(
key
in
data
)
{
...
...
@@ -140,6 +159,7 @@
this
.
options
.
beforeSend
();
}
return
this
.
dataEndpoint
(
""
,
(
function
(
_this
)
{
// Fetch the data by calling the data funcfion
return
function
(
data
)
{
if
(
_this
.
options
.
success
)
{
_this
.
options
.
success
(
data
);
...
...
@@ -171,6 +191,7 @@
};
})(
this
)
});
// Fetch the data through ajax if the data is a string
};
return
GitLabDropdownRemote
;
...
...
@@ -209,13 +230,18 @@
self
=
this
;
selector
=
$
(
this
.
el
).
data
(
"
target
"
);
this
.
dropdown
=
selector
!=
null
?
$
(
selector
)
:
$
(
this
.
el
).
parent
();
// Set Defaults
ref
=
this
.
options
,
this
.
filterInput
=
(
ref1
=
ref
.
filterInput
)
!=
null
?
ref1
:
this
.
getElement
(
FILTER_INPUT
),
this
.
highlight
=
(
ref2
=
ref
.
highlight
)
!=
null
?
ref2
:
false
,
this
.
filterInputBlur
=
(
ref3
=
ref
.
filterInputBlur
)
!=
null
?
ref3
:
true
;
// If no input is passed create a default one
self
=
this
;
// If selector was passed
if
(
_
.
isString
(
this
.
filterInput
))
{
this
.
filterInput
=
this
.
getElement
(
this
.
filterInput
);
}
searchFields
=
this
.
options
.
search
?
this
.
options
.
search
.
fields
:
[];
if
(
this
.
options
.
data
)
{
// If we provided data
// data could be an array of objects or a group of arrays
if
(
_
.
isObject
(
this
.
options
.
data
)
&&
!
_
.
isFunction
(
this
.
options
.
data
))
{
this
.
fullData
=
this
.
options
.
data
;
currentIndex
=
-
1
;
...
...
@@ -232,10 +258,12 @@
return
_this
.
filter
.
input
.
trigger
(
'
keyup
'
);
}
};
// Remote data
})(
this
)
});
}
}
// Init filterable
if
(
this
.
options
.
filterable
)
{
this
.
filter
=
new
GitLabDropdownFilter
(
this
.
filterInput
,
{
elIsInput
:
$
(
this
.
el
).
is
(
'
input
'
),
...
...
@@ -278,12 +306,14 @@
})(
this
)
});
}
// Event listeners
this
.
dropdown
.
on
(
"
shown.bs.dropdown
"
,
this
.
opened
);
this
.
dropdown
.
on
(
"
hidden.bs.dropdown
"
,
this
.
hidden
);
$
(
this
.
el
).
on
(
"
update.label
"
,
this
.
updateLabel
);
this
.
dropdown
.
on
(
"
click
"
,
"
.dropdown-menu, .dropdown-menu-close
"
,
this
.
shouldPropagate
);
this
.
dropdown
.
on
(
'
keyup
'
,
(
function
(
_this
)
{
return
function
(
e
)
{
// Escape key
if
(
e
.
which
===
27
)
{
return
$
(
'
.dropdown-menu-close
'
,
_this
.
dropdown
).
trigger
(
'
click
'
);
}
...
...
@@ -327,6 +357,7 @@
}
}
// Finds an element inside wrapper element
GitLabDropdown
.
prototype
.
getElement
=
function
(
selector
)
{
return
this
.
dropdown
.
find
(
selector
);
};
...
...
@@ -344,6 +375,7 @@
}
}
menu
.
toggleClass
(
PAGE_TWO_CLASS
);
// Focus first visible input on active page
return
this
.
dropdown
.
find
(
'
[class^="dropdown-page-"]:visible :text:visible:first
'
).
focus
();
};
...
...
@@ -351,23 +383,28 @@
var
full_html
,
groupData
,
html
,
name
;
this
.
renderedData
=
data
;
if
(
this
.
options
.
filterable
&&
data
.
length
===
0
)
{
// render no matching results
html
=
[
this
.
noResults
()];
}
else
{
// Handle array groups
if
(
gl
.
utils
.
isObject
(
data
))
{
html
=
[];
for
(
name
in
data
)
{
groupData
=
data
[
name
];
html
.
push
(
this
.
renderItem
({
header
:
name
// Add header for each group
},
name
));
this
.
renderData
(
groupData
,
name
).
map
(
function
(
item
)
{
return
html
.
push
(
item
);
});
}
}
else
{
// Render each row
html
=
this
.
renderData
(
data
);
}
}
// Render the full menu
full_html
=
this
.
renderMenu
(
html
);
return
this
.
appendMenu
(
full_html
);
};
...
...
@@ -406,6 +443,7 @@
if
(
this
.
options
.
setActiveIds
)
{
this
.
options
.
setActiveIds
.
call
(
this
);
}
// Makes indeterminate items effective
if
(
this
.
fullData
&&
this
.
dropdown
.
find
(
'
.dropdown-menu-toggle
'
).
hasClass
(
'
js-filter-bulk-update
'
))
{
this
.
parseData
(
this
.
fullData
);
}
...
...
@@ -427,6 +465,8 @@
if
(
this
.
options
.
filterable
)
{
$input
.
blur
().
val
(
""
);
}
// Triggering 'keyup' will re-render the dropdown which is not always required
// specially if we want to keep the state of the dropdown needed for bulk-assignment
if
(
!
this
.
options
.
persistWhenHide
)
{
$input
.
trigger
(
"
keyup
"
);
}
...
...
@@ -439,6 +479,7 @@
return
this
.
dropdown
.
trigger
(
'
hidden.gl.dropdown
'
);
};
// Render the full menu
GitLabDropdown
.
prototype
.
renderMenu
=
function
(
html
)
{
var
menu_html
;
menu_html
=
""
;
...
...
@@ -450,6 +491,7 @@
return
menu_html
;
};
// Append the menu into the dropdown
GitLabDropdown
.
prototype
.
appendMenu
=
function
(
html
)
{
var
selector
;
selector
=
'
.dropdown-content
'
;
...
...
@@ -465,19 +507,24 @@
group
=
false
;
}
if
(
index
==
null
)
{
// Render the row
index
=
false
;
}
html
=
""
;
// Divider
if
(
data
===
"
divider
"
)
{
return
"
<li class='divider'></li>
"
;
}
// Separator is a full-width divider
if
(
data
===
"
separator
"
)
{
return
"
<li class='separator'></li>
"
;
}
// Header
if
(
data
.
header
!=
null
)
{
return
_
.
template
(
'
<li class="dropdown-header"><%- header %></li>
'
)({
header
:
data
.
header
});
}
if
(
this
.
options
.
renderRow
)
{
// Call the render function
html
=
this
.
options
.
renderRow
.
call
(
this
.
options
,
data
,
this
);
}
else
{
if
(
!
selected
)
{
...
...
@@ -489,11 +536,13 @@
selected
=
true
;
}
}
// Set URL
if
(
this
.
options
.
url
!=
null
)
{
url
=
this
.
options
.
url
(
data
);
}
else
{
url
=
data
.
url
!=
null
?
data
.
url
:
'
#
'
;
}
// Set Text
if
(
this
.
options
.
text
!=
null
)
{
text
=
this
.
options
.
text
(
data
);
}
else
{
...
...
@@ -584,6 +633,7 @@
if
(
value
==
null
)
{
field
.
remove
();
}
// Toggle active class for the tick mark
el
.
addClass
(
ACTIVE_CLASS
);
if
(
value
!=
null
)
{
if
(
!
field
.
length
&&
fieldName
)
{
...
...
@@ -604,6 +654,7 @@
GitLabDropdown
.
prototype
.
addInput
=
function
(
fieldName
,
value
,
selectedObject
)
{
var
$input
;
// Create hidden input for form
$input
=
$
(
'
<input>
'
).
attr
(
'
type
'
,
'
hidden
'
).
attr
(
'
name
'
,
fieldName
).
val
(
value
);
if
(
this
.
options
.
inputId
!=
null
)
{
$input
.
attr
(
'
id
'
,
this
.
options
.
inputId
);
...
...
@@ -625,6 +676,7 @@
if
(
this
.
dropdown
.
find
(
"
.dropdown-toggle-page
"
).
length
)
{
selector
=
"
.dropdown-page-one
"
+
selector
;
}
// simulate a click on the first link
$el
=
$
(
selector
,
this
.
dropdown
);
if
(
$el
.
length
)
{
var
href
=
$el
.
attr
(
'
href
'
);
...
...
@@ -653,11 +705,15 @@
e
.
stopImmediatePropagation
();
PREV_INDEX
=
currentIndex
;
$listItems
=
$
(
selector
,
_this
.
dropdown
);
// if @options.filterable
// $input.blur()
if
(
currentKeyCode
===
40
)
{
// Move down
if
(
currentIndex
<
(
$listItems
.
length
-
1
))
{
currentIndex
+=
1
;
}
}
else
if
(
currentKeyCode
===
38
)
{
// Move up
if
(
currentIndex
>
0
)
{
currentIndex
-=
1
;
}
...
...
@@ -685,24 +741,32 @@
GitLabDropdown
.
prototype
.
highlightRowAtIndex
=
function
(
$listItems
,
index
)
{
var
$dropdownContent
,
$listItem
,
dropdownContentBottom
,
dropdownContentHeight
,
dropdownContentTop
,
dropdownScrollTop
,
listItemBottom
,
listItemHeight
,
listItemTop
;
// Remove the class for the previously focused row
$
(
'
.is-focused
'
,
this
.
dropdown
).
removeClass
(
'
is-focused
'
);
// Update the class for the row at the specific index
$listItem
=
$listItems
.
eq
(
index
);
$listItem
.
find
(
'
a:first-child
'
).
addClass
(
"
is-focused
"
);
// Dropdown content scroll area
$dropdownContent
=
$listItem
.
closest
(
'
.dropdown-content
'
);
dropdownScrollTop
=
$dropdownContent
.
scrollTop
();
dropdownContentHeight
=
$dropdownContent
.
outerHeight
();
dropdownContentTop
=
$dropdownContent
.
prop
(
'
offsetTop
'
);
dropdownContentBottom
=
dropdownContentTop
+
dropdownContentHeight
;
// Get the offset bottom of the list item
listItemHeight
=
$listItem
.
outerHeight
();
listItemTop
=
$listItem
.
prop
(
'
offsetTop
'
);
listItemBottom
=
listItemTop
+
listItemHeight
;
if
(
!
index
)
{
// Scroll the dropdown content to the top
$dropdownContent
.
scrollTop
(
0
)
}
else
if
(
index
===
(
$listItems
.
length
-
1
))
{
// Scroll the dropdown content to the bottom
$dropdownContent
.
scrollTop
(
$dropdownContent
.
prop
(
'
scrollHeight
'
));
}
else
if
(
listItemBottom
>
(
dropdownContentBottom
+
dropdownScrollTop
))
{
// Scroll the dropdown content down
$dropdownContent
.
scrollTop
(
listItemBottom
-
dropdownContentBottom
+
CURSOR_SELECT_SCROLL_PADDING
);
}
else
if
(
listItemTop
<
(
dropdownContentTop
+
dropdownScrollTop
))
{
// Scroll the dropdown content up
return
$dropdownContent
.
scrollTop
(
listItemTop
-
dropdownContentTop
-
CURSOR_SELECT_SCROLL_PADDING
);
}
};
...
...
app/assets/javascripts/gl_form.js
View file @
7f6474b2
...
...
@@ -3,12 +3,15 @@
function
GLForm
(
form
)
{
this
.
form
=
form
;
this
.
textarea
=
this
.
form
.
find
(
'
textarea.js-gfm-input
'
);
// Before we start, we should clean up any previous data for this form
this
.
destroy
();
// Setup the form
this
.
setupForm
();
this
.
form
.
data
(
'
gl-form
'
,
this
);
}
GLForm
.
prototype
.
destroy
=
function
()
{
// Clean form listeners
this
.
clearEventListeners
();
return
this
.
form
.
data
(
'
gl-form
'
,
null
);
};
...
...
@@ -21,12 +24,15 @@
this
.
form
.
find
(
'
.div-dropzone
'
).
remove
();
this
.
form
.
addClass
(
'
gfm-form
'
);
disableButtonIfEmptyField
(
this
.
form
.
find
(
'
.js-note-text
'
),
this
.
form
.
find
(
'
.js-comment-button
'
));
// remove notify commit author checkbox for non-commit notes
GitLab
.
GfmAutoComplete
.
setup
(
this
.
form
.
find
(
'
.js-gfm-input
'
));
new
DropzoneInput
(
this
.
form
);
autosize
(
this
.
textarea
);
// form and textarea event listeners
this
.
addEventListeners
();
gl
.
text
.
init
(
this
.
form
);
}
// hide discard button
this
.
form
.
find
(
'
.js-note-discard
'
).
hide
();
return
this
.
form
.
show
();
};
...
...
app/assets/javascripts/graphs/graphs_bundle.js
View file @
7f6474b2
// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// 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_tree . */
(
function
()
{
}).
call
(
this
);
app/assets/javascripts/graphs/stat_graph_contributors_graph.js
View file @
7f6474b2
...
...
@@ -204,6 +204,7 @@
function
ContributorsAuthorGraph
(
data1
)
{
this
.
data
=
data1
;
// Don't split graph size in half for mobile devices.
if
(
$
(
window
).
width
()
<
768
)
{
this
.
width
=
$
(
'
.content
'
).
width
()
-
80
;
}
else
{
...
...
app/assets/javascripts/groups_select.js
View file @
7f6474b2
...
...
@@ -38,6 +38,7 @@
return
_this
.
formatSelection
.
apply
(
_this
,
args
);
},
dropdownCssClass
:
"
ajax-groups-dropdown
"
,
// we do not want to escape markup since we are displaying html in results
escapeMarkup
:
function
(
m
)
{
return
m
;
}
...
...
app/assets/javascripts/issuable.js.es6
View file @
7f6474b2
...
...
@@ -38,9 +38,11 @@
return $(document).off('click', '.js-label-filter-remove').on('click', '.js-label-filter-remove', function(e) {
var $button;
$button = $(this);
// Remove the label input box
$('input[name="label_name[]"]').filter(function() {
return this.value === $button.data('label');
}).remove();
// Submit the form to get new data
Issuable.filterResults($('.filter-form'));
return $('.js-label-select').trigger('update.label');
});
...
...
app/assets/javascripts/issue.js
View file @
7f6474b2
/*= require flash */
/*= require jquery.waitforimages */
/*= require task_list */
(
function
()
{
...
...
@@ -13,6 +9,7 @@
this
.
Issue
=
(
function
()
{
function
Issue
()
{
this
.
submitNoteForm
=
bind
(
this
.
submitNoteForm
,
this
);
// Prevent duplicate event bindings
this
.
disableTaskList
();
if
(
$
(
'
a.btn-close
'
).
length
)
{
this
.
initTaskList
();
...
...
@@ -99,6 +96,8 @@
url
:
$
(
'
form.js-issuable-update
'
).
attr
(
'
action
'
),
data
:
patchData
});
// TODO (rspeicher): Make the issue description inline-editable like a note so
// that we can re-use its form here
};
Issue
.
prototype
.
initMergeRequests
=
function
()
{
...
...
@@ -128,6 +127,8 @@
Issue
.
prototype
.
initCanCreateBranch
=
function
()
{
var
$container
;
$container
=
$
(
'
#new-branch
'
);
// If the user doesn't have the required permissions the container isn't
// rendered at all.
if
(
$container
.
length
===
0
)
{
return
;
}
...
...
app/assets/javascripts/issues-bulk-assignment.js
View file @
7f6474b2
(
function
()
{
this
.
IssuableBulkActions
=
(
function
()
{
function
IssuableBulkActions
(
opts
)
{
// Set defaults
var
ref
,
ref1
,
ref2
;
if
(
opts
==
null
)
{
opts
=
{};
}
this
.
container
=
(
ref
=
opts
.
container
)
!=
null
?
ref
:
$
(
'
.content
'
),
this
.
form
=
(
ref1
=
opts
.
form
)
!=
null
?
ref1
:
this
.
getElement
(
'
.bulk-update
'
),
this
.
issues
=
(
ref2
=
opts
.
issues
)
!=
null
?
ref2
:
this
.
getElement
(
'
.issuable-list > li
'
);
// Save instance
this
.
form
.
data
(
'
bulkActions
'
,
this
);
this
.
willUpdateLabels
=
false
;
this
.
bindEvents
();
// Fixes bulk-assign not working when navigating through pages
Issuable
.
initChecks
();
}
...
...
@@ -86,6 +89,7 @@
ref1
=
this
.
getLabelsFromSelection
();
for
(
j
=
0
,
len1
=
ref1
.
length
;
j
<
len1
;
j
++
)
{
id
=
ref1
[
j
];
// Only the ones that we are not going to keep
if
(
labelsToKeep
.
indexOf
(
id
)
===
-
1
)
{
result
.
push
(
id
);
}
...
...
@@ -147,6 +151,8 @@
indeterminatedLabels
=
this
.
getUnmarkedIndeterminedLabels
();
labelsToApply
=
this
.
getLabelsToApply
();
indeterminatedLabels
.
map
(
function
(
id
)
{
// We need to exclude label IDs that will be applied
// By not doing this will cause issues from selection to not add labels at all
if
(
labelsToApply
.
indexOf
(
id
)
===
-
1
)
{
return
result
.
push
(
id
);
}
...
...
app/assets/javascripts/labels.js
View file @
7f6474b2
...
...
@@ -26,13 +26,16 @@
var
previewColor
;
previewColor
=
$
(
'
input#label_color
'
).
val
();
return
$
(
'
div.label-color-preview
'
).
css
(
'
background-color
'
,
previewColor
);
// Updates the the preview color with the hex-color input
};
// Updates the preview color with a click on a suggested color
Labels
.
prototype
.
setSuggestedColor
=
function
(
e
)
{
var
color
;
color
=
$
(
e
.
currentTarget
).
data
(
'
color
'
);
$
(
'
input#label_color
'
).
val
(
color
);
this
.
updateColorPreview
();
// Notify the form, that color has changed
$
(
'
.label-form
'
).
trigger
(
'
keyup
'
);
return
e
.
preventDefault
();
};
...
...
app/assets/javascripts/labels_select.js
View file @
7f6474b2
...
...
@@ -156,11 +156,13 @@
selectedClass
.
push
(
'
is-indeterminate
'
);
}
if
(
active
.
indexOf
(
label
.
id
)
!==
-
1
)
{
// Remove is-indeterminate class if the item will be marked as active
i
=
selectedClass
.
indexOf
(
'
is-indeterminate
'
);
if
(
i
!==
-
1
)
{
selectedClass
.
splice
(
i
,
1
);
}
selectedClass
.
push
(
'
is-active
'
);
// Add input manually
instance
.
addInput
(
this
.
fieldName
,
label
.
id
);
}
}
...
...
@@ -172,6 +174,7 @@
}
if
(
label
.
duplicate
)
{
spacing
=
100
/
label
.
color
.
length
;
// Reduce the colors to 4
label
.
color
=
label
.
color
.
filter
(
function
(
color
,
i
)
{
return
i
<
4
;
});
...
...
@@ -192,11 +195,13 @@
}
else
{
colorEl
=
''
;
}
// We need to identify which items are actually labels
if
(
label
.
id
)
{
selectedClass
.
push
(
'
label-item
'
);
$a
.
attr
(
'
data-label-id
'
,
label
.
id
);
}
$a
.
addClass
(
selectedClass
.
join
(
'
'
)).
html
(
colorEl
+
"
"
+
label
.
title
);
// Return generated html
return
$li
.
html
(
$a
).
prop
(
'
outerHTML
'
);
},
persistWhenHide
:
$dropdown
.
data
(
'
persistWhenHide
'
),
...
...
@@ -238,6 +243,7 @@
isIssueIndex
=
page
===
'
projects:issues:index
'
;
isMRIndex
=
page
===
'
projects:merge_requests:index
'
;
$selectbox
.
hide
();
// display:block overrides the hide-collapse rule
$value
.
removeAttr
(
'
style
'
);
if
(
page
===
'
projects:boards:show
'
)
{
return
;
...
...
@@ -255,6 +261,7 @@
}
}
if
(
$dropdown
.
hasClass
(
'
js-filter-bulk-update
'
))
{
// If we are persisting state we need the classes
if
(
!
this
.
options
.
persistWhenHide
)
{
return
$dropdown
.
parent
().
find
(
'
.is-active, .is-indeterminate
'
).
removeClass
();
}
...
...
@@ -324,7 +331,9 @@
if
(
$
(
'
.selected_issue:checked
'
).
length
)
{
return
;
}
// Remove inputs
$
(
'
.issues_bulk_update .labels-filter input[type="hidden"]
'
).
remove
();
// Also restore button text
return
$
(
'
.issues_bulk_update .labels-filter .dropdown-toggle-text
'
).
text
(
'
Label
'
);
};
...
...
app/assets/javascripts/lib/chart.js
View file @
7f6474b2
...
...
@@ -3,5 +3,4 @@
(
function
()
{
}).
call
(
this
);
app/assets/javascripts/lib/cropper.js
View file @
7f6474b2
...
...
@@ -3,5 +3,4 @@
(
function
()
{
}).
call
(
this
);
app/assets/javascripts/lib/d3.js
View file @
7f6474b2
...
...
@@ -3,5 +3,4 @@
(
function
()
{
}).
call
(
this
);
app/assets/javascripts/lib/raphael.js
View file @
7f6474b2
/*= require raphael */
/*= require g.raphael */
/*= require g.bar */
(
function
()
{
}).
call
(
this
);
app/assets/javascripts/lib/utils/datetime_utility.js
View file @
7f6474b2
...
...
@@ -29,6 +29,7 @@
if
(
setTimeago
)
{
$timeagoEls
.
timeago
();
$timeagoEls
.
tooltip
(
'
destroy
'
);
// Recreate with custom template
return
$timeagoEls
.
tooltip
({
template
:
'
<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>
'
});
...
...
app/assets/javascripts/lib/utils/emoji_aliases.js.coffee.erb
deleted
100644 → 0
View file @
4c833a1d
gl.emojiAliases = ->
JSON.parse('
<%=
Gitlab
::
AwardEmoji
.
aliases
.
to_json
%>
')
app/assets/javascripts/lib/utils/emoji_aliases.js.erb
0 → 100644
View file @
7f6474b2
(function() {
gl.emojiAliases = function() {
return JSON.parse('
<%=
Gitlab
::
AwardEmoji
.
aliases
.
to_json
%>
');
};
}).call(this);
app/assets/javascripts/lib/utils/notify.js
View file @
7f6474b2
...
...
@@ -6,6 +6,7 @@
notification
=
new
Notification
(
message
,
opts
);
setTimeout
(
function
()
{
return
notification
.
close
();
// Hide the notification after X amount of seconds
},
8000
);
if
(
onclick
)
{
return
notification
.
onclick
=
onclick
;
...
...
@@ -22,12 +23,16 @@
body
:
body
,
icon
:
icon
};
// Let's check if the browser supports notifications
if
(
!
(
'
Notification
'
in
window
))
{
// do nothing
}
else
if
(
Notification
.
permission
===
'
granted
'
)
{
// If it's okay let's create a notification
return
notificationGranted
(
message
,
opts
,
onclick
);
}
else
if
(
Notification
.
permission
!==
'
denied
'
)
{
return
Notification
.
requestPermission
(
function
(
permission
)
{
// If the user accepts, let's create a notification
if
(
permission
===
'
granted
'
)
{
return
notificationGranted
(
message
,
opts
,
onclick
);
}
...
...
app/assets/javascripts/lib/utils/text_utility.js
View file @
7f6474b2
...
...
@@ -29,6 +29,7 @@
lineBefore
=
this
.
lineBefore
(
text
,
textArea
);
lineAfter
=
this
.
lineAfter
(
text
,
textArea
);
if
(
lineBefore
===
blockTag
&&
lineAfter
===
blockTag
)
{
// To remove the block tag we have to select the line before & after
if
(
blockTag
!=
null
)
{
textArea
.
selectionStart
=
textArea
.
selectionStart
-
(
blockTag
.
length
+
1
);
textArea
.
selectionEnd
=
textArea
.
selectionEnd
+
(
blockTag
.
length
+
1
);
...
...
@@ -63,11 +64,11 @@
if
(
!
inserted
)
{
try
{
document
.
execCommand
(
"
ms-beginUndoUnit
"
);
}
catch
(
undefined
)
{}
}
catch
(
error
)
{}
textArea
.
value
=
this
.
replaceRange
(
text
,
textArea
.
selectionStart
,
textArea
.
selectionEnd
,
insertText
);
try
{
document
.
execCommand
(
"
ms-endUndoUnit
"
);
}
catch
(
undefined
)
{}
}
catch
(
error
)
{}
}
return
this
.
moveCursor
(
textArea
,
tag
,
wrap
);
};
...
...
app/assets/javascripts/lib/utils/url_utility.js
View file @
7f6474b2
...
...
@@ -7,6 +7,8 @@
if
((
base
=
w
.
gl
).
utils
==
null
)
{
base
.
utils
=
{};
}
// Returns an array containing the value(s) of the
// of the key passed as an argument
w
.
gl
.
utils
.
getParameterValues
=
function
(
sParam
)
{
var
i
,
sPageURL
,
sParameterName
,
sURLVariables
,
values
;
sPageURL
=
decodeURIComponent
(
window
.
location
.
search
.
substring
(
1
));
...
...
@@ -23,6 +25,8 @@
}
return
values
;
};
// @param {Object} params - url keys and value to merge
// @param {String} url
w
.
gl
.
utils
.
mergeUrlParams
=
function
(
params
,
url
)
{
var
lastChar
,
newUrl
,
paramName
,
paramValue
,
pattern
;
newUrl
=
decodeURIComponent
(
url
);
...
...
@@ -37,12 +41,14 @@
newUrl
=
""
+
newUrl
+
(
newUrl
.
indexOf
(
'
?
'
)
>
0
?
'
&
'
:
'
?
'
)
+
paramName
+
"
=
"
+
paramValue
;
}
}
// Remove a trailing ampersand
lastChar
=
newUrl
[
newUrl
.
length
-
1
];
if
(
lastChar
===
'
&
'
)
{
newUrl
=
newUrl
.
slice
(
0
,
-
1
);
}
return
newUrl
;
};
// removes parameter query string from url. returns the modified url
w
.
gl
.
utils
.
removeParamQueryString
=
function
(
url
,
param
)
{
var
urlVariables
,
variables
;
url
=
decodeURIComponent
(
url
);
...
...
app/assets/javascripts/line_highlighter.js
View file @
7f6474b2
// LineHighlighter
//
// Handles single- and multi-line selection and highlight for blob views.
//
/*= require jquery.scrollTo */
//
// ### Example Markup
//
// <div id="blob-content-holder">
// <div class="file-content">
// <div class="line-numbers">
// <a href="#L1" id="L1" data-line-number="1">1</a>
// <a href="#L2" id="L2" data-line-number="2">2</a>
// <a href="#L3" id="L3" data-line-number="3">3</a>
// <a href="#L4" id="L4" data-line-number="4">4</a>
// <a href="#L5" id="L5" data-line-number="5">5</a>
// </div>
// <pre class="code highlight">
// <code>
// <span id="LC1" class="line">...</span>
// <span id="LC2" class="line">...</span>
// <span id="LC3" class="line">...</span>
// <span id="LC4" class="line">...</span>
// <span id="LC5" class="line">...</span>
// </code>
// </pre>
// </div>
// </div>
//
(
function
()
{
var
bind
=
function
(
fn
,
me
){
return
function
(){
return
fn
.
apply
(
me
,
arguments
);
};
};
this
.
LineHighlighter
=
(
function
()
{
// CSS class applied to highlighted lines
LineHighlighter
.
prototype
.
highlightClass
=
'
hll
'
;
// Internal copy of location.hash so we're not dependent on `location` in tests
LineHighlighter
.
prototype
.
_hash
=
''
;
function
LineHighlighter
(
hash
)
{
var
range
;
if
(
hash
==
null
)
{
// Initialize a LineHighlighter object
//
// hash - String URL hash for dependency injection in tests
hash
=
location
.
hash
;
}
this
.
setHash
=
bind
(
this
.
setHash
,
this
);
...
...
@@ -24,6 +56,8 @@
if
(
range
[
0
])
{
this
.
highlightRange
(
range
);
$
.
scrollTo
(
"
#L
"
+
range
[
0
],
{
// Scroll to the first highlighted line on initial load
// Offset -50 for the sticky top bar, and another -100 for some context
offset
:
-
150
});
}
...
...
@@ -32,6 +66,12 @@
LineHighlighter
.
prototype
.
bindEvents
=
function
()
{
$
(
'
#blob-content-holder
'
).
on
(
'
mousedown
'
,
'
a[data-line-number]
'
,
this
.
clickHandler
);
// While it may seem odd to bind to the mousedown event and then throw away
// the click event, there is a method to our madness.
//
// If not done this way, the line number anchor will sometimes keep its
// active state even when the event is cancelled, resulting in an ugly border
// around the link and/or a persisted underline text decoration.
return
$
(
'
#blob-content-holder
'
).
on
(
'
click
'
,
'
a[data-line-number]
'
,
function
(
event
)
{
return
event
.
preventDefault
();
});
...
...
@@ -44,6 +84,8 @@
lineNumber
=
$
(
event
.
target
).
closest
(
'
a
'
).
data
(
'
line-number
'
);
current
=
this
.
hashToRange
(
this
.
_hash
);
if
(
!
(
current
[
0
]
&&
event
.
shiftKey
))
{
// If there's no current selection, or there is but Shift wasn't held,
// treat this like a single-line selection.
this
.
setHash
(
lineNumber
);
return
this
.
highlightLine
(
lineNumber
);
}
else
if
(
event
.
shiftKey
)
{
...
...
@@ -59,10 +101,23 @@
LineHighlighter
.
prototype
.
clearHighlight
=
function
()
{
return
$
(
"
.
"
+
this
.
highlightClass
).
removeClass
(
this
.
highlightClass
);
// Unhighlight previously highlighted lines
};
// Convert a URL hash String into line numbers
//
// hash - Hash String
//
// Examples:
//
// hashToRange('#L5') # => [5, null]
// hashToRange('#L5-15') # => [5, 15]
// hashToRange('#foo') # => [null, null]
//
// Returns an Array
LineHighlighter
.
prototype
.
hashToRange
=
function
(
hash
)
{
var
first
,
last
,
matches
;
//?L(\d+)(?:-(\d+))?$/)
matches
=
hash
.
match
(
/^#
?
L
(\d
+
)(?:
-
(\d
+
))?
$/
);
if
(
matches
&&
matches
.
length
)
{
first
=
parseInt
(
matches
[
1
]);
...
...
@@ -73,10 +128,16 @@
}
};
// Highlight a single line
//
// lineNumber - Line number to highlight
LineHighlighter
.
prototype
.
highlightLine
=
function
(
lineNumber
)
{
return
$
(
"
#LC
"
+
lineNumber
).
addClass
(
this
.
highlightClass
);
};
// Highlight all lines within a range
//
// range - Array containing the starting and ending line numbers
LineHighlighter
.
prototype
.
highlightRange
=
function
(
range
)
{
var
i
,
lineNumber
,
ref
,
ref1
,
results
;
if
(
range
[
1
])
{
...
...
@@ -90,6 +151,7 @@
}
};
// Set the URL hash string
LineHighlighter
.
prototype
.
setHash
=
function
(
firstLineNumber
,
lastLineNumber
)
{
var
hash
;
if
(
lastLineNumber
)
{
...
...
@@ -101,10 +163,15 @@
return
this
.
__setLocationHash__
(
hash
);
};
// Make the actual hash change in the browser
//
// This method is stubbed in tests.
LineHighlighter
.
prototype
.
__setLocationHash__
=
function
(
value
)
{
return
history
.
pushState
({
turbolinks
:
false
,
url
:
value
// We're using pushState instead of assigning location.hash directly to
// prevent the page from scrolling on the hashchange event
},
document
.
title
,
value
);
};
...
...
app/assets/javascripts/merge_request.js
View file @
7f6474b2
/*= require jquery.waitforimages */
/*= require task_list */
/*= require merge_request_tabs */
(
function
()
{
...
...
@@ -12,6 +8,11 @@
this
.
MergeRequest
=
(
function
()
{
function
MergeRequest
(
opts
)
{
// Initialize MergeRequest behavior
//
// Options:
// action - String, current controller action
//
this
.
opts
=
opts
!=
null
?
opts
:
{};
this
.
submitNoteForm
=
bind
(
this
.
submitNoteForm
,
this
);
this
.
$el
=
$
(
'
.merge-request
'
);
...
...
@@ -21,6 +22,7 @@
};
})(
this
));
this
.
initTabs
();
// Prevent duplicate event bindings
this
.
disableTaskList
();
this
.
initMRBtnListeners
();
if
(
$
(
"
a.btn-close
"
).
length
)
{
...
...
@@ -28,14 +30,17 @@
}
}
// Local jQuery finder
MergeRequest
.
prototype
.
$
=
function
(
selector
)
{
return
this
.
$el
.
find
(
selector
);
};
MergeRequest
.
prototype
.
initTabs
=
function
()
{
if
(
this
.
opts
.
action
!==
'
new
'
)
{
// `MergeRequests#new` has no tab-persisting or lazy-loading behavior
window
.
mrTabs
=
new
MergeRequestTabs
(
this
.
opts
);
}
else
{
// Show the first tab (Commits)
return
$
(
'
.merge-request-tabs a[data-toggle="tab"]:first
'
).
tab
(
'
show
'
);
}
};
...
...
@@ -96,6 +101,8 @@
url
:
$
(
'
form.js-issuable-update
'
).
attr
(
'
action
'
),
data
:
patchData
});
// TODO (rspeicher): Make the merge request description inline-editable like a
// note so that we can re-use its form here
};
return
MergeRequest
;
...
...
app/assets/javascripts/merge_request_tabs.js
View file @
7f6474b2
// MergeRequestTabs
//
// Handles persisting and restoring the current tab selection and lazily-loading
// content on the MergeRequests#show page.
//
/*= require jquery.cookie */
//
// ### Example Markup
//
// <ul class="nav-links merge-request-tabs">
// <li class="notes-tab active">
// <a data-action="notes" data-target="#notes" data-toggle="tab" href="/foo/bar/merge_requests/1">
// Discussion
// </a>
// </li>
// <li class="commits-tab">
// <a data-action="commits" data-target="#commits" data-toggle="tab" href="/foo/bar/merge_requests/1/commits">
// Commits
// </a>
// </li>
// <li class="diffs-tab">
// <a data-action="diffs" data-target="#diffs" data-toggle="tab" href="/foo/bar/merge_requests/1/diffs">
// Diffs
// </a>
// </li>
// </ul>
//
// <div class="tab-content">
// <div class="notes tab-pane active" id="notes">
// Notes Content
// </div>
// <div class="commits tab-pane" id="commits">
// Commits Content
// </div>
// <div class="diffs tab-pane" id="diffs">
// Diffs Content
// </div>
// </div>
//
// <div class="mr-loading-status">
// <div class="loading">
// Loading Animation
// </div>
// </div>
//
(
function
()
{
var
bind
=
function
(
fn
,
me
){
return
function
(){
return
fn
.
apply
(
me
,
arguments
);
};
};
...
...
@@ -19,6 +62,7 @@
this
.
setCurrentAction
=
bind
(
this
.
setCurrentAction
,
this
);
this
.
tabShown
=
bind
(
this
.
tabShown
,
this
);
this
.
showTab
=
bind
(
this
.
showTab
,
this
);
// Store the `location` object, allowing for easier stubbing in tests
this
.
_location
=
location
;
this
.
bindEvents
();
this
.
activateTab
(
this
.
opts
.
action
);
...
...
@@ -77,6 +121,7 @@
}
};
// Activate a tab based on the current action
MergeRequestTabs
.
prototype
.
activateTab
=
function
(
action
)
{
if
(
action
===
'
show
'
)
{
action
=
'
notes
'
;
...
...
@@ -84,20 +129,48 @@
return
$
(
"
.merge-request-tabs a[data-action='
"
+
action
+
"
']
"
).
tab
(
'
show
'
);
};
// Replaces the current Merge Request-specific action in the URL with a new one
//
// If the action is "notes", the URL is reset to the standard
// `MergeRequests#show` route.
//
// Examples:
//
// location.pathname # => "/namespace/project/merge_requests/1"
// setCurrentAction('diffs')
// location.pathname # => "/namespace/project/merge_requests/1/diffs"
//
// location.pathname # => "/namespace/project/merge_requests/1/diffs"
// setCurrentAction('notes')
// location.pathname # => "/namespace/project/merge_requests/1"
//
// location.pathname # => "/namespace/project/merge_requests/1/diffs"
// setCurrentAction('commits')
// location.pathname # => "/namespace/project/merge_requests/1/commits"
//
// Returns the new URL String
MergeRequestTabs
.
prototype
.
setCurrentAction
=
function
(
action
)
{
var
new_state
;
// Normalize action, just to be safe
if
(
action
===
'
show
'
)
{
action
=
'
notes
'
;
}
this
.
currentAction
=
action
;
// Remove a trailing '/commits' or '/diffs'
new_state
=
this
.
_location
.
pathname
.
replace
(
/
\/(
commits|diffs|builds|pipelines
)(\.
html
)?\/?
$/
,
''
);
// Append the new action if we're on a tab other than 'notes'
if
(
action
!==
'
notes
'
)
{
new_state
+=
"
/
"
+
action
;
}
// Ensure parameters and hash come along for the ride
new_state
+=
this
.
_location
.
search
+
this
.
_location
.
hash
;
history
.
replaceState
({
turbolinks
:
true
,
url
:
new_state
// Replace the current history state with the new one without breaking
// Turbolinks' history.
//
// See https://github.com/rails/turbolinks/issues/363
},
document
.
title
,
new_state
);
return
new_state
;
};
...
...
@@ -191,6 +264,7 @@
});
};
<<<<<<<
a79ff9346b73079148cc4ecc81da82804bb51f3c
MergeRequestTabs
.
prototype
.
loadPipelines
=
function
(
source
)
{
if
(
this
.
pipelinesLoaded
)
{
return
;
...
...
@@ -206,6 +280,11 @@
});
};
=======
// Show or hide the loading spinner
//
// status - Boolean, true to show, false to hide
>>>>>>>
Restore
comments
lost
when
converting
CoffeeScript
to
JavaScript
MergeRequestTabs
.
prototype
.
toggleLoading
=
function
(
status
)
{
return
$
(
'
.mr-loading-status .loading
'
).
toggle
(
status
);
};
...
...
@@ -232,6 +311,7 @@
MergeRequestTabs
.
prototype
.
diffViewType
=
function
()
{
return
$
(
'
.inline-parallel-buttons a.active
'
).
data
(
'
view-type
'
);
// Returns diff view type
};
MergeRequestTabs
.
prototype
.
expandViewContainer
=
function
()
{
...
...
@@ -245,6 +325,8 @@
if
(
$gutterIcon
.
is
(
'
.fa-angle-double-right
'
))
{
return
$gutterIcon
.
closest
(
'
a
'
).
trigger
(
'
click
'
,
[
true
]);
}
// Wait until listeners are set
// Only when sidebar is expanded
},
0
);
};
...
...
@@ -259,6 +341,9 @@
return
$gutterIcon
.
closest
(
'
a
'
).
trigger
(
'
click
'
,
[
true
]);
}
},
0
);
// Expand the issuable sidebar unless the user explicitly collapsed it
// Wait until listeners are set
// Only when sidebar is collapsed
};
return
MergeRequestTabs
;
...
...
app/assets/javascripts/merge_request_widget.js
View file @
7f6474b2
...
...
@@ -3,6 +3,12 @@
this
.
MergeRequestWidget
=
(
function
()
{
function
MergeRequestWidget
(
opts
)
{
// Initialize MergeRequestWidget behavior
//
// check_enable - Boolean, whether to check automerge status
// merge_check_url - String, URL to use to check automerge status
// ci_status_url - String, URL to use to check CI status
//
this
.
opts
=
opts
;
$
(
'
#modal_merge_info
'
).
modal
({
show
:
false
...
...
@@ -118,6 +124,8 @@
if
(
data
.
coverage
)
{
_this
.
showCICoverage
(
data
.
coverage
);
}
// The first check should only update the UI, a notification
// should only be displayed on status changes
if
(
showNotification
&&
!
_this
.
firstCICheck
)
{
status
=
_this
.
ciLabelForStatus
(
data
.
status
);
if
(
status
===
"
preparing
"
)
{
...
...
app/assets/javascripts/milestone.js
View file @
7f6474b2
...
...
@@ -110,6 +110,7 @@
},
update
:
function
(
event
,
ui
)
{
var
data
;
// Prevents sorting from container which element has been removed.
if
(
$
(
this
).
find
(
ui
.
item
).
length
>
0
)
{
data
=
$
(
this
).
sortable
(
"
serialize
"
);
return
Milestone
.
sortIssues
(
data
);
...
...
app/assets/javascripts/milestone_select.js
View file @
7f6474b2
...
...
@@ -92,6 +92,7 @@
},
hidden
:
function
()
{
$selectbox
.
hide
();
// display:block overrides the hide-collapse rule
return
$value
.
css
(
'
display
'
,
''
);
},
clicked
:
function
(
selected
,
$el
,
e
)
{
...
...
app/assets/javascripts/network/branch-graph.js
View file @
7f6474b2
...
...
@@ -90,6 +90,7 @@
results
=
[];
while
(
k
<
this
.
mspace
)
{
this
.
colors
.
push
(
Raphael
.
getColor
(.
8
));
// Skipping a few colors in the spectrum to get more contrast between colors
Raphael
.
getColor
();
Raphael
.
getColor
();
results
.
push
(
k
++
);
...
...
@@ -112,6 +113,7 @@
for
(
mm
=
j
=
0
,
len
=
ref
.
length
;
j
<
len
;
mm
=
++
j
)
{
day
=
ref
[
mm
];
if
(
cuday
!==
day
[
0
]
||
cumonth
!==
day
[
1
])
{
// Dates
r
.
text
(
55
,
this
.
offsetY
+
this
.
unitTime
*
mm
,
day
[
0
]).
attr
({
font
:
"
12px Monaco, monospace
"
,
fill
:
"
#BBB
"
...
...
@@ -119,6 +121,7 @@
cuday
=
day
[
0
];
}
if
(
cumonth
!==
day
[
1
])
{
// Months
r
.
text
(
20
,
this
.
offsetY
+
this
.
unitTime
*
mm
,
day
[
1
]).
attr
({
font
:
"
12px Monaco, monospace
"
,
fill
:
"
#EEE
"
...
...
@@ -207,6 +210,7 @@
}
r
=
this
.
r
;
shortrefs
=
commit
.
refs
;
// Truncate if longer than 15 chars
if
(
shortrefs
.
length
>
17
)
{
shortrefs
=
shortrefs
.
substr
(
0
,
15
)
+
"
…
"
;
}
...
...
@@ -217,6 +221,7 @@
title
:
commit
.
refs
});
textbox
=
text
.
getBBox
();
// Create rectangle based on the size of the textbox
rect
=
r
.
rect
(
x
,
y
-
7
,
textbox
.
width
+
5
,
textbox
.
height
+
5
,
4
).
attr
({
fill
:
"
#000
"
,
"
fill-opacity
"
:
.
5
,
...
...
@@ -229,6 +234,7 @@
});
label
=
r
.
set
(
rect
,
text
);
label
.
transform
([
"
t
"
,
-
rect
.
getBBox
().
width
-
15
,
0
]);
// Set text to front
return
text
.
toFront
();
};
...
...
@@ -283,11 +289,13 @@
parentY
=
this
.
offsetY
+
this
.
unitTime
*
parentCommit
.
time
;
parentX1
=
this
.
offsetX
+
this
.
unitSpace
*
(
this
.
mspace
-
parentCommit
.
space
);
parentX2
=
this
.
offsetX
+
this
.
unitSpace
*
(
this
.
mspace
-
parent
[
1
]);
// Set line color
if
(
parentCommit
.
space
<=
commit
.
space
)
{
color
=
this
.
colors
[
commit
.
space
];
}
else
{
color
=
this
.
colors
[
parentCommit
.
space
];
}
// Build line shape
if
(
parent
[
1
]
===
commit
.
space
)
{
offset
=
[
0
,
5
];
arrow
=
"
l-2,5,4,0,-2,-5,0,5
"
;
...
...
@@ -298,13 +306,17 @@
offset
=
[
-
3
,
3
];
arrow
=
"
l-5,0,2,4,3,-4,-4,2
"
;
}
// Start point
route
=
[
"
M
"
,
x
+
offset
[
0
],
y
+
offset
[
1
]];
// Add arrow if not first parent
if
(
i
>
0
)
{
route
.
push
(
arrow
);
}
// Circumvent if overlap
if
(
commit
.
space
!==
parentCommit
.
space
||
commit
.
space
!==
parent
[
1
])
{
route
.
push
(
"
L
"
,
parentX2
,
y
+
10
,
"
L
"
,
parentX2
,
parentY
-
5
);
}
// End point
route
.
push
(
"
L
"
,
parentX1
,
parentY
);
results
.
push
(
r
.
path
(
route
).
attr
({
stroke
:
color
,
...
...
@@ -325,6 +337,7 @@
"
fill-opacity
"
:
.
5
,
stroke
:
"
none
"
});
// Displayed in the center
return
this
.
element
.
scrollTop
(
y
-
this
.
graphHeight
/
2
);
}
};
...
...
app/assets/javascripts/network/network_bundle.js
View file @
7f6474b2
// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// 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_tree . */
(
function
()
{
...
...
app/assets/javascripts/notes.js
View file @
7f6474b2
/*= require autosave */
/*= require autosize */
/*= require dropzone */
/*= require dropzone_input */
/*= require gfm_auto_complete */
/*= require jquery.atwho */
/*= require task_list */
(
function
()
{
...
...
@@ -60,26 +48,43 @@
}
Notes
.
prototype
.
addBinding
=
function
()
{
// add note to UI after creation
$
(
document
).
on
(
"
ajax:success
"
,
"
.js-main-target-form
"
,
this
.
addNote
);
$
(
document
).
on
(
"
ajax:success
"
,
"
.js-discussion-note-form
"
,
this
.
addDiscussionNote
);
// catch note ajax errors
$
(
document
).
on
(
"
ajax:error
"
,
"
.js-main-target-form
"
,
this
.
addNoteError
);
// change note in UI after update
$
(
document
).
on
(
"
ajax:success
"
,
"
form.edit-note
"
,
this
.
updateNote
);
// Edit note link
$
(
document
).
on
(
"
click
"
,
"
.js-note-edit
"
,
this
.
showEditForm
);
$
(
document
).
on
(
"
click
"
,
"
.note-edit-cancel
"
,
this
.
cancelEdit
);
// Reopen and close actions for Issue/MR combined with note form submit
$
(
document
).
on
(
"
click
"
,
"
.js-comment-button
"
,
this
.
updateCloseButton
);
$
(
document
).
on
(
"
keyup input
"
,
"
.js-note-text
"
,
this
.
updateTargetButtons
);
// resolve a discussion
$
(
document
).
on
(
'
click
'
,
'
.js-comment-resolve-button
'
,
this
.
resolveDiscussion
);
// remove a note (in general)
$
(
document
).
on
(
"
click
"
,
"
.js-note-delete
"
,
this
.
removeNote
);
// delete note attachment
$
(
document
).
on
(
"
click
"
,
"
.js-note-attachment-delete
"
,
this
.
removeAttachment
);
// reset main target form after submit
$
(
document
).
on
(
"
ajax:complete
"
,
"
.js-main-target-form
"
,
this
.
reenableTargetFormSubmitButton
);
$
(
document
).
on
(
"
ajax:success
"
,
"
.js-main-target-form
"
,
this
.
resetMainTargetForm
);
// reset main target form when clicking discard
$
(
document
).
on
(
"
click
"
,
"
.js-note-discard
"
,
this
.
resetMainTargetForm
);
// update the file name when an attachment is selected
$
(
document
).
on
(
"
change
"
,
"
.js-note-attachment-input
"
,
this
.
updateFormAttachment
);
// reply to diff/discussion notes
$
(
document
).
on
(
"
click
"
,
"
.js-discussion-reply-button
"
,
this
.
replyToDiscussionNote
);
// add diff note
$
(
document
).
on
(
"
click
"
,
"
.js-add-diff-note-button
"
,
this
.
addDiffNote
);
// hide diff note form
$
(
document
).
on
(
"
click
"
,
"
.js-close-discussion-note-form
"
,
this
.
cancelDiscussionForm
);
// fetch notes when tab becomes visible
$
(
document
).
on
(
"
visibilitychange
"
,
this
.
visibilityChange
);
// when issue status changes, we need to refresh data
$
(
document
).
on
(
"
issuable:change
"
,
this
.
refresh
);
// when a key is clicked on the notes
return
$
(
document
).
on
(
"
keydown
"
,
"
.js-note-text
"
,
this
.
keydownNoteText
);
};
...
...
@@ -112,6 +117,7 @@
return
;
}
$textarea
=
$
(
e
.
target
);
// Edit previous note when UP arrow is hit
switch
(
e
.
which
)
{
case
38
:
if
(
$textarea
.
val
()
!==
''
)
{
...
...
@@ -123,6 +129,7 @@
return
myLastNoteEditBtn
.
trigger
(
'
click
'
,
[
true
,
myLastNote
]);
}
break
;
// Cancel creating diff note or editing any note when ESCAPE is hit
case
27
:
discussionNoteForm
=
$textarea
.
closest
(
'
.js-discussion-note-form
'
);
if
(
discussionNoteForm
.
length
)
{
...
...
@@ -247,10 +254,13 @@
votesBlock
=
$
(
'
.js-awards-block
'
).
eq
(
0
);
gl
.
awardsHandler
.
addAwardToEmojiBar
(
votesBlock
,
note
.
name
);
return
gl
.
awardsHandler
.
scrollToAwards
();
// render note if it not present in loaded list
// or skip if rendered
}
else
if
(
this
.
isNewNote
(
note
))
{
this
.
note_ids
.
push
(
note
.
id
);
$notesList
=
$
(
'
ul.main-notes-list
'
);
$notesList
.
append
(
note
.
html
).
syntaxHighlight
();
// Update datetime format on the recent note
gl
.
utils
.
localTimeAgo
(
$notesList
.
find
(
"
#note_
"
+
note
.
id
+
"
.js-timeago
"
),
false
);
this
.
initTaskList
();
this
.
refresh
();
...
...
@@ -291,19 +301,26 @@
row
=
form
.
closest
(
"
tr
"
);
note_html
=
$
(
note
.
html
);
note_html
.
syntaxHighlight
();
// is this the first note of discussion?
discussionContainer
=
$
(
"
.notes[data-discussion-id='
"
+
note
.
discussion_id
+
"
']
"
);
if
((
note
.
original_discussion_id
!=
null
)
&&
discussionContainer
.
length
===
0
)
{
discussionContainer
=
$
(
"
.notes[data-discussion-id='
"
+
note
.
original_discussion_id
+
"
']
"
);
}
if
(
discussionContainer
.
length
===
0
)
{
// insert the note and the reply button after the temp row
row
.
after
(
note
.
diff_discussion_html
);
// remove the note (will be added again below)
row
.
next
().
find
(
"
.note
"
).
remove
();
// Before that, the container didn't exist
discussionContainer
=
$
(
"
.notes[data-discussion-id='
"
+
note
.
discussion_id
+
"
']
"
);
// Add note to 'Changes' page discussions
discussionContainer
.
append
(
note_html
);
// Init discussion on 'Discussion' page if it is merge request page
if
(
$
(
'
body
'
).
attr
(
'
data-page
'
).
indexOf
(
'
projects:merge_request
'
)
===
0
)
{
$
(
'
ul.main-notes-list
'
).
append
(
note
.
discussion_html
).
syntaxHighlight
();
}
}
else
{
// append new note to all matching discussions
discussionContainer
.
append
(
note_html
);
}
...
...
@@ -327,7 +344,9 @@
Notes
.
prototype
.
resetMainTargetForm
=
function
(
e
)
{
var
form
;
form
=
$
(
"
.js-main-target-form
"
);
// remove validation errors
form
.
find
(
"
.js-errors
"
).
remove
();
// reset text and preview
form
.
find
(
"
.js-md-write-button
"
).
click
();
form
.
find
(
"
.js-note-text
"
).
val
(
""
).
trigger
(
"
input
"
);
form
.
find
(
"
.js-note-text
"
).
data
(
"
autosave
"
).
reset
();
...
...
@@ -354,9 +373,13 @@
Notes
.
prototype
.
setupMainTargetNoteForm
=
function
()
{
var
form
;
// find the form
form
=
$
(
"
.js-new-note-form
"
);
// Set a global clone of the form for later cloning
this
.
formClone
=
form
.
clone
();
// show the form
this
.
setupNoteForm
(
form
);
// fix classes
form
.
removeClass
(
"
js-new-note-form
"
);
form
.
addClass
(
"
js-main-target-form
"
);
form
.
find
(
"
#note_line_code
"
).
remove
();
...
...
@@ -421,6 +444,7 @@
}
this
.
renderDiscussionNote
(
note
);
// cleanup after successfully creating a diff/discussion note
this
.
removeDiscussionNoteForm
(
$form
);
};
...
...
@@ -433,10 +457,12 @@
Notes
.
prototype
.
updateNote
=
function
(
_xhr
,
note
,
_status
)
{
var
$html
,
$note_li
;
// Convert returned HTML to a jQuery object so we can modify it further
$html
=
$
(
note
.
html
);
gl
.
utils
.
localTimeAgo
(
$
(
'
.js-timeago
'
,
$html
));
$html
.
syntaxHighlight
();
$html
.
find
(
'
.js-task-list-container
'
).
taskList
(
'
enable
'
);
// Find the note's `li` element by ID and replace it with the updated HTML
$note_li
=
$
(
'
.note-row-
'
+
note
.
id
);
$note_li
.
replaceWith
(
$html
);
...
...
@@ -461,15 +487,20 @@
note
.
addClass
(
"
is-editting
"
);
form
=
note
.
find
(
"
.note-edit-form
"
);
form
.
addClass
(
'
current-note-edit-form
'
);
// Show the attachment delete link
note
.
find
(
"
.js-note-attachment-delete
"
).
show
();
done
=
function
(
$noteText
)
{
var
noteTextVal
;
// Neat little trick to put the cursor at the end
noteTextVal
=
$noteText
.
val
();
// Store the original note text in a data attribute to retrieve if a user cancels edit.
form
.
find
(
'
form.edit-note
'
).
data
(
'
original-note
'
,
noteTextVal
);
return
$noteText
.
val
(
''
).
val
(
noteTextVal
);
};
new
GLForm
(
form
);
if
((
scrollTo
!=
null
)
&&
(
myLastNote
!=
null
))
{
// scroll to the bottom
// so the open of the last element doesn't make a jump
$
(
'
html, body
'
).
scrollTop
(
$
(
document
).
height
());
return
$
(
'
html, body
'
).
animate
({
scrollTop
:
myLastNote
.
offset
().
top
-
150
...
...
@@ -505,6 +536,7 @@
form
=
note
.
find
(
"
.current-note-edit-form
"
);
note
.
removeClass
(
"
is-editting
"
);
form
.
removeClass
(
"
current-note-edit-form
"
);
// Replace markdown textarea text with original note text.
return
form
.
find
(
"
.js-note-text
"
).
val
(
form
.
find
(
'
form.edit-note
'
).
data
(
'
original-note
'
));
};
...
...
@@ -520,6 +552,9 @@
var
noteId
;
noteId
=
$
(
e
.
currentTarget
).
closest
(
"
.note
"
).
attr
(
"
id
"
);
$
(
"
.note[id='
"
+
noteId
+
"
']
"
).
each
((
function
(
_this
)
{
// A same note appears in the "Discussion" and in the "Changes" tab, we have
// to remove all. Using $(".note[id='noteId']") ensure we get all the notes,
// where $("#noteId") would return only one.
return
function
(
i
,
el
)
{
var
note
,
notes
;
note
=
$
(
el
);
...
...
@@ -533,13 +568,17 @@
}
}
// check if this is the last note for this line
if
(
notes
.
find
(
"
.note
"
).
length
===
1
)
{
// "Discussions" tab
notes
.
closest
(
"
.timeline-entry
"
).
remove
();
// "Changes" tab / commit view
notes
.
closest
(
"
tr
"
).
remove
();
}
return
note
.
remove
();
};
})(
this
));
// Decrement the "Discussions" counter only once
return
this
.
updateNotesCount
(
-
1
);
};
...
...
@@ -571,10 +610,12 @@
var
form
,
replyLink
;
form
=
this
.
formClone
.
clone
();
replyLink
=
$
(
e
.
target
).
closest
(
"
.js-discussion-reply-button
"
);
// insert the form after the button
replyLink
.
closest
(
'
.discussion-reply-holder
'
)
.
hide
()
.
after
(
form
);
// show the form
return
this
.
setupDiscussionNoteForm
(
replyLink
,
form
);
};
...
...
@@ -589,6 +630,7 @@
*/
Notes
.
prototype
.
setupDiscussionNoteForm
=
function
(
dataHolder
,
form
)
{
// setup note target
form
.
attr
(
'
id
'
,
"
new-discussion-note-form-
"
+
(
dataHolder
.
data
(
"
discussionId
"
)));
form
.
attr
(
"
data-line-code
"
,
dataHolder
.
data
(
"
lineCode
"
));
form
.
find
(
"
#note_type
"
).
val
(
dataHolder
.
data
(
"
noteType
"
));
...
...
@@ -636,6 +678,7 @@
addForm
=
false
;
notesContentSelector
=
"
.notes_content
"
;
rowCssToAdd
=
"
<tr class=
\"
notes_holder js-temp-notes-holder
\"
><td class=
\"
notes_line
\"
colspan=
\"
2
\"
></td><td class=
\"
notes_content
\"
><div class=
\"
content
\"
></div></td></tr>
"
;
// In parallel view, look inside the correct left/right pane
if
(
this
.
isParallelView
())
{
lineType
=
$link
.
data
(
"
lineType
"
);
notesContentSelector
+=
"
.
"
+
lineType
;
...
...
@@ -652,6 +695,7 @@
e
.
target
=
replyButton
[
0
];
$
.
proxy
(
this
.
replyToDiscussionNote
,
replyButton
[
0
],
e
).
call
();
}
else
{
// In parallel view, the form may not be present in one of the panes
noteForm
=
notesContent
.
find
(
"
.js-discussion-note-form
"
);
if
(
noteForm
.
length
===
0
)
{
addForm
=
true
;
...
...
@@ -659,6 +703,7 @@
}
}
}
else
{
// add a notes row and insert the form
row
.
after
(
rowCssToAdd
);
nextRow
=
row
.
next
();
notesContent
=
nextRow
.
find
(
notesContentSelector
);
...
...
@@ -667,6 +712,7 @@
if
(
addForm
)
{
newForm
=
this
.
formClone
.
clone
();
newForm
.
appendTo
(
notesContent
);
// show the form
return
this
.
setupDiscussionNoteForm
(
$link
,
newForm
);
}
};
...
...
@@ -685,12 +731,15 @@
glForm
=
form
.
data
(
'
gl-form
'
);
glForm
.
destroy
();
form
.
find
(
"
.js-note-text
"
).
data
(
"
autosave
"
).
reset
();
// show the reply button (will only work for replies)
form
.
prev
(
'
.discussion-reply-holder
'
)
.
show
();
if
(
row
.
is
(
"
.js-temp-notes-holder
"
))
{
// remove temporary row for diff lines
return
row
.
remove
();
}
else
{
// only remove the form
return
form
.
remove
();
}
};
...
...
@@ -712,6 +761,7 @@
Notes
.
prototype
.
updateFormAttachment
=
function
()
{
var
filename
,
form
;
form
=
$
(
this
).
closest
(
"
form
"
);
// get only the basename
filename
=
$
(
this
).
val
().
replace
(
/^.*
[\\\/]
/
,
""
);
return
form
.
find
(
"
.js-attachment-filename
"
).
text
(
filename
);
};
...
...
app/assets/javascripts/preview_markdown.js
View file @
7f6474b2
// MarkdownPreview
//
// Handles toggling the "Write" and "Preview" tab clicks, rendering the preview,
// and showing a warning when more than `x` users are referenced.
//
(
function
()
{
var
lastTextareaPreviewed
,
markdownPreview
,
previewButtonSelector
,
writeButtonSelector
;
this
.
MarkdownPreview
=
(
function
()
{
function
MarkdownPreview
()
{}
// Minimum number of users referenced before triggering a warning
MarkdownPreview
.
prototype
.
referenceThreshold
=
10
;
MarkdownPreview
.
prototype
.
ajaxCache
=
{};
...
...
@@ -101,8 +107,10 @@
return
;
}
lastTextareaPreviewed
=
$form
.
find
(
'
textarea.markdown-area
'
);
// toggle tabs
$form
.
find
(
writeButtonSelector
).
parent
().
removeClass
(
'
active
'
);
$form
.
find
(
previewButtonSelector
).
parent
().
addClass
(
'
active
'
);
// toggle content
$form
.
find
(
'
.md-write-holder
'
).
hide
();
$form
.
find
(
'
.md-preview-holder
'
).
show
();
return
markdownPreview
.
showPreview
(
$form
);
...
...
@@ -113,8 +121,10 @@
return
;
}
lastTextareaPreviewed
=
null
;
// toggle tabs
$form
.
find
(
writeButtonSelector
).
parent
().
addClass
(
'
active
'
);
$form
.
find
(
previewButtonSelector
).
parent
().
removeClass
(
'
active
'
);
// toggle content
$form
.
find
(
'
.md-write-holder
'
).
show
();
$form
.
find
(
'
textarea.markdown-area
'
).
focus
();
return
$form
.
find
(
'
.md-preview-holder
'
).
hide
();
...
...
app/assets/javascripts/profile/gl_crop.js
View file @
7f6474b2
...
...
@@ -5,6 +5,7 @@
GitLabCrop
=
(
function
()
{
var
FILENAMEREGEX
;
// Matches everything but the file name
FILENAMEREGEX
=
/^.*
[\\\/]
/
;
function
GitLabCrop
(
input
,
opts
)
{
...
...
@@ -17,11 +18,18 @@
this
.
onModalShow
=
bind
(
this
.
onModalShow
,
this
);
this
.
onPickImageClick
=
bind
(
this
.
onPickImageClick
,
this
);
this
.
fileInput
=
$
(
input
);
// We should rename to avoid spec to fail
// Form will submit the proper input filed with a file using FormData
this
.
fileInput
.
attr
(
'
name
'
,
(
this
.
fileInput
.
attr
(
'
name
'
))
+
"
-trigger
"
).
attr
(
'
id
'
,
(
this
.
fileInput
.
attr
(
'
id
'
))
+
"
-trigger
"
);
// Set defaults
this
.
exportWidth
=
(
ref
=
opts
.
exportWidth
)
!=
null
?
ref
:
200
,
this
.
exportHeight
=
(
ref1
=
opts
.
exportHeight
)
!=
null
?
ref1
:
200
,
this
.
cropBoxWidth
=
(
ref2
=
opts
.
cropBoxWidth
)
!=
null
?
ref2
:
200
,
this
.
cropBoxHeight
=
(
ref3
=
opts
.
cropBoxHeight
)
!=
null
?
ref3
:
200
,
this
.
form
=
(
ref4
=
opts
.
form
)
!=
null
?
ref4
:
this
.
fileInput
.
parents
(
'
form
'
),
this
.
filename
=
opts
.
filename
,
this
.
previewImage
=
opts
.
previewImage
,
this
.
modalCrop
=
opts
.
modalCrop
,
this
.
pickImageEl
=
opts
.
pickImageEl
,
this
.
uploadImageBtn
=
opts
.
uploadImageBtn
,
this
.
modalCropImg
=
opts
.
modalCropImg
;
// Required params
// Ensure needed elements are jquery objects
// If selector is provided we will convert them to a jQuery Object
this
.
filename
=
this
.
getElement
(
this
.
filename
);
this
.
previewImage
=
this
.
getElement
(
this
.
previewImage
);
this
.
pickImageEl
=
this
.
getElement
(
this
.
pickImageEl
);
// Modal elements usually are outside the @form element
this
.
modalCrop
=
_
.
isString
(
this
.
modalCrop
)
?
$
(
this
.
modalCrop
)
:
this
.
modalCrop
;
this
.
uploadImageBtn
=
_
.
isString
(
this
.
uploadImageBtn
)
?
$
(
this
.
uploadImageBtn
)
:
this
.
uploadImageBtn
;
this
.
modalCropImg
=
_
.
isString
(
this
.
modalCropImg
)
?
$
(
this
.
modalCropImg
)
:
this
.
modalCropImg
;
...
...
@@ -93,8 +101,8 @@
return
this
.
modalCropImg
.
attr
(
'
src
'
,
''
).
cropper
(
'
destroy
'
);
};
GitLabCrop
.
prototype
.
onUploadImageBtnClick
=
function
(
e
)
{
e
.
preventDefault
();
GitLabCrop
.
prototype
.
onUploadImageBtnClick
=
function
(
e
)
{
// Remove attached image
e
.
preventDefault
();
// Destroy cropper instance
this
.
setBlob
();
this
.
setPreview
();
this
.
modalCrop
.
modal
(
'
hide
'
);
...
...
app/assets/javascripts/profile/profile.js
View file @
7f6474b2
...
...
@@ -11,9 +11,11 @@
this
.
form
=
(
ref
=
opts
.
form
)
!=
null
?
ref
:
$
(
'
.edit-user
'
);
$
(
'
.js-preferences-form
'
).
on
(
'
change.preference
'
,
'
input[type=radio]
'
,
function
()
{
return
$
(
this
).
parents
(
'
form
'
).
submit
();
// Automatically submit the Preferences form when any of its radio buttons change
});
$
(
'
#user_notification_email
'
).
on
(
'
change
'
,
function
()
{
return
$
(
this
).
parents
(
'
form
'
).
submit
();
// Automatically submit email form when it changes
});
$
(
'
.update-username
'
).
on
(
'
ajax:before
'
,
function
()
{
$
(
'
.loading-username
'
).
show
();
...
...
@@ -76,6 +78,7 @@
},
complete
:
function
()
{
window
.
scrollTo
(
0
,
0
);
// Enable submit button after requests ends
return
self
.
form
.
find
(
'
:input[disabled]
'
).
enable
();
}
});
...
...
@@ -93,6 +96,7 @@
if
(
comment
&&
comment
.
length
>
1
&&
$title
.
val
()
===
''
)
{
return
$title
.
val
(
comment
[
1
]).
change
();
}
// Extract the SSH Key title from its comment
});
if
(
gl
.
utils
.
getPagePath
()
===
'
profiles
'
)
{
return
new
Profile
();
...
...
app/assets/javascripts/profile/profile_bundle.js
View file @
7f6474b2
...
...
@@ -3,5 +3,4 @@
(
function
()
{
}).
call
(
this
);
app/assets/javascripts/project.js
View file @
7f6474b2
...
...
@@ -11,7 +11,13 @@
url
=
$
(
"
#project_clone
"
).
val
();
$
(
'
#project_clone
'
).
val
(
url
);
return
$
(
'
.clone
'
).
text
(
url
);
// Git protocol switcher
// Remove the active class for all buttons (ssh, http, kerberos if shown)
// Add the active class for the clicked button
// Update the input field
// Update the command line instructions
});
// Ref switcher
this
.
initRefSwitcher
();
$
(
'
.project-refs-select
'
).
on
(
'
change
'
,
function
()
{
return
$
(
this
).
parents
(
'
form
'
).
submit
();
...
...
app/assets/javascripts/project_find_file.js
View file @
7f6474b2
...
...
@@ -13,8 +13,11 @@
this
.
selectRowUp
=
bind
(
this
.
selectRowUp
,
this
);
this
.
filePaths
=
{};
this
.
inputElement
=
this
.
element
.
find
(
"
.file-finder-input
"
);
// init event
this
.
initEvent
();
// focus text input box
this
.
inputElement
.
focus
();
// load file list
this
.
load
(
this
.
options
.
url
);
}
...
...
@@ -42,6 +45,7 @@
}
}
});
// init event
};
ProjectFindFile
.
prototype
.
findFile
=
function
()
{
...
...
@@ -49,8 +53,10 @@
searchText
=
this
.
inputElement
.
val
();
result
=
searchText
.
length
>
0
?
fuzzaldrinPlus
.
filter
(
this
.
filePaths
,
searchText
)
:
this
.
filePaths
;
return
this
.
renderList
(
result
,
searchText
);
// find file
};
// files pathes load
ProjectFindFile
.
prototype
.
load
=
function
(
url
)
{
return
$
.
ajax
({
url
:
url
,
...
...
@@ -67,6 +73,7 @@
});
};
// render result
ProjectFindFile
.
prototype
.
renderList
=
function
(
filePaths
,
searchText
)
{
var
blobItemUrl
,
filePath
,
html
,
i
,
j
,
len
,
matches
,
results
;
this
.
element
.
find
(
"
.tree-table > tbody
"
).
empty
();
...
...
@@ -86,6 +93,7 @@
return
results
;
};
// highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> )
highlighter
=
function
(
element
,
text
,
matches
)
{
var
highlightText
,
j
,
lastIndex
,
len
,
matchIndex
,
matchedChars
,
unmatched
;
lastIndex
=
0
;
...
...
@@ -110,6 +118,7 @@
return
element
.
append
(
document
.
createTextNode
(
text
.
substring
(
lastIndex
)));
};
// make tbody row html
ProjectFindFile
.
prototype
.
makeHtml
=
function
(
filePath
,
matches
,
blobItemUrl
)
{
var
$tr
;
$tr
=
$
(
"
<tr class='tree-item'><td class='tree-item-file-name'><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'><a></a></span></td></tr>
"
);
...
...
app/assets/javascripts/project_show.js
View file @
7f6474b2
...
...
@@ -7,3 +7,5 @@
})();
}).
call
(
this
);
// I kept class for future
app/assets/javascripts/projects_list.js
View file @
7f6474b2
...
...
@@ -33,6 +33,7 @@
$
(
'
.projects-list-holder
'
).
replaceWith
(
data
.
html
);
return
history
.
replaceState
({
page
:
project_filter_url
// Change url so if user reload a page - search results are saved
},
document
.
title
,
project_filter_url
);
},
dataType
:
"
json
"
...
...
app/assets/javascripts/protected_branch_dropdown.js.es6
View file @
7f6474b2
...
...
@@ -45,6 +45,7 @@ class ProtectedBranchDropdown {
}
onClickCreateWildcard() {
// Refresh the dropdown's data, which ends up calling `getProtectedBranches`
this.$dropdown.data('glDropdown').remote.execute();
this.$dropdown.data('glDropdown').selectRowAtIndex(0);
}
...
...
app/assets/javascripts/search_autocomplete.js
View file @
7f6474b2
...
...
@@ -24,6 +24,7 @@
this
.
onSearchInputKeyUp
=
bind
(
this
.
onSearchInputKeyUp
,
this
);
this
.
onSearchInputKeyDown
=
bind
(
this
.
onSearchInputKeyDown
,
this
);
this
.
wrap
=
(
ref
=
opts
.
wrap
)
!=
null
?
ref
:
$
(
'
.search
'
),
this
.
optsEl
=
(
ref1
=
opts
.
optsEl
)
!=
null
?
ref1
:
this
.
wrap
.
find
(
'
.search-autocomplete-opts
'
),
this
.
autocompletePath
=
(
ref2
=
opts
.
autocompletePath
)
!=
null
?
ref2
:
this
.
optsEl
.
data
(
'
autocomplete-path
'
),
this
.
projectId
=
(
ref3
=
opts
.
projectId
)
!=
null
?
ref3
:
this
.
optsEl
.
data
(
'
autocomplete-project-id
'
)
||
''
,
this
.
projectRef
=
(
ref4
=
opts
.
projectRef
)
!=
null
?
ref4
:
this
.
optsEl
.
data
(
'
autocomplete-project-ref
'
)
||
''
;
// Dropdown Element
this
.
dropdown
=
this
.
wrap
.
find
(
'
.dropdown
'
);
this
.
dropdownContent
=
this
.
dropdown
.
find
(
'
.dropdown-content
'
);
this
.
locationBadgeEl
=
this
.
getElement
(
'
.location-badge
'
);
...
...
@@ -35,6 +36,7 @@
this
.
repositoryInputEl
=
this
.
getElement
(
'
#repository_ref
'
);
this
.
clearInput
=
this
.
getElement
(
'
.js-clear-input
'
);
this
.
saveOriginalState
();
// Only when user is logged in
if
(
gon
.
current_user_id
)
{
this
.
createAutocomplete
();
}
...
...
@@ -43,6 +45,7 @@
this
.
bindEvents
();
}
// Finds an element inside wrapper element
SearchAutocomplete
.
prototype
.
getElement
=
function
(
selector
)
{
return
this
.
wrap
.
find
(
selector
);
};
...
...
@@ -82,6 +85,7 @@
}
return
;
}
// Prevent multiple ajax calls
if
(
this
.
loadingSuggestions
)
{
return
;
}
...
...
@@ -92,14 +96,17 @@
term
:
term
},
function
(
response
)
{
var
data
,
firstCategory
,
i
,
lastCategory
,
len
,
suggestion
;
// Hide dropdown menu if no suggestions returns
if
(
!
response
.
length
)
{
_this
.
disableAutocomplete
();
return
;
}
data
=
[];
// List results
firstCategory
=
true
;
for
(
i
=
0
,
len
=
response
.
length
;
i
<
len
;
i
++
)
{
suggestion
=
response
[
i
];
// Add group header before list each group
if
(
lastCategory
!==
suggestion
.
category
)
{
if
(
!
firstCategory
)
{
data
.
push
(
'
separator
'
);
...
...
@@ -119,6 +126,7 @@
url
:
suggestion
.
url
});
}
// Add option to proceed with the search
if
(
data
.
length
)
{
data
.
push
(
'
separator
'
);
data
.
push
({
...
...
@@ -169,11 +177,13 @@
SearchAutocomplete
.
prototype
.
serializeState
=
function
()
{
return
{
// Search Criteria
search_project_id
:
this
.
projectInputEl
.
val
(),
group_id
:
this
.
groupInputEl
.
val
(),
search_code
:
this
.
searchCodeInputEl
.
val
(),
repository_ref
:
this
.
repositoryInputEl
.
val
(),
scope
:
this
.
scopeInputEl
.
val
(),
// Location badge
_location
:
this
.
locationBadgeEl
.
text
()
};
};
...
...
@@ -194,6 +204,7 @@
SearchAutocomplete
.
prototype
.
enableAutocomplete
=
function
()
{
var
_this
;
// No need to enable anything if user is not logged in
if
(
!
gon
.
current_user_id
)
{
return
;
}
...
...
@@ -206,18 +217,22 @@
};
SearchAutocomplete
.
prototype
.
onSearchInputKeyDown
=
function
()
{
// Saves last length of the entered text
return
this
.
saveTextLength
();
};
SearchAutocomplete
.
prototype
.
onSearchInputKeyUp
=
function
(
e
)
{
switch
(
e
.
keyCode
)
{
case
KEYCODE
.
BACKSPACE
:
// when trying to remove the location badge
if
(
this
.
lastTextLength
===
0
&&
this
.
badgePresent
())
{
this
.
removeLocationBadge
();
}
// When removing the last character and no badge is present
if
(
this
.
lastTextLength
===
1
)
{
this
.
disableAutocomplete
();
}
// When removing any character from existin value
if
(
this
.
lastTextLength
>
1
)
{
this
.
enableAutocomplete
();
}
...
...
@@ -232,9 +247,12 @@
case
KEYCODE
.
DOWN
:
return
;
default
:
// Handle the case when deleting the input value other than backspace
// e.g. Pressing ctrl + backspace or ctrl + x
if
(
this
.
searchInput
.
val
()
===
''
)
{
this
.
disableAutocomplete
();
}
else
{
// We should display the menu only when input is not empty
if
(
e
.
keyCode
!==
KEYCODE
.
ENTER
)
{
this
.
enableAutocomplete
();
}
...
...
@@ -243,7 +261,9 @@
this
.
wrap
.
toggleClass
(
'
has-value
'
,
!!
e
.
target
.
value
);
};
// Avoid falsy value to be returned
SearchAutocomplete
.
prototype
.
onSearchInputClick
=
function
(
e
)
{
// Prevents closing the dropdown menu
return
e
.
stopImmediatePropagation
();
};
...
...
@@ -267,6 +287,7 @@
SearchAutocomplete
.
prototype
.
onSearchInputBlur
=
function
(
e
)
{
this
.
isFocused
=
false
;
this
.
wrap
.
removeClass
(
'
search-active
'
);
// If input is blank then restore state
if
(
this
.
searchInput
.
val
()
===
''
)
{
return
this
.
restoreOriginalState
();
}
...
...
@@ -311,6 +332,7 @@
results
=
[];
for
(
i
=
0
,
len
=
inputs
.
length
;
i
<
len
;
i
++
)
{
input
=
inputs
[
i
];
// _location isnt a input
if
(
input
===
'
_location
'
)
{
break
;
}
...
...
app/assets/javascripts/shortcuts.js
View file @
7f6474b2
...
...
@@ -86,6 +86,7 @@
var
defaultStopCallback
;
defaultStopCallback
=
Mousetrap
.
stopCallback
;
return
function
(
e
,
element
,
combo
)
{
// allowed shortcuts if textarea, input, contenteditable are focused
if
([
'
ctrl+shift+p
'
,
'
command+shift+p
'
].
indexOf
(
combo
)
!==
-
1
)
{
return
false
;
}
else
{
...
...
app/assets/javascripts/shortcuts_find_file.js
View file @
7f6474b2
...
...
@@ -14,8 +14,10 @@
ShortcutsFindFile
.
__super__
.
constructor
.
call
(
this
);
_oldStopCallback
=
Mousetrap
.
stopCallback
;
Mousetrap
.
stopCallback
=
(
function
(
_this
)
{
// override to fire shortcuts action when focus in textbox
return
function
(
event
,
element
,
combo
)
{
if
(
element
===
_this
.
projectFindFile
.
inputElement
[
0
]
&&
(
combo
===
'
up
'
||
combo
===
'
down
'
||
combo
===
'
esc
'
||
combo
===
'
enter
'
))
{
// when press up/down key in textbox, cusor prevent to move to home/end
event
.
preventDefault
();
return
false
;
}
...
...
app/assets/javascripts/shortcuts_issuable.js
View file @
7f6474b2
/*= require mousetrap */
/*= require shortcuts_navigation */
(
function
()
{
...
...
@@ -43,16 +41,20 @@
if
(
selected
.
trim
()
===
""
)
{
return
;
}
// Put a '>' character before each non-empty line in the selection
quote
=
_
.
map
(
selected
.
split
(
"
\n
"
),
function
(
val
)
{
if
(
val
.
trim
()
!==
''
)
{
return
"
>
"
+
val
+
"
\n
"
;
}
});
// If replyField already has some content, add a newline before our quote
separator
=
replyField
.
val
().
trim
()
!==
""
&&
"
\n
"
||
''
;
replyField
.
val
(
function
(
_
,
current
)
{
return
current
+
separator
+
quote
.
join
(
''
)
+
"
\n
"
;
});
// Trigger autosave for the added text
replyField
.
trigger
(
'
input
'
);
// Focus the input field
return
replyField
.
focus
();
}
};
...
...
app/assets/javascripts/syntax_highlight.js
View file @
7f6474b2
// Syntax Highlighter
//
// Applies a syntax highlighting color scheme CSS class to any element with the
// `js-syntax-highlight` class
//
// ### Example Markup
//
// <div class="js-syntax-highlight"></div>
//
(
function
()
{
$
.
fn
.
syntaxHighlight
=
function
()
{
var
$children
;
if
(
$
(
this
).
hasClass
(
'
js-syntax-highlight
'
))
{
// Given the element itself, apply highlighting
return
$
(
this
).
addClass
(
gon
.
user_color_scheme
);
}
else
{
// Given a parent element, recurse to any of its applicable children
$children
=
$
(
this
).
find
(
'
.js-syntax-highlight
'
);
if
(
$children
.
length
)
{
return
$children
.
syntaxHighlight
();
...
...
app/assets/javascripts/todos.js
View file @
7f6474b2
...
...
@@ -129,16 +129,21 @@
var
currPage
,
currPages
,
newPages
,
pageParams
,
url
;
currPages
=
this
.
getTotalPages
();
currPage
=
this
.
getCurrentPage
();
// Refresh if no remaining Todos
if
(
!
total
)
{
location
.
reload
();
return
;
}
// Do nothing if no pagination
if
(
!
currPages
)
{
return
;
}
newPages
=
Math
.
ceil
(
total
/
this
.
getTodosPerPage
());
// Includes query strings
url
=
location
.
href
;
// If new total of pages is different than we have now
if
(
newPages
!==
currPages
)
{
// Redirect to previous page if there's one available
if
(
currPages
>
1
&&
currPage
===
currPages
)
{
pageParams
=
{
page
:
currPages
-
1
...
...
@@ -155,6 +160,7 @@
if
(
!
todoLink
)
{
return
;
}
// Allow Meta-Click or Mouse3-click to open in a new tab
if
(
e
.
metaKey
||
e
.
which
===
2
)
{
e
.
preventDefault
();
return
window
.
open
(
todoLink
,
'
_blank
'
);
...
...
app/assets/javascripts/tree.js
View file @
7f6474b2
...
...
@@ -2,6 +2,8 @@
this
.
TreeView
=
(
function
()
{
function
TreeView
()
{
this
.
initKeyNav
();
// Code browser tree slider
// Make the entire tree-item row clickable, but not if clicking another link (like a commit message)
$
(
"
.tree-content-holder .tree-item
"
).
on
(
'
click
'
,
function
(
e
)
{
var
$clickedEl
,
path
;
$clickedEl
=
$
(
e
.
target
);
...
...
@@ -15,6 +17,7 @@
}
}
});
// Show the "Loading commit data" for only the first element
$
(
'
span.log_loading:first
'
).
removeClass
(
'
hide
'
);
}
...
...
app/assets/javascripts/u2f/authenticate.js
View file @
7f6474b2
// Authenticate U2F (universal 2nd factor) devices for users to authenticate with.
//
// State Flow #1: setup -> in_progress -> authenticated -> POST to server
// State Flow #2: setup -> in_progress -> error -> setup
(
function
()
{
var
bind
=
function
(
fn
,
me
){
return
function
(){
return
fn
.
apply
(
me
,
arguments
);
};
};
...
...
@@ -15,6 +19,17 @@
this
.
appId
=
u2fParams
.
app_id
;
this
.
challenge
=
u2fParams
.
challenge
;
this
.
signRequests
=
u2fParams
.
sign_requests
.
map
(
function
(
request
)
{
// The U2F Javascript API v1.1 requires a single challenge, with
// _no challenges per-request_. The U2F Javascript API v1.0 requires a
// challenge per-request, which is done by copying the single challenge
// into every request.
//
// In either case, we don't need the per-request challenges that the server
// has generated, so we can remove them.
//
// Note: The server library fixes this behaviour in (unreleased) version 1.0.0.
// This can be removed once we upgrade.
// https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4
return
_
(
request
).
omit
(
'
challenge
'
);
});
}
...
...
@@ -41,6 +56,7 @@
})(
this
),
10
);
};
// Rendering #
U2FAuthenticate
.
prototype
.
templates
=
{
"
notSupported
"
:
"
#js-authenticate-u2f-not-supported
"
,
"
setup
"
:
'
#js-authenticate-u2f-setup
'
,
...
...
@@ -75,6 +91,8 @@
U2FAuthenticate
.
prototype
.
renderAuthenticated
=
function
(
deviceResponse
)
{
this
.
renderTemplate
(
'
authenticated
'
);
// Prefer to do this instead of interpolating using Underscore templates
// because of JSON escaping issues.
return
this
.
container
.
find
(
"
#js-device-response
"
).
val
(
deviceResponse
);
};
...
...
app/assets/javascripts/u2f/register.js
View file @
7f6474b2
// Register U2F (universal 2nd factor) devices for users to authenticate with.
//
// State Flow #1: setup -> in_progress -> registered -> POST to server
// State Flow #2: setup -> in_progress -> error -> setup
(
function
()
{
var
bind
=
function
(
fn
,
me
){
return
function
(){
return
fn
.
apply
(
me
,
arguments
);
};
};
...
...
@@ -39,6 +43,7 @@
})(
this
),
10
);
};
// Rendering #
U2FRegister
.
prototype
.
templates
=
{
"
notSupported
"
:
"
#js-register-u2f-not-supported
"
,
"
setup
"
:
'
#js-register-u2f-setup
'
,
...
...
@@ -73,6 +78,8 @@
U2FRegister
.
prototype
.
renderRegistered
=
function
(
deviceResponse
)
{
this
.
renderTemplate
(
'
registered
'
);
// Prefer to do this instead of interpolating using Underscore templates
// because of JSON escaping issues.
return
this
.
container
.
find
(
"
#js-device-response
"
).
val
(
deviceResponse
);
};
...
...
app/assets/javascripts/user_tabs.js
View file @
7f6474b2
// UserTabs
//
// Handles persisting and restoring the current tab selection and lazily-loading
// content on the Users#show page.
//
// ### Example Markup
//
// <ul class="nav-links">
// <li class="activity-tab active">
// <a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username">
// Activity
// </a>
// </li>
// <li class="groups-tab">
// <a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups">
// Groups
// </a>
// </li>
// <li class="contributed-tab">
// <a data-action="contributed" data-target="#contributed" data-toggle="tab" href="/u/username/contributed">
// Contributed projects
// </a>
// </li>
// <li class="projects-tab">
// <a data-action="projects" data-target="#projects" data-toggle="tab" href="/u/username/projects">
// Personal projects
// </a>
// </li>
// <li class="snippets-tab">
// <a data-action="snippets" data-target="#snippets" data-toggle="tab" href="/u/username/snippets">
// </a>
// </li>
// </ul>
//
// <div class="tab-content">
// <div class="tab-pane" id="activity">
// Activity Content
// </div>
// <div class="tab-pane" id="groups">
// Groups Content
// </div>
// <div class="tab-pane" id="contributed">
// Contributed projects content
// </div>
// <div class="tab-pane" id="projects">
// Projects content
// </div>
// <div class="tab-pane" id="snippets">
// Snippets content
// </div>
// </div>
//
// <div class="loading-status">
// <div class="loading">
// Loading Animation
// </div>
// </div>
//
(
function
()
{
var
bind
=
function
(
fn
,
me
){
return
function
(){
return
fn
.
apply
(
me
,
arguments
);
};
};
...
...
@@ -6,18 +64,23 @@
this
.
tabShown
=
bind
(
this
.
tabShown
,
this
);
var
i
,
item
,
len
,
ref
,
ref1
,
ref2
,
ref3
;
this
.
action
=
(
ref
=
opts
.
action
)
!=
null
?
ref
:
'
activity
'
,
this
.
defaultAction
=
(
ref1
=
opts
.
defaultAction
)
!=
null
?
ref1
:
'
activity
'
,
this
.
parentEl
=
(
ref2
=
opts
.
parentEl
)
!=
null
?
ref2
:
$
(
document
);
// Make jQuery object if selector is provided
if
(
typeof
this
.
parentEl
===
'
string
'
)
{
this
.
parentEl
=
$
(
this
.
parentEl
);
}
// Store the `location` object, allowing for easier stubbing in tests
this
.
_location
=
location
;
// Set tab states
this
.
loaded
=
{};
ref3
=
this
.
parentEl
.
find
(
'
.nav-links a
'
);
for
(
i
=
0
,
len
=
ref3
.
length
;
i
<
len
;
i
++
)
{
item
=
ref3
[
i
];
this
.
loaded
[
$
(
item
).
attr
(
'
data-action
'
)]
=
false
;
}
// Actions
this
.
actions
=
Object
.
keys
(
this
.
loaded
);
this
.
bindEvents
();
// Set active tab
if
(
this
.
action
===
'
show
'
)
{
this
.
action
=
this
.
defaultAction
;
}
...
...
@@ -25,6 +88,7 @@
}
UserTabs
.
prototype
.
bindEvents
=
function
()
{
// Toggle event listeners
return
this
.
parentEl
.
off
(
'
shown.bs.tab
'
,
'
.nav-links a[data-toggle="tab"]
'
).
on
(
'
shown.bs.tab
'
,
'
.nav-links a[data-toggle="tab"]
'
,
this
.
tabShown
);
};
...
...
@@ -74,6 +138,7 @@
tabSelector
=
'
div#
'
+
action
;
_this
.
parentEl
.
find
(
tabSelector
).
html
(
data
.
html
);
_this
.
loaded
[
action
]
=
true
;
// Fix tooltips
return
gl
.
utils
.
localTimeAgo
(
$
(
'
.js-timeago
'
,
tabSelector
));
};
})(
this
)
...
...
@@ -97,13 +162,17 @@
UserTabs
.
prototype
.
setCurrentAction
=
function
(
action
)
{
var
new_state
,
regExp
;
// Remove possible actions from URL
regExp
=
new
RegExp
(
'
\
/(
'
+
this
.
actions
.
join
(
'
|
'
)
+
'
)(
\
.html)?
\
/?$
'
);
new_state
=
this
.
_location
.
pathname
;
// remove trailing slashes
new_state
=
new_state
.
replace
(
/
\/
+$/
,
""
);
new_state
=
new_state
.
replace
(
regExp
,
''
);
// Append the new action if we're on a tab other than 'activity'
if
(
action
!==
this
.
defaultAction
)
{
new_state
+=
"
/
"
+
action
;
}
// Ensure parameters and hash come along for the ride
new_state
+=
this
.
_location
.
search
+
this
.
_location
.
hash
;
history
.
replaceState
({
turbolinks
:
true
,
...
...
app/assets/javascripts/users/calendar.js
View file @
7f6474b2
...
...
@@ -11,6 +11,8 @@
this
.
daySizeWithSpace
=
this
.
daySize
+
(
this
.
daySpace
*
2
);
this
.
monthNames
=
[
'
Jan
'
,
'
Feb
'
,
'
Mar
'
,
'
Apr
'
,
'
May
'
,
'
Jun
'
,
'
Jul
'
,
'
Aug
'
,
'
Sep
'
,
'
Oct
'
,
'
Nov
'
,
'
Dec
'
];
this
.
months
=
[];
// Loop through the timestamps to create a group of objects
// The group of objects will be grouped based on the day of the week they are
this
.
timestampsTmp
=
[];
var
group
=
0
;
...
...
@@ -29,12 +31,15 @@
var
day
=
date
.
getDay
();
var
count
=
timestamps
[
date
.
getTime
()
*
0.001
];
// Create a new group array if this is the first day of the week
// or if is first object
if
((
day
===
0
&&
i
!==
0
)
||
i
===
0
)
{
this
.
timestampsTmp
.
push
([]);
group
++
;
}
var
innerArray
=
this
.
timestampsTmp
[
group
-
1
];
// Push to the inner array the values that will be used to render map
innerArray
.
push
({
count
:
count
||
0
,
date
:
date
,
...
...
@@ -42,8 +47,10 @@
});
}
// Init color functions
this
.
colorKey
=
this
.
initColorKey
();
this
.
color
=
this
.
initColor
();
// Init the svg element
this
.
renderSvg
(
group
);
this
.
renderDays
();
this
.
renderMonths
();
...
...
app/assets/javascripts/users/users_bundle.js
View file @
7f6474b2
...
...
@@ -3,5 +3,4 @@
(
function
()
{
}).
call
(
this
);
app/assets/javascripts/users_select.js
View file @
7f6474b2
...
...
@@ -81,6 +81,7 @@
if
(
term
.
length
===
0
)
{
showDivider
=
0
;
if
(
firstUser
)
{
// Move current user to the front of the list
for
(
index
=
j
=
0
,
len
=
users
.
length
;
j
<
len
;
index
=
++
j
)
{
obj
=
users
[
index
];
if
(
obj
.
username
===
firstUser
)
{
...
...
@@ -115,6 +116,7 @@
if
(
showDivider
)
{
users
.
splice
(
showDivider
,
0
,
"
divider
"
);
}
// Send the data back
return
callback
(
users
);
});
},
...
...
@@ -139,6 +141,7 @@
inputId
:
'
issue_assignee_id
'
,
hidden
:
function
(
e
)
{
$selectbox
.
hide
();
// display:block overrides the hide-collapse rule
return
$value
.
css
(
'
display
'
,
''
);
},
clicked
:
function
(
user
,
$el
,
e
)
{
...
...
@@ -177,6 +180,7 @@
img
=
"
<img src='
"
+
avatar
+
"
' class='avatar avatar-inline' width='30' />
"
;
}
}
// split into three parts so we can remove the username section if nessesary
listWithName
=
"
<li> <a href='#' class='dropdown-menu-user-link
"
+
selected
+
"
'>
"
+
img
+
"
<strong class='dropdown-menu-user-full-name'>
"
+
user
.
name
+
"
</strong>
"
;
listWithUserName
=
"
<span class='dropdown-menu-user-username'>
"
+
username
+
"
</span>
"
;
listClosingTags
=
"
</a> </li>
"
;
...
...
@@ -215,6 +219,7 @@
};
if
(
query
.
term
.
length
===
0
)
{
if
(
firstUser
)
{
// Move current user to the front of the list
ref
=
data
.
results
;
for
(
index
=
j
=
0
,
len
=
ref
.
length
;
j
<
len
;
index
=
++
j
)
{
obj
=
ref
[
index
];
...
...
@@ -271,6 +276,7 @@
return
_this
.
formatSelection
.
apply
(
_this
,
args
);
},
dropdownCssClass
:
"
ajax-users-dropdown
"
,
// we do not want to escape markup since we are displaying html in results
escapeMarkup
:
function
(
m
)
{
return
m
;
}
...
...
@@ -318,6 +324,8 @@
});
};
// Return users list. Filtered by query
// Only active users retrieved
UsersSelect
.
prototype
.
users
=
function
(
query
,
options
,
callback
)
{
var
url
;
url
=
this
.
buildUrl
(
this
.
usersPath
);
...
...
app/assets/javascripts/zen_mode.js
View file @
7f6474b2
// Zen Mode (full screen) textarea
//
/*= provides zen_mode:enter */
/*= provides zen_mode:leave */
//
/*= require jquery.scrollTo */
/*= require dropzone */
/*= require mousetrap */
/*= require mousetrap/pause */
//
// ### Events
//
// `zen_mode:enter`
//
// Fired when the "Edit in fullscreen" link is clicked.
//
// **Synchronicity** Sync
// **Bubbles** Yes
// **Cancelable** No
// **Target** a.js-zen-enter
//
// `zen_mode:leave`
//
// Fired when the "Leave Fullscreen" link is clicked.
//
// **Synchronicity** Sync
// **Bubbles** Yes
// **Cancelable** No
// **Target** a.js-zen-leave
//
(
function
()
{
this
.
ZenMode
=
(
function
()
{
function
ZenMode
()
{
...
...
@@ -40,6 +53,7 @@
};
})(
this
));
$
(
document
).
on
(
'
keydown
'
,
function
(
e
)
{
// Esc
if
(
e
.
keyCode
===
27
)
{
e
.
preventDefault
();
return
$
(
document
).
trigger
(
'
zen_mode:leave
'
);
...
...
@@ -52,6 +66,7 @@
this
.
active_backdrop
=
$
(
backdrop
);
this
.
active_backdrop
.
addClass
(
'
fullscreen
'
);
this
.
active_textarea
=
this
.
active_backdrop
.
find
(
'
textarea
'
);
// Prevent a user-resized textarea from persisting to fullscreen
this
.
active_textarea
.
removeAttr
(
'
style
'
);
return
this
.
active_textarea
.
focus
();
};
...
...
spec/javascripts/awards_handler_spec.js
View file @
7f6474b2
/*= require awards_handler */
/*= require jquery */
/*= require jquery.cookie */
/*= require ./fixtures/emoji_menu */
(
function
()
{
...
...
@@ -33,6 +27,7 @@
return
setTimeout
(
function
()
{
assertFn
();
return
done
();
// Maybe jasmine.clock here?
},
333
);
};
...
...
spec/javascripts/behaviors/quick_submit_spec.js
View file @
7f6474b2
...
...
@@ -8,6 +8,7 @@
beforeEach
(
function
()
{
fixture
.
load
(
'
behaviors/quick_submit.html
'
);
$
(
'
form
'
).
submit
(
function
(
e
)
{
// Prevent a form submit from moving us off the testing page
return
e
.
preventDefault
();
});
return
this
.
spies
=
{
...
...
@@ -38,6 +39,8 @@
expect
(
$
(
'
input[type=submit]
'
)).
toBeDisabled
();
return
expect
(
$
(
'
button[type=submit]
'
)).
toBeDisabled
();
});
// We cannot stub `navigator.userAgent` for CI's `rake teaspoon` task, so we'll
// only run the tests that apply to the current platform
if
(
navigator
.
userAgent
.
match
(
/Macintosh/
))
{
it
(
'
responds to Meta+Enter
'
,
function
()
{
$
(
'
input.quick-submit-input
'
).
trigger
(
keydownEvent
());
...
...
spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
View file @
7f6474b2
spec/javascripts/issue_spec.js
View file @
7f6474b2
/*= require lib/utils/text_utility */
/*= require issue */
(
function
()
{
...
...
spec/javascripts/new_branch_spec.js
View file @
7f6474b2
/*= require jquery-ui/autocomplete */
/*= require new_branch_form */
(
function
()
{
...
...
spec/javascripts/project_title_spec.js
View file @
7f6474b2
/*= require bootstrap */
/*= require select2 */
/*= require lib/utils/type_utility */
/*= require gl_dropdown */
/*= require api */
/*= require project_select */
/*= require project */
(
function
()
{
...
...
spec/javascripts/right_sidebar_spec.js
View file @
7f6474b2
/*= require right_sidebar */
/*= require jquery */
/*= require jquery.cookie */
(
function
()
{
...
...
spec/javascripts/search_autocomplete_spec.js
View file @
7f6474b2
/*= require gl_dropdown */
/*= require search_autocomplete */
/*= require jquery */
/*= require lib/utils/common_utils */
/*= require lib/utils/type_utility */
/*= require fuzzaldrin-plus */
(
function
()
{
...
...
@@ -43,6 +33,8 @@
groupName
=
'
Gitlab Org
'
;
// Add required attributes to body before starting the test.
// section would be dashboard|group|project
addBodyAttributes
=
function
(
section
)
{
var
$body
;
if
(
section
==
null
)
{
...
...
@@ -64,6 +56,7 @@
}
};
// Mock `gl` object in window for dashboard specific page. App code will need it.
mockDashboardOptions
=
function
()
{
window
.
gl
||
(
window
.
gl
=
{});
return
window
.
gl
.
dashboardOptions
=
{
...
...
@@ -72,6 +65,7 @@
};
};
// Mock `gl` object in window for project specific page. App code will need it.
mockProjectOptions
=
function
()
{
window
.
gl
||
(
window
.
gl
=
{});
return
window
.
gl
.
projectOptions
=
{
...
...
spec/javascripts/shortcuts_issuable_spec.js
View file @
7f6474b2
...
...
@@ -10,6 +10,7 @@
});
return
describe
(
'
#replyWithSelectedText
'
,
function
()
{
var
stubSelection
;
// Stub window.getSelection to return the provided String.
stubSelection
=
function
(
text
)
{
return
window
.
getSelection
=
function
()
{
return
text
;
...
...
spec/javascripts/spec_helper.js
View file @
7f6474b2
/*= require support/bind-poly */
(
function
()
{
/*= require jquery */
}).
call
(
this
);
// PhantomJS (Teaspoons default driver) doesn't have support for
// Function.prototype.bind, which has caused confusion. Use this polyfill to
// avoid the confusion.
/*= require support/bind-poly */
// You can require your own javascript files here. By default this will include
// everything in application, however you may get better load performance if you
// require the specific files that are being used in the spec that tests them.
/*= require jquery */
/*= require jquery.turbolinks */
/*= require bootstrap */
/*= require underscore */
// Teaspoon includes some support files, but you can use anything from your own
// support path too.
// require support/jasmine-jquery-1.7.0
// require support/jasmine-jquery-2.0.0
/*= require support/jasmine-jquery-2.1.0 */
(
function
()
{
}).
call
(
this
);
// require support/sinon
// require support/your-support-file
// Deferring execution
// If you're using CommonJS, RequireJS or some other asynchronous library you can
// defer execution. Call Teaspoon.execute() after everything has been loaded.
// Simple example of a timeout:
// Teaspoon.defer = true
// setTimeout(Teaspoon.execute, 1000)
// Matching files
// By default Teaspoon will look for files that match
// _spec.{js,js.coffee,.coffee}. Add a filename_spec.js file in your spec path
// and it'll be included in the default suite automatically. If you want to
// customize suites, check out the configuration in teaspoon_env.rb
// Manifest
// If you'd rather require your spec files manually (to control order for
// instance) you can disable the suite matcher in the configuration and use this
// file as a manifest.
// For more information: http://github.com/modeset/teaspoon
spec/javascripts/u2f/authenticate_spec.js
View file @
7f6474b2
/*= require u2f/authenticate */
/*= require u2f/util */
/*= require u2f/error */
/*= require u2f */
/*= require ./mock_u2f_device */
(
function
()
{
...
...
spec/javascripts/u2f/register_spec.js
View file @
7f6474b2
/*= require u2f/register */
/*= require u2f/util */
/*= require u2f/error */
/*= require u2f */
/*= require ./mock_u2f_device */
(
function
()
{
...
...
spec/javascripts/zen_mode_spec.js
View file @
7f6474b2
...
...
@@ -14,8 +14,10 @@
return
true
;
}
};
// Stub Dropzone.forElement(...).enable()
});
this
.
zen
=
new
ZenMode
();
// Set this manually because we can't actually scroll the window
return
this
.
zen
.
scroll_position
=
456
;
});
describe
(
'
on enter
'
,
function
()
{
...
...
@@ -60,7 +62,7 @@
return
$
(
'
a.js-zen-enter
'
).
click
();
};
exitZen
=
function
()
{
exitZen
=
function
()
{
// Ohmmmmmmm
return
$
(
'
a.js-zen-leave
'
).
click
();
};
...
...
vendor/assets/javascripts/task_list.js
View file @
7f6474b2
// 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.
(
function
()
{
var
codeFencesPattern
,
complete
,
completePattern
,
disableTaskList
,
disableTaskLists
,
enableTaskList
,
enableTaskLists
,
escapePattern
,
incomplete
,
incompletePattern
,
itemPattern
,
itemsInParasPattern
,
updateTaskList
,
updateTaskListItem
,
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
;
};
...
...
@@ -18,20 +121,48 @@
complete
=
"
[x]
"
;
// Escapes the String for regular expression matching.
escapePattern
=
function
(
str
)
{
return
str
.
replace
(
/
([\[\]])
/g
,
"
\\
$1
"
).
replace
(
/
\s
/
,
"
\\
s
"
).
replace
(
"
x
"
,
"
[xX]
"
);
};
incompletePattern
=
RegExp
(
""
+
(
escapePattern
(
incomplete
)));
completePattern
=
RegExp
(
""
+
(
escapePattern
(
complete
)));
incompletePattern
=
RegExp
(
""
+
(
escapePattern
(
incomplete
)));
// escape square brackets
// match all white space
completePattern
=
RegExp
(
""
+
(
escapePattern
(
complete
)));
// match all cases
// Pattern used to identify all task list items.
// Useful when you need iterate over all items.
itemPattern
=
RegExp
(
"
^(?:
\\
s*(?:>
\\
s*)*(?:[-+*]|(?:
\\
d+
\\
.)))
\\
s*(
"
+
(
escapePattern
(
complete
))
+
"
|
"
+
(
escapePattern
(
incomplete
))
+
"
)
\\
s+(?!
\\
(.*?
\\
))(?=(?:
\\
[.*?
\\
]
\\
s*(?:
\\
[.*?
\\
]|
\\
(.*?
\\
))
\\
s*)*(?:[^
\\
[]|$))
"
);
// prefix, consisting of
// optional leading whitespace
// zero or more blockquotes
// list item indicator
// optional whitespace prefix
// checkbox
// is followed by whitespace
// is not part of a [foo](url) link
// and is followed by zero or more links
// 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
+
)?[\S\s]
.*
[\S\s]
^`
{3}
$/mg
;
// ```
// followed by optional language
// whitespace
// code
// whitespace
// ```
// Used to filter out potential mismatches (items not in lists).
// http://rubular.com/r/OInl6CiePy
itemsInParasPattern
=
RegExp
(
"
^(
"
+
(
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
=
function
(
source
,
itemIndex
,
checked
)
{
var
clean
,
index
,
line
,
result
;
clean
=
source
.
replace
(
/
\r
/g
,
''
).
replace
(
codeFencesPattern
,
''
).
replace
(
itemsInParasPattern
,
''
).
split
(
"
\n
"
);
...
...
@@ -55,6 +186,9 @@
return
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
=
function
(
$item
)
{
var
$container
,
$field
,
checked
,
event
,
index
;
$container
=
$item
.
closest
(
'
.js-task-list-container
'
);
...
...
@@ -70,10 +204,12 @@
}
};
// When the task list item checkbox is updated, submit the change
$
(
document
).
on
(
'
change
'
,
'
.task-list-item-checkbox
'
,
function
()
{
return
updateTaskList
(
$
(
this
));
});
// Enables TaskList item changes.
enableTaskList
=
function
(
$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
);
...
...
@@ -81,6 +217,7 @@
}
};
// Enables a collection of TaskList containers.
enableTaskLists
=
function
(
$containers
)
{
var
container
,
i
,
len
,
results
;
results
=
[];
...
...
@@ -91,11 +228,13 @@
return
results
;
};
// Disable TaskList item changes.
disableTaskList
=
function
(
$container
)
{
$container
.
find
(
'
.task-list-item
'
).
removeClass
(
'
enabled
'
).
find
(
'
.task-list-item-checkbox
'
).
attr
(
'
disabled
'
,
'
disabled
'
);
return
$container
.
removeClass
(
'
is-task-list-enabled
'
).
trigger
(
'
tasklist:disabled
'
);
};
// Disables a collection of TaskList containers.
disableTaskLists
=
function
(
$containers
)
{
var
container
,
i
,
len
,
results
;
results
=
[];
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment