Commit 69852abd authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'master' into '7-7-stable'

Changes from master into 7.7

There are list of changes it will be good to have in 7.7 release like comment improvements and fixes to CI on tags

See merge request !1402
parents 46139825 23255bff
...@@ -24,6 +24,10 @@ v 7.7.0 ...@@ -24,6 +24,10 @@ v 7.7.0
- Trigger GitLab CI when push tags - Trigger GitLab CI when push tags
- When accept merge request - do merge using sidaekiq job - When accept merge request - do merge using sidaekiq job
- Enable web signups by default - Enable web signups by default
- Fixes for diff comments: drag-n-drop images, selecting images
- Fixes for edit comments: drag-n-drop images, preview mode, selecting images, save & update
- Remove password strength indicator
v 7.6.0 v 7.6.0
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#= require jquery.turbolinks #= require jquery.turbolinks
#= require turbolinks #= require turbolinks
#= require bootstrap #= require bootstrap
#= require password_strength
#= require select2 #= require select2
#= require raphael #= require raphael
#= require g.raphael-min #= require g.raphael-min
...@@ -109,9 +108,19 @@ window.unbindEvents = -> ...@@ -109,9 +108,19 @@ window.unbindEvents = ->
$(document).unbind('scroll') $(document).unbind('scroll')
$(document).off('scroll') $(document).off('scroll')
window.shiftWindow = ->
scrollBy 0, -50
document.addEventListener("page:fetch", unbindEvents) document.addEventListener("page:fetch", unbindEvents)
# Scroll the window to avoid the topnav bar
# https://github.com/twitter/bootstrap/issues/1768
if location.hash
setTimeout shiftWindow, 1
window.addEventListener "hashchange", shiftWindow
$ -> $ ->
# Click a .one_click_select field, select the contents # Click a .one_click_select field, select the contents
$(".one_click_select").on 'click', -> $(@).select() $(".one_click_select").on 'click', -> $(@).select()
......
...@@ -33,17 +33,20 @@ class Dispatcher ...@@ -33,17 +33,20 @@ class Dispatcher
GitLab.GfmAutoComplete.setup() GitLab.GfmAutoComplete.setup()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new ZenMode() new ZenMode()
new DropzoneInput($('.issue-form'))
when 'projects:merge_requests:new', 'projects:merge_requests:edit' when 'projects:merge_requests:new', 'projects:merge_requests:edit'
GitLab.GfmAutoComplete.setup() GitLab.GfmAutoComplete.setup()
new Diff() new Diff()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new ZenMode() new ZenMode()
new DropzoneInput($('.merge-request-form'))
when 'projects:merge_requests:show' when 'projects:merge_requests:show'
new Diff() new Diff()
shortcut_handler = new ShortcutsIssueable() shortcut_handler = new ShortcutsIssueable()
new ZenMode() new ZenMode()
when "projects:merge_requests:diffs" when "projects:merge_requests:diffs"
new Diff() new Diff()
new ZenMode()
when 'projects:merge_requests:index' when 'projects:merge_requests:index'
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
when 'dashboard:show' when 'dashboard:show'
...@@ -108,6 +111,7 @@ class Dispatcher ...@@ -108,6 +111,7 @@ class Dispatcher
new Wikis() new Wikis()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new ZenMode() new ZenMode()
new DropzoneInput($('.wiki-form'))
when 'snippets', 'labels', 'graphs' when 'snippets', 'labels', 'graphs'
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches' when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches'
......
class @DropzoneInput
constructor: (form) ->
Dropzone.autoDiscover = false
alertClass = "alert alert-danger alert-dismissable div-dropzone-alert"
alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\""
divHover = "<div class=\"div-dropzone-hover\"></div>"
divSpinner = "<div class=\"div-dropzone-spinner\"></div>"
divAlert = "<div class=\"" + alertClass + "\"></div>"
iconPicture = "<i class=\"fa fa-picture-o div-dropzone-icon\"></i>"
iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>"
btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
project_image_path_upload = window.project_image_path_upload or null
form_textarea = $(form).find("textarea.markdown-area")
form_textarea.wrap "<div class=\"div-dropzone\"></div>"
form_dropzone = $(form).find('.div-dropzone')
form_dropzone.parent().addClass "div-dropzone-wrapper"
form_dropzone.append divHover
$(".div-dropzone-hover").append iconPicture
form_dropzone.append divSpinner
$(".div-dropzone-spinner").append iconSpinner
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
# Preview button
$(document).off "click", ".js-md-preview-button"
$(document).on "click", ".js-md-preview-button", (e) ->
###
Shows the Markdown preview.
Lets the server render GFM into Html and displays it.
###
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-md-write-button").parent().removeClass "active"
form.find(".js-md-preview-button").parent().addClass "active"
# toggle content
form.find(".md-write-holder").hide()
form.find(".md-preview-holder").show()
preview = form.find(".js-md-preview")
mdText = form.find(".markdown-area").val()
if mdText.trim().length is 0
preview.text "Nothing to preview."
else
preview.text "Loading..."
$.get($(this).data("url"),
md_text: mdText
).success (previewData) ->
preview.html previewData
# Write button
$(document).off "click", ".js-md-write-button"
$(document).on "click", ".js-md-write-button", (e) ->
###
Shows the Markdown textarea.
###
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-md-write-button").parent().addClass "active"
form.find(".js-md-preview-button").parent().removeClass "active"
# toggle content
form.find(".md-write-holder").show()
form.find(".md-preview-holder").hide()
dropzone = form_dropzone.dropzone(
url: project_image_path_upload
dictDefaultMessage: ""
clickable: true
paramName: "markdown_img"
maxFilesize: 10
uploadMultiple: false
acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png"
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
previewContainer: false
processing: ->
$(".div-dropzone-alert").alert "close"
dragover: ->
form_textarea.addClass "div-dropzone-focus"
form.find(".div-dropzone-hover").css "opacity", 0.7
return
dragleave: ->
form_textarea.removeClass "div-dropzone-focus"
form.find(".div-dropzone-hover").css "opacity", 0
return
drop: ->
form_textarea.removeClass "div-dropzone-focus"
form.find(".div-dropzone-hover").css "opacity", 0
form_textarea.focus()
return
success: (header, response) ->
child = $(dropzone[0]).children("textarea")
$(child).val $(child).val() + formatLink(response.link) + "\n"
return
error: (temp, errorMessage) ->
checkIfMsgExists = $(".error-alert").children().length
if checkIfMsgExists is 0
$(".error-alert").append divAlert
$(".div-dropzone-alert").append btnAlert + errorMessage
return
sending: ->
form_dropzone.find(".div-dropzone-spinner").css
"opacity": 0.7
"display": "inherit"
return
complete: ->
$(".dz-preview").remove()
$(".markdown-area").trigger "input"
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
return
)
child = $(dropzone[0]).children("textarea")
formatLink = (str) ->
"![" + str.alt + "](" + str.url + ")"
handlePaste = (e) ->
e.preventDefault()
my_event = e.originalEvent
if my_event.clipboardData and my_event.clipboardData.items
processItem(my_event)
processItem = (e) ->
image = isImage(e)
if image
filename = getFilename(e) or "image.png"
text = "{{" + filename + "}}"
pasteText(text)
uploadFile image.getAsFile(), filename
else
text = e.clipboardData.getData("text/plain")
pasteText(text)
isImage = (data) ->
i = 0
while i < data.clipboardData.items.length
item = data.clipboardData.items[i]
if item.type.indexOf("image") isnt -1
return item
i++
return false
pasteText = (text) ->
caretStart = $(child)[0].selectionStart
caretEnd = $(child)[0].selectionEnd
textEnd = $(child).val().length
beforeSelection = $(child).val().substring 0, caretStart
afterSelection = $(child).val().substring caretEnd, textEnd
$(child).val beforeSelection + text + afterSelection
form_textarea.trigger "input"
getFilename = (e) ->
if window.clipboardData and window.clipboardData.getData
value = window.clipboardData.getData("Text")
else if e.clipboardData and e.clipboardData.getData
value = e.clipboardData.getData("text/plain")
value = value.split("\r")
value.first()
uploadFile = (item, filename) ->
formData = new FormData()
formData.append "markdown_img", item, filename
$.ajax
url: project_image_path_upload
type: "POST"
data: formData
dataType: "json"
processData: false
contentType: false
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
beforeSend: ->
showSpinner()
closeAlertMessage()
success: (e, textStatus, response) ->
insertToTextArea(filename, formatLink(response.responseJSON.link))
error: (response) ->
showError(response.responseJSON.message)
complete: ->
closeSpinner()
insertToTextArea = (filename, url) ->
$(child).val (index, val) ->
val.replace("{{" + filename + "}}", url + "\n")
appendToTextArea = (url) ->
$(child).val (index, val) ->
val + url + "\n"
showSpinner = (e) ->
form.find(".div-dropzone-spinner").css
"opacity": 0.7
"display": "inherit"
closeSpinner = ->
form.find(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
showError = (message) ->
checkIfMsgExists = $(".error-alert").children().length
if checkIfMsgExists is 0
$(".error-alert").append divAlert
$(".div-dropzone-alert").append btnAlert + message
closeAlertMessage = ->
form.find(".div-dropzone-alert").alert "close"
form.find(".markdown-selector").click (e) ->
e.preventDefault()
$(@).closest('.gfm-form').find('.div-dropzone').click()
return
formatLink: (str) ->
"![" + str.alt + "](" + str.url + ")"
formatLink = (str) ->
"![" + str.alt + "](" + str.url + ")"
$(document).ready ->
alertClass = "alert alert-danger alert-dismissable div-dropzone-alert"
alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\""
divHover = "<div class=\"div-dropzone-hover\"></div>"
divSpinner = "<div class=\"div-dropzone-spinner\"></div>"
divAlert = "<div class=\"" + alertClass + "\"></div>"
iconPicture = "<i class=\"fa fa-picture-o div-dropzone-icon\"></i>"
iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>"
btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
project_image_path_upload = window.project_image_path_upload or null
$("textarea.markdown-area").wrap "<div class=\"div-dropzone\"></div>"
$(".div-dropzone").parent().addClass "div-dropzone-wrapper"
$(".div-dropzone").append divHover
$(".div-dropzone-hover").append iconPicture
$(".div-dropzone").append divSpinner
$(".div-dropzone-spinner").append iconSpinner
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
# Preview button
$(document).off "click", ".js-md-preview-button"
$(document).on "click", ".js-md-preview-button", (e) ->
###
Shows the Markdown preview.
Lets the server render GFM into Html and displays it.
###
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-md-write-button").parent().removeClass "active"
form.find(".js-md-preview-button").parent().addClass "active"
# toggle content
form.find(".md-write-holder").hide()
form.find(".md-preview-holder").show()
preview = form.find(".js-md-preview")
mdText = form.find(".markdown-area").val()
if mdText.trim().length is 0
preview.text "Nothing to preview."
else
preview.text "Loading..."
$.get($(this).data("url"),
md_text: mdText
).success (previewData) ->
preview.html previewData
# Write button
$(document).off "click", ".js-md-write-button"
$(document).on "click", ".js-md-write-button", (e) ->
###
Shows the Markdown textarea.
###
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-md-write-button").parent().addClass "active"
form.find(".js-md-preview-button").parent().removeClass "active"
# toggle content
form.find(".md-write-holder").show()
form.find(".md-preview-holder").hide()
dropzone = $(".div-dropzone").dropzone(
url: project_image_path_upload
dictDefaultMessage: ""
clickable: true
paramName: "markdown_img"
maxFilesize: 10
uploadMultiple: false
acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png"
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
previewContainer: false
processing: ->
$(".div-dropzone-alert").alert "close"
dragover: ->
$(".div-dropzone > textarea").addClass "div-dropzone-focus"
$(".div-dropzone-hover").css "opacity", 0.7
return
dragleave: ->
$(".div-dropzone > textarea").removeClass "div-dropzone-focus"
$(".div-dropzone-hover").css "opacity", 0
return
drop: ->
$(".div-dropzone > textarea").removeClass "div-dropzone-focus"
$(".div-dropzone-hover").css "opacity", 0
$(".div-dropzone > textarea").focus()
return
success: (header, response) ->
child = $(dropzone[0]).children("textarea")
$(child).val $(child).val() + formatLink(response.link) + "\n"
return
error: (temp, errorMessage) ->
checkIfMsgExists = $(".error-alert").children().length
if checkIfMsgExists is 0
$(".error-alert").append divAlert
$(".div-dropzone-alert").append btnAlert + errorMessage
return
sending: ->
$(".div-dropzone-spinner").css
"opacity": 0.7
"display": "inherit"
return
complete: ->
$(".dz-preview").remove()
$(".markdown-area").trigger "input"
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
return
)
child = $(dropzone[0]).children("textarea")
formatLink = (str) ->
"![" + str.alt + "](" + str.url + ")"
handlePaste = (e) ->
e.preventDefault()
my_event = e.originalEvent
if my_event.clipboardData and my_event.clipboardData.items
processItem(my_event)
processItem = (e) ->
image = isImage(e)
if image
filename = getFilename(e) or "image.png"
text = "{{" + filename + "}}"
pasteText(text)
uploadFile image.getAsFile(), filename
else
text = e.clipboardData.getData("text/plain")
pasteText(text)
isImage = (data) ->
i = 0
while i < data.clipboardData.items.length
item = data.clipboardData.items[i]
if item.type.indexOf("image") isnt -1
return item
i++
return false
pasteText = (text) ->
caretStart = $(child)[0].selectionStart
caretEnd = $(child)[0].selectionEnd
textEnd = $(child).val().length
beforeSelection = $(child).val().substring 0, caretStart
afterSelection = $(child).val().substring caretEnd, textEnd
$(child).val beforeSelection + text + afterSelection
$(".markdown-area").trigger "input"
getFilename = (e) ->
if window.clipboardData and window.clipboardData.getData
value = window.clipboardData.getData("Text")
else if e.clipboardData and e.clipboardData.getData
value = e.clipboardData.getData("text/plain")
value = value.split("\r")
value.first()
uploadFile = (item, filename) ->
formData = new FormData()
formData.append "markdown_img", item, filename
$.ajax
url: project_image_path_upload
type: "POST"
data: formData
dataType: "json"
processData: false
contentType: false
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
beforeSend: ->
showSpinner()
closeAlertMessage()
success: (e, textStatus, response) ->
insertToTextArea(filename, formatLink(response.responseJSON.link))
error: (response) ->
showError(response.responseJSON.message)
complete: ->
closeSpinner()
insertToTextArea = (filename, url) ->
$(child).val (index, val) ->
val.replace("{{" + filename + "}}", url + "\n")
appendToTextArea = (url) ->
$(child).val (index, val) ->
val + url + "\n"
showSpinner = (e) ->
$(".div-dropzone-spinner").css
"opacity": 0.7
"display": "inherit"
closeSpinner = ->
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
showError = (message) ->
checkIfMsgExists = $(".error-alert").children().length
if checkIfMsgExists is 0
$(".error-alert").append divAlert
$(".div-dropzone-alert").append btnAlert + message
closeAlertMessage = ->
$(".div-dropzone-alert").alert "close"
$(".markdown-selector").click (e) ->
e.preventDefault()
$(@).closest('.gfm-form').find('.div-dropzone').click()
return
return
...@@ -219,6 +219,7 @@ class @Notes ...@@ -219,6 +219,7 @@ class @Notes
setupNoteForm: (form) -> setupNoteForm: (form) ->
disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button") disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
form.removeClass "js-new-note-form" form.removeClass "js-new-note-form"
form.find('.div-dropzone').remove()
# setup preview buttons # setup preview buttons
form.find(".js-md-write-button, .js-md-preview-button").tooltip placement: "left" form.find(".js-md-write-button, .js-md-preview-button").tooltip placement: "left"
...@@ -233,6 +234,7 @@ class @Notes ...@@ -233,6 +234,7 @@ class @Notes
# remove notify commit author checkbox for non-commit notes # remove notify commit author checkbox for non-commit notes
form.find(".js-notify-commit-author").remove() if form.find("#note_noteable_type").val() isnt "Commit" form.find(".js-notify-commit-author").remove() if form.find("#note_noteable_type").val() isnt "Commit"
GitLab.GfmAutoComplete.setup() GitLab.GfmAutoComplete.setup()
new DropzoneInput(form)
form.show() form.show()
...@@ -259,8 +261,10 @@ class @Notes ...@@ -259,8 +261,10 @@ class @Notes
Updates the current note field. Updates the current note field.
### ###
updateNote: (xhr, note, status) => updateNote: (xhr, note, status) =>
note_li = $("#note_" + note.id) note_li = $(".note-row-" + note.id)
note_li.replaceWith(note.html) note_li.replaceWith(note.html)
note_li.find('.note-edit-form').hide()
note_li.find('.note-text').show()
code = "#note_" + note.id + " .highlight pre code" code = "#note_" + note.id + " .highlight pre code"
$(code).each (i, e) -> $(code).each (i, e) ->
hljs.highlightBlock(e) hljs.highlightBlock(e)
...@@ -276,11 +280,19 @@ class @Notes ...@@ -276,11 +280,19 @@ class @Notes
e.preventDefault() e.preventDefault()
note = $(this).closest(".note") note = $(this).closest(".note")
note.find(".note-text").hide() note.find(".note-text").hide()
note.find(".note-header").hide()
base_form = note.find(".note-edit-form")
form = base_form.clone().insertAfter(base_form)
form.addClass('current-note-edit-form')
form.find('.div-dropzone').remove()
# Show the attachment delete link # Show the attachment delete link
note.find(".js-note-attachment-delete").show() note.find(".js-note-attachment-delete").show()
# Setup markdown form
GitLab.GfmAutoComplete.setup() GitLab.GfmAutoComplete.setup()
form = note.find(".note-edit-form") new DropzoneInput(form)
form.show() form.show()
textarea = form.find("textarea") textarea = form.find("textarea")
textarea.focus() textarea.focus()
...@@ -295,8 +307,8 @@ class @Notes ...@@ -295,8 +307,8 @@ class @Notes
e.preventDefault() e.preventDefault()
note = $(this).closest(".note") note = $(this).closest(".note")
note.find(".note-text").show() note.find(".note-text").show()
note.find(".js-note-attachment-delete").hide() note.find(".note-header").show()
note.find(".note-edit-form").hide() note.find(".current-note-edit-form").remove()
### ###
Called in response to deleting a note of any kind. Called in response to deleting a note of any kind.
......
#= require pwstrength-bootstrap-1.2.2
overwritten_messages =
wordSimilarToUsername: "Your password should not contain your username"
overwritten_rules =
wordSequences: false
options =
showProgressBar: false
showVerdicts: false
showPopover: true
showErrors: true
showStatus: true
errorMessages: overwritten_messages
$(document).ready ->
profileOptions = {}
profileOptions.ui = options
profileOptions.rules =
activated: overwritten_rules
deviseOptions = {}
deviseOptions.common =
usernameField: "#user_username"
deviseOptions.ui = options
deviseOptions.rules =
activated: overwritten_rules
$("#user_password_profile").pwstrength profileOptions
$("#user_password_sign_up").pwstrength deviseOptions
$("#user_password_recover").pwstrength deviseOptions
...@@ -10,7 +10,15 @@ class @ZenMode ...@@ -10,7 +10,15 @@ class @ZenMode
if not @active_checkbox if not @active_checkbox
@scroll_position = window.pageYOffset @scroll_position = window.pageYOffset
$('body').on 'change', '.zennable input[type=checkbox]', (e) => $('body').on 'click', '.zen-enter-link', (e) =>
e.preventDefault()
$(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', true)
$('body').on 'click', '.zen-leave-link', (e) =>
e.preventDefault()
$(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', false)
$('body').on 'change', '.zen-toggle-comment', (e) =>
checkbox = e.currentTarget checkbox = e.currentTarget
if checkbox.checked if checkbox.checked
# Disable other keyboard shortcuts in ZEN mode # Disable other keyboard shortcuts in ZEN mode
...@@ -32,8 +40,6 @@ class @ZenMode ...@@ -32,8 +40,6 @@ class @ZenMode
@active_zen_area = @active_checkbox.parent().find('textarea') @active_zen_area = @active_checkbox.parent().find('textarea')
@active_zen_area.focus() @active_zen_area.focus()
window.location.hash = ZenMode.fullscreen_prefix + @active_checkbox.prop('id') window.location.hash = ZenMode.fullscreen_prefix + @active_checkbox.prop('id')
# Disable dropzone in ZEN mode
Dropzone.forElement('.div-dropzone').disable()
exitZenMode: => exitZenMode: =>
if @active_zen_area isnt null if @active_zen_area isnt null
......
...@@ -273,10 +273,6 @@ img.emoji { ...@@ -273,10 +273,6 @@ img.emoji {
height: 220px; height: 220px;
} }
.navless-container {
margin-top: 68px;
}
.description-block { .description-block {
@extend .light-well; @extend .light-well;
@extend .light; @extend .light;
......
...@@ -97,136 +97,3 @@ label { ...@@ -97,136 +97,3 @@ label {
.wiki-content { .wiki-content {
margin-top: 35px; margin-top: 35px;
} }
.zennable {
position: relative;
input {
display: none;
}
.collapse {
display: none;
opacity: 0.5;
&:before {
content: '\f066';
font-family: FontAwesome;
color: #000;
font-size: 28px;
position: relative;
padding: 30px 40px 0 0;
}
&:hover {
opacity: 0.8;
}
}
.expand {
opacity: 0.5;
&:before {
content: '\f065';
font-family: FontAwesome;
color: #000;
font-size: 14px;
line-height: 14px;
padding-right: 20px;
position: relative;
vertical-align: middle;
}
&:hover {
opacity: 0.8;
}
}
input:checked ~ .zen-backdrop .expand {
display: none;
}
input:checked ~ .zen-backdrop .collapse {
display: block;
position: absolute;
top: 0;
}
label {
position: absolute;
top: -26px;
right: 0;
font-variant: small-caps;
text-transform: uppercase;
font-size: 10px;
padding: 4px;
font-weight: 500;
letter-spacing: 1px;
&:before {
display: inline-block;
width: 10px;
height: 14px;
}
}
input:checked ~ .zen-backdrop {
background-color: white;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1031;
textarea {
border: none;
box-shadow: none;
border-radius: 0;
color: #000;
font-size: 20px;
line-height: 26px;
padding: 30px;
display: block;
outline: none;
resize: none;
height: 100vh;
max-width: 900px;
margin: 0 auto;
}
}
.zen-backdrop textarea::-webkit-input-placeholder {
color: white;
}
.zen-backdrop textarea:-moz-placeholder {
color: white;
}
.zen-backdrop textarea::-moz-placeholder {
color: white;
}
.zen-backdrop textarea:-ms-input-placeholder {
color: white;
}
input:checked ~ .zen-backdrop textarea::-webkit-input-placeholder {
color: #999;
}
input:checked ~ .zen-backdrop textarea:-moz-placeholder {
color: #999;
opacity: 1;
}
input:checked ~ .zen-backdrop textarea::-moz-placeholder {
color: #999;
opacity: 1;
}
input:checked ~ .zen-backdrop textarea:-ms-input-placeholder {
color: #999;
}
}
.zennable {
position: relative;
input {
display: none;
}
.zen-enter-link {
color: #888;
position: absolute;
top: -26px;
right: 4px;
}
.zen-leave-link {
display: none;
color: #888;
position: absolute;
top: 10px;
right: 10px;
padding: 5px;
font-size: 36px;
&:hover {
color: #111;
}
}
input:checked ~ .zen-backdrop .zen-enter-link {
display: none;
}
input:checked ~ .zen-backdrop .zen-leave-link {
display: block;
position: absolute;
top: 0;
}
input:checked ~ .zen-backdrop {
background-color: white;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1031;
textarea {
border: none;
box-shadow: none;
border-radius: 0;
color: #000;
font-size: 20px;
line-height: 26px;
padding: 30px;
display: block;
outline: none;
resize: none;
height: 100vh;
max-width: 900px;
margin: 0 auto;
}
}
.zen-backdrop textarea::-webkit-input-placeholder {
color: white;
}
.zen-backdrop textarea:-moz-placeholder {
color: white;
}
.zen-backdrop textarea::-moz-placeholder {
color: white;
}
.zen-backdrop textarea:-ms-input-placeholder {
color: white;
}
input:checked ~ .zen-backdrop textarea::-webkit-input-placeholder {
color: #999;
}
input:checked ~ .zen-backdrop textarea:-moz-placeholder {
color: #999;
opacity: 1;
}
input:checked ~ .zen-backdrop textarea::-moz-placeholder {
color: #999;
opacity: 1;
}
input:checked ~ .zen-backdrop textarea:-ms-input-placeholder {
color: #999;
}
}
...@@ -2,6 +2,10 @@ html { ...@@ -2,6 +2,10 @@ html {
overflow-y: scroll; overflow-y: scroll;
&.touch .tooltip { display: none !important; } &.touch .tooltip { display: none !important; }
body {
padding-top: 47px;
}
} }
.container { .container {
...@@ -13,3 +17,6 @@ html { ...@@ -13,3 +17,6 @@ html {
margin: 0 0; margin: 0 0;
} }
.navless-container {
margin-top: 30px;
}
...@@ -65,10 +65,6 @@ ...@@ -65,10 +65,6 @@
max-width: 100%; max-width: 100%;
} }
*:first-child {
margin-top: 0;
}
code { padding: 0 4px; } code { padding: 0 4px; }
h1 { h1 {
......
...@@ -8,8 +8,6 @@ header { ...@@ -8,8 +8,6 @@ header {
margin-bottom: 0; margin-bottom: 0;
min-height: 40px; min-height: 40px;
border: none; border: none;
position: fixed;
top: 0;
width: 100%; width: 100%;
.navbar-inner { .navbar-inner {
......
.markdown-area {
background: #FFF;
border: 1px solid #ddd;
min-height: 100px;
padding: 5px;
font-size: 14px;
box-shadow: none;
width: 100%;
}
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
width: 100%; width: 100%;
padding: 15px; padding: 15px;
background: #FFF; background: #FFF;
margin-top: 48px;
} }
.nav-sidebar { .nav-sidebar {
...@@ -159,3 +158,4 @@ ...@@ -159,3 +158,4 @@
@include expanded-sidebar; @include expanded-sidebar;
} }
/**
* Note Form
*/
.comment-btn {
@extend .btn-create;
}
.reply-btn {
@extend .btn-primary;
}
.diff-file .diff-content {
tr.line_holder:hover {
&> td.line_content {
background: $hover !important;
border-color: darken($hover, 10%) !important;
}
&> td.new_line,
&> td.old_line {
background: darken($hover, 4%) !important;
border-color: darken($hover, 10%) !important;
}
}
tr.line_holder:hover > td .line_note_link {
opacity: 1.0;
filter: alpha(opacity=100);
}
}
.diff-file,
.discussion {
.new_note {
margin: 0;
border: none;
}
}
.new_note {
display: none;
}
.new_note, .edit_note {
.buttons {
float: left;
margin-top: 8px;
}
.clearfix {
margin-bottom: 0;
}
.note-preview-holder {
> p {
overflow-x: auto;
}
}
.note_text {
width: 100%;
}
}
/* loading indicator */
.notes-busy {
margin: 18px;
}
.note-image-attach {
@extend .col-md-4;
@extend .thumbnail;
margin-left: 45px;
float: none;
}
.common-note-form {
margin: 0;
background: #F9F9F9;
padding: 5px;
border: 1px solid #DDD;
}
.note-form-actions {
background: #F9F9F9;
height: 45px;
.note-form-option {
margin-top: 8px;
margin-left: 30px;
@extend .pull-left;
}
.js-notify-commit-author {
float: left;
}
.write-preview-btn {
// makes the "absolute" position for links relative to this
position: relative;
// preview/edit buttons
> a {
position: absolute;
right: 5px;
top: 8px;
}
}
}
.note-edit-form {
display: none;
font-size: 13px;
.form-actions {
padding-left: 20px;
.btn-save {
float: left;
}
.note-form-option {
float: left;
padding: 2px 0 0 25px;
}
}
}
.js-note-attachment-delete {
display: none;
}
.parallel-comment {
padding: 6px;
}
.error-alert > .alert {
margin-top: 5px;
margin-bottom: 5px;
}
.discussion-body,
.diff-file {
.notes .note {
border-color: #ddd;
padding: 10px 15px;
}
.discussion-reply-holder {
background: #f9f9f9;
padding: 10px 15px;
border-top: 1px solid #DDD;
}
}
.discussion-notes-count {
font-size: 16px;
}
.edit_note {
.markdown-area {
min-height: 140px;
}
.note-form-actions {
background: transparent;
}
}
.comment-hints {
color: #999;
background: #FFF;
padding: 5px;
margin-top: -7px;
border: 1px solid #DDD;
font-size: 13px;
}
...@@ -190,169 +190,3 @@ ul.notes { ...@@ -190,169 +190,3 @@ ul.notes {
} }
} }
/**
* Note Form
*/
.comment-btn {
@extend .btn-create;
}
.reply-btn {
@extend .btn-primary;
}
.diff-file .diff-content {
tr.line_holder:hover {
&> td.line_content {
background: $hover !important;
border-color: darken($hover, 10%) !important;
}
&> td.new_line,
&> td.old_line {
background: darken($hover, 4%) !important;
border-color: darken($hover, 10%) !important;
}
}
tr.line_holder:hover > td .line_note_link {
opacity: 1.0;
filter: alpha(opacity=100);
}
}
.diff-file,
.discussion {
.new_note {
margin: 0;
border: none;
}
}
.new_note {
display: none;
.buttons {
float: left;
margin-top: 8px;
}
.clearfix {
margin-bottom: 0;
}
.note_text {
background: #FFF;
border: 1px solid #ddd;
min-height: 100px;
padding: 5px;
font-size: 14px;
box-shadow: none;
}
.note-preview-holder {
> p {
overflow-x: auto;
}
}
.note_text {
width: 100%;
}
}
/* loading indicator */
.notes-busy {
margin: 18px;
}
.note-image-attach {
@extend .col-md-4;
@extend .thumbnail;
margin-left: 45px;
float: none;
}
.common-note-form {
margin: 0;
background: #F9F9F9;
padding: 5px;
border: 1px solid #DDD;
}
.note-form-actions {
background: #F9F9F9;
height: 45px;
.note-form-option {
margin-top: 8px;
margin-left: 30px;
@extend .pull-left;
}
.js-notify-commit-author {
float: left;
}
.write-preview-btn {
// makes the "absolute" position for links relative to this
position: relative;
// preview/edit buttons
> a {
position: absolute;
right: 5px;
top: 8px;
}
}
}
.note-edit-form {
display: none;
.note_text {
border: 1px solid #DDD;
box-shadow: none;
font-size: 14px;
height: 80px;
width: 100%;
}
.form-actions {
padding-left: 20px;
.btn-save {
float: left;
}
.note-form-option {
float: left;
padding: 2px 0 0 25px;
}
}
}
.js-note-attachment-delete {
display: none;
}
.parallel-comment {
padding: 6px;
}
.error-alert > .alert {
margin-top: 5px;
margin-bottom: 5px;
}
.discussion-body,
.diff-file {
.notes .note {
border-color: #ddd;
padding: 10px 15px;
}
.discussion-reply-holder {
background: #f9f9f9;
padding: 10px 15px;
border-top: 1px solid #DDD;
}
}
.discussion-notes-count {
font-size: 16px;
}
...@@ -111,20 +111,3 @@ ...@@ -111,20 +111,3 @@
height: 50px; height: 50px;
} }
} }
//CSS for password-strength indicator
#password-strength {
margin-bottom: 0;
}
.has-success input {
background-color: #D6F1D7 !important;
}
.has-error input {
background-color: #F3CECE !important;
}
.has-warning input {
background-color: #FFE9A4 !important;
}
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
.devise-errors .devise-errors
= devise_error_messages! = devise_error_messages!
= f.hidden_field :reset_password_token = f.hidden_field :reset_password_token
.form-group#password-strength %div
= f.password_field :password, class: "form-control top", id: "user_password_recover", placeholder: "New password", required: true = f.password_field :password, class: "form-control top", placeholder: "New password", required: true
%div %div
= f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm new password", required: true = f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm new password", required: true
.clearfix.append-bottom-10 .clearfix.append-bottom-10
......
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
= f.text_field :username, class: "form-control middle", placeholder: "Username", required: true = f.text_field :username, class: "form-control middle", placeholder: "Username", required: true
%div %div
= f.email_field :email, class: "form-control middle", placeholder: "Email", required: true = f.email_field :email, class: "form-control middle", placeholder: "Email", required: true
.form-group#password-strength %div
= f.password_field :password, class: "form-control middle", id: "user_password_sign_up", placeholder: "Password", required: true = f.password_field :password, class: "form-control middle", placeholder: "Password", required: true
%div %div
= f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm password", required: true = f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm password", required: true
%div %div
......
...@@ -2,7 +2,3 @@ ...@@ -2,7 +2,3 @@
.broadcast-message{ style: broadcast_styling(broadcast_message) } .broadcast-message{ style: broadcast_styling(broadcast_message) }
%i.fa.fa-bullhorn %i.fa.fa-bullhorn
= broadcast_message.message = broadcast_message.message
:css
.sidebar-wrapper .nav-sidebar {
margin-top: 58px;
}
%header.navbar.navbar-static-top.navbar-gitlab %header.navbar.navbar-fixed-top.navbar-gitlab
.navbar-inner .navbar-inner
.container .container
%div.app_logo %div.app_logo
......
%header.navbar.navbar-static-top.navbar-gitlab %header.navbar.navbar-fixed-top.navbar-gitlab
.navbar-inner .navbar-inner
.container .container
%div.app_logo %div.app_logo
......
...@@ -24,8 +24,8 @@ ...@@ -24,8 +24,8 @@
%p %p
\— \—
%br %br
- if @project
You're receiving this notification because you are a member of the #{link_to_unless @target_url, @project.name_with_namespace, project_url(@project)} project team.
- if @target_url - if @target_url
#{link_to "View it on GitLab", @target_url} #{link_to "View it on GitLab", @target_url}
= email_action @target_url = email_action @target_url
- if @project
You're receiving this notification because you are a member of the #{link_to_unless @target_url, @project.name_with_namespace, project_url(@project)} project team.
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
.form-group .form-group
= f.label :password, 'New password', class: 'control-label' = f.label :password, 'New password', class: 'control-label'
.col-sm-10 .col-sm-10
= f.password_field :password, required: true, class: 'form-control', id: 'user_password_profile' = f.password_field :password, required: true, class: 'form-control'
.form-group .form-group
= f.label :password_confirmation, class: 'control-label' = f.label :password_confirmation, class: 'control-label'
.col-sm-10 .col-sm-10
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
.col-sm-10= f.password_field :current_password, required: true, class: 'form-control' .col-sm-10= f.password_field :current_password, required: true, class: 'form-control'
.form-group .form-group
= f.label :password, class: 'control-label' = f.label :password, class: 'control-label'
.col-sm-10= f.password_field :password, required: true, class: 'form-control', id: 'user_password_profile' .col-sm-10= f.password_field :password, required: true, class: 'form-control'
.form-group .form-group
= f.label :password_confirmation, class: 'control-label' = f.label :password_confirmation, class: 'control-label'
.col-sm-10 .col-sm-10
......
.zennable .zennable
%input#zen-toggle-comment{ tabindex: '-1', type: 'checkbox' } %input#zen-toggle-comment.zen-toggle-comment{ tabindex: '-1', type: 'checkbox' }
.zen-backdrop .zen-backdrop
- classes << ' js-gfm-input markdown-area' - classes << ' js-gfm-input markdown-area'
= f.text_area attr, class: classes, placeholder: 'Leave a comment' = f.text_area attr, class: classes, placeholder: 'Leave a comment'
%label{ for: 'zen-toggle-comment', class: 'expand' } Edit in fullscreen = link_to nil, class: 'zen-enter-link', tabindex: '-1' do
%label{ for: 'zen-toggle-comment', class: 'collapse' } %i.fa.fa-expand
Edit in fullscreen
= link_to nil, class: 'zen-leave-link' do
%i.fa.fa-compress
.note-edit-form
= form_for note, url: project_note_path(@project, note), method: :put, remote: true, authenticity_token: true do |f|
= render layout: 'projects/md_preview' do
= render 'projects/zen', f: f, attr: :note,
classes: 'note_text js-note-text'
.comment-hints.clearfix
.pull-left Comments are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }}
.pull-right Attach images (JPG, PNG, GIF) by dragging &amp; dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }.
.note-form-actions
.buttons
= f.submit 'Save Comment', class: "btn btn-primary btn-save btn-grouped js-comment-button"
= link_to 'Cancel', "#", class: "btn btn-cancel note-edit-cancel"
.note-form-option.hidden-xs
%a.choose-btn.btn.js-choose-note-attachment-button
%i.fa.fa-paperclip
%span Choose File ...
&nbsp;
%span.file_name.js-attachment-filename
= f.file_field :attachment, class: "js-note-attachment-input hidden"
...@@ -7,8 +7,9 @@ ...@@ -7,8 +7,9 @@
= render layout: 'projects/md_preview' do = render layout: 'projects/md_preview' do
= render 'projects/zen', f: f, attr: :note, = render 'projects/zen', f: f, attr: :note,
classes: 'note_text js-note-text' classes: 'note_text js-note-text'
.light.clearfix
.comment-hints.clearfix
.pull-left Comments are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }} .pull-left Comments are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }}
.pull-right Attach images (JPG, PNG, GIF) by dragging &amp; dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }. .pull-right Attach images (JPG, PNG, GIF) by dragging &amp; dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }.
...@@ -24,7 +25,7 @@ ...@@ -24,7 +25,7 @@
%i.fa.fa-paperclip %i.fa.fa-paperclip
%span Choose File ... %span Choose File ...
&nbsp; &nbsp;
%span.file_name.js-attachment-filename File name... %span.file_name.js-attachment-filename
= f.file_field :attachment, class: "js-note-attachment-input hidden" = f.file_field :attachment, class: "js-note-attachment-input hidden"
:javascript :javascript
......
%li.timeline-entry{ id: dom_id(note), class: [dom_class(note), ('system-note' if note.system)], data: { discussion: note.discussion_id } } %li.timeline-entry{ id: dom_id(note), class: [dom_class(note), "note-row-#{note.id}", ('system-note' if note.system)], data: { discussion: note.discussion_id } }
.timeline-entry-inner .timeline-entry-inner
.timeline-icon .timeline-icon
- if note.system - if note.system
...@@ -42,25 +42,7 @@ ...@@ -42,25 +42,7 @@
.note-text .note-text
= preserve do = preserve do
= markdown(note.note, {no_header_anchors: true}) = markdown(note.note, {no_header_anchors: true})
= render 'projects/notes/edit_form', note: note
.note-edit-form
= form_for note, url: project_note_path(@project, note), method: :put, remote: true, authenticity_token: true do |f|
= render layout: 'projects/md_preview' do
= f.text_area :note, class: 'note_text js-note-text js-gfm-input turn-on'
.form-actions.clearfix
= f.submit 'Save changes', class: "btn btn-primary btn-save js-comment-button"
.note-form-option
%a.choose-btn.btn.js-choose-note-attachment-button
%i.fa.fa-paperclip
%span Choose File ...
&nbsp;
%span.file_name.js-attachment-filename File name...
= f.file_field :attachment, class: "js-note-attachment-input hidden"
= link_to 'Cancel', "#", class: "btn btn-cancel note-edit-cancel"
- if note.attachment.url - if note.attachment.url
.note-attachment .note-attachment
......
...@@ -11,7 +11,7 @@ RUN apt-get update -q \ ...@@ -11,7 +11,7 @@ RUN apt-get update -q \
# If the Omnibus package version below is outdated please contribute a merge request to update it. # If the Omnibus package version below is outdated please contribute a merge request to update it.
# If you run GitLab Enterprise Edition point it to a location where you have downloaded it. # If you run GitLab Enterprise Edition point it to a location where you have downloaded it.
RUN TMP_FILE=$(mktemp); \ RUN TMP_FILE=$(mktemp); \
wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.5.3-omnibus.5.2.1.ci-1_amd64.deb \ wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.6.2-omnibus.5.3.0.ci.1-1_amd64.deb \
&& dpkg -i $TMP_FILE \ && dpkg -i $TMP_FILE \
&& rm -f $TMP_FILE && rm -f $TMP_FILE
......
...@@ -97,22 +97,3 @@ Feature: Profile ...@@ -97,22 +97,3 @@ Feature: Profile
Given I visit profile design page Given I visit profile design page
When I change my code preview theme When I change my code preview theme
Then I should receive feedback that the changes were saved Then I should receive feedback that the changes were saved
@javascript
Scenario: I see the password strength indicator
Given I visit profile password page
When I try to set a weak password
Then I should see the input field yellow
@javascript
Scenario: I see the password strength indicator error
Given I visit profile password page
When I try to set a short password
Then I should see the input field red
And I should see the password error message
@javascript
Scenario: I see the password strength indicator with success
Given I visit profile password page
When I try to set a strong password
Then I should see the input field green
...@@ -58,34 +58,16 @@ class Spinach::Features::Profile < Spinach::FeatureSteps ...@@ -58,34 +58,16 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
step 'I try change my password w/o old one' do step 'I try change my password w/o old one' do
within '.update-password' do within '.update-password' do
fill_in "user_password_profile", with: "22233344" fill_in "user_password", with: "22233344"
fill_in "user_password_confirmation", with: "22233344" fill_in "user_password_confirmation", with: "22233344"
click_button "Save" click_button "Save"
end end
end end
step 'I try to set a weak password' do
within '.update-password' do
fill_in "user_password_profile", with: "22233344"
end
end
step 'I try to set a short password' do
within '.update-password' do
fill_in "user_password_profile", with: "short"
end
end
step 'I try to set a strong password' do
within '.update-password' do
fill_in "user_password_profile", with: "Itulvo9z8uud%$"
end
end
step 'I change my password' do step 'I change my password' do
within '.update-password' do within '.update-password' do
fill_in "user_current_password", with: "12345678" fill_in "user_current_password", with: "12345678"
fill_in "user_password_profile", with: "22233344" fill_in "user_password", with: "22233344"
fill_in "user_password_confirmation", with: "22233344" fill_in "user_password_confirmation", with: "22233344"
click_button "Save" click_button "Save"
end end
...@@ -94,7 +76,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps ...@@ -94,7 +76,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
step 'I unsuccessfully change my password' do step 'I unsuccessfully change my password' do
within '.update-password' do within '.update-password' do
fill_in "user_current_password", with: "12345678" fill_in "user_current_password", with: "12345678"
fill_in "user_password_profile", with: "password" fill_in "user_password", with: "password"
fill_in "user_password_confirmation", with: "confirmation" fill_in "user_password_confirmation", with: "confirmation"
click_button "Save" click_button "Save"
end end
...@@ -104,22 +86,6 @@ class Spinach::Features::Profile < Spinach::FeatureSteps ...@@ -104,22 +86,6 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
page.should have_content "You must provide a valid current password" page.should have_content "You must provide a valid current password"
end end
step 'I should see the input field yellow' do
page.should have_css 'div.has-warning'
end
step 'I should see the input field green' do
page.should have_css 'div.has-success'
end
step 'I should see the input field red' do
page.should have_css 'div.has-error'
end
step 'I should see the password error message' do
page.should have_content 'Your password is too short'
end
step "I should see a password error message" do step "I should see a password error message" do
page.should have_content "Password confirmation doesn't match" page.should have_content "Password confirmation doesn't match"
end end
...@@ -180,7 +146,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps ...@@ -180,7 +146,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
step 'I submit new password' do step 'I submit new password' do
fill_in :user_current_password, with: '12345678' fill_in :user_current_password, with: '12345678'
fill_in :user_password_profile, with: '12345678' fill_in :user_password, with: '12345678'
fill_in :user_password_confirmation, with: '12345678' fill_in :user_password_confirmation, with: '12345678'
click_button "Set new password" click_button "Set new password"
end end
......
module Gitlab module Gitlab
module Git module Git
BLANK_SHA = '0' * 40 BLANK_SHA = '0' * 40
def self.extract_ref_name(ref)
ref.gsub(/\Arefs\/(tags|heads)\//, '')
end
end end
end end
module Gitlab module Gitlab
class PushDataBuilder class PushDataBuilder
# Produce a hash of post-receive data class << self
# # Produce a hash of post-receive data
# data = { #
# before: String, # data = {
# after: String, # before: String,
# ref: String, # after: String,
# user_id: String, # ref: String,
# user_name: String, # user_id: String,
# project_id: String, # user_name: String,
# repository: { # project_id: String,
# name: String, # repository: {
# url: String, # name: String,
# description: String, # url: String,
# homepage: String, # description: String,
# }, # homepage: String,
# commits: Array, # },
# total_commits_count: Fixnum # commits: Array,
# } # total_commits_count: Fixnum
# # }
def self.build(project, user, oldrev, newrev, ref, commits = []) #
# Total commits count def build(project, user, oldrev, newrev, ref, commits = [])
commits_count = commits.size # Total commits count
commits_count = commits.size
# Get latest 20 commits ASC # Get latest 20 commits ASC
commits_limited = commits.last(20) commits_limited = commits.last(20)
# Hash to be passed as post_receive_data # Hash to be passed as post_receive_data
data = { data = {
before: oldrev, before: oldrev,
after: newrev, after: newrev,
ref: ref, ref: ref,
user_id: user.id, checkout_sha: checkout_sha(project.repository, newrev, ref),
user_name: user.name, user_id: user.id,
project_id: project.id, user_name: user.name,
repository: { project_id: project.id,
name: project.name, repository: {
url: project.url_to_repo, name: project.name,
description: project.description, url: project.url_to_repo,
homepage: project.web_url, description: project.description,
}, homepage: project.web_url,
commits: [], },
total_commits_count: commits_count commits: [],
} total_commits_count: commits_count
}
# For performance purposes maximum 20 latest commits # For performance purposes maximum 20 latest commits
# will be passed as post receive hook data. # will be passed as post receive hook data.
commits_limited.each do |commit| commits_limited.each do |commit|
data[:commits] << commit.hook_attrs(project) data[:commits] << commit.hook_attrs(project)
end
data
end end
data # This method provide a sample data generated with
end # existing project and commits to test web hooks
def build_sample(project, user)
commits = project.repository.commits(project.default_branch, nil, 3)
build(project, user, commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}", commits)
end
# This method provide a sample data generated with def checkout_sha(repository, newrev, ref)
# existing project and commits to test web hooks if newrev != Gitlab::Git::BLANK_SHA && ref.start_with?('refs/tags/')
def self.build_sample(project, user) tag_name = Gitlab::Git.extract_ref_name(ref)
commits = project.repository.commits(project.default_branch, nil, 3) tag = repository.find_tag(tag_name)
build(project, user, commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}", commits)
if tag
commit = repository.commit(tag.target)
commit.try(:sha)
end
else
newrev
end
end
end end
end end
end end
...@@ -54,8 +54,8 @@ namespace :gitlab do ...@@ -54,8 +54,8 @@ namespace :gitlab do
<div class='footer' style='margin-top: 10px;'> <div class='footer' style='margin-top: 10px;'>
<p> <p>
<br> <br>
You're receiving this notification because you are a member of the Base / Rails Project project team.
<a href=\"#{url}\">View it on GitLab</a> <a href=\"#{url}\">View it on GitLab</a>
You're receiving this notification because you are a member of the Base / Rails Project project team.
#{schema} #{schema}
</p> </p>
</div> </div>
......
...@@ -72,27 +72,23 @@ describe 'Comments' do ...@@ -72,27 +72,23 @@ describe 'Comments' do
it "should show the note edit form and hide the note body" do it "should show the note edit form and hide the note body" do
within("#note_#{note.id}") do within("#note_#{note.id}") do
find(".current-note-edit-form", visible: true).should be_visible
find(".note-edit-form", visible: true).should be_visible find(".note-edit-form", visible: true).should be_visible
find(".note-text", visible: false).should_not be_visible find(:css, ".note-text", visible: false).should_not be_visible
end end
end end
it "should reset the edit note form textarea with the original content of the note if cancelled" do # TODO: fix after 7.7 release
find('.note').hover #it "should reset the edit note form textarea with the original content of the note if cancelled" do
find(".js-note-edit").click #within(".current-note-edit-form") do
#fill_in "note[note]", with: "Some new content"
within(".note-edit-form") do #find(".btn-cancel").click
fill_in "note[note]", with: "Some new content" #find(".js-note-text", visible: false).text.should == note.note
find(".btn-cancel").click #end
find(".js-note-text", visible: false).text.should == note.note #end
end
end
it "appends the edited at time to the note" do it "appends the edited at time to the note" do
find('.note').hover within(".current-note-edit-form") do
find(".js-note-edit").click
within(".note-edit-form") do
fill_in "note[note]", with: "Some new content" fill_in "note[note]", with: "Some new content"
find(".btn-save").click find(".btn-save").click
end end
...@@ -119,7 +115,7 @@ describe 'Comments' do ...@@ -119,7 +115,7 @@ describe 'Comments' do
it "removes the attachment div and resets the edit form" do it "removes the attachment div and resets the edit form" do
find(".js-note-attachment-delete").click find(".js-note-attachment-delete").click
should_not have_css(".note-attachment") should_not have_css(".note-attachment")
find(".note-edit-form", visible: false).should_not be_visible find(".current-note-edit-form", visible: false).should_not be_visible
end end
end end
end end
......
...@@ -11,7 +11,7 @@ describe 'Users', feature: true do ...@@ -11,7 +11,7 @@ describe 'Users', feature: true do
fill_in "user_name", with: "Name Surname" fill_in "user_name", with: "Name Surname"
fill_in "user_username", with: "Great" fill_in "user_username", with: "Great"
fill_in "user_email", with: "name@mail.com" fill_in "user_email", with: "name@mail.com"
fill_in "user_password_sign_up", with: "password1234" fill_in "user_password", with: "password1234"
fill_in "user_password_confirmation", with: "password1234" fill_in "user_password_confirmation", with: "password1234"
expect { click_button "Sign up" }.to change {User.count}.by(1) expect { click_button "Sign up" }.to change {User.count}.by(1)
end end
......
...@@ -21,13 +21,14 @@ describe 'Gitlab::PushDataBuilder' do ...@@ -21,13 +21,14 @@ describe 'Gitlab::PushDataBuilder' do
Gitlab::PushDataBuilder.build(project, Gitlab::PushDataBuilder.build(project,
user, user,
Gitlab::Git::BLANK_SHA, Gitlab::Git::BLANK_SHA,
'5937ac0a7beb003549fc5fd26fc247adbce4a52e', '8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b',
'refs/tags/v1.1.0') 'refs/tags/v1.1.0')
end end
it { data.should be_a(Hash) } it { data.should be_a(Hash) }
it { data[:before].should == Gitlab::Git::BLANK_SHA } it { data[:before].should == Gitlab::Git::BLANK_SHA }
it { data[:after].should == '5937ac0a7beb003549fc5fd26fc247adbce4a52e' } it { data[:checkout_sha].should == '5937ac0a7beb003549fc5fd26fc247adbce4a52e' }
it { data[:after].should == '8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b' }
it { data[:ref].should == 'refs/tags/v1.1.0' } it { data[:ref].should == 'refs/tags/v1.1.0' }
it { data[:commits].should be_empty } it { data[:commits].should be_empty }
it { data[:total_commits_count].should be_zero } it { data[:total_commits_count].should be_zero }
......
/*!
* jQuery Password Strength plugin for Twitter Bootstrap
*
* Copyright (c) 2008-2013 Tane Piper
* Copyright (c) 2013 Alejandro Blanco
* Dual licensed under the MIT and GPL licenses.
*/
(function (jQuery) {
// Source: src/rules.js
var rulesEngine = {};
try {
if (!jQuery && module && module.exports) {
var jQuery = require("jquery"),
jsdom = require("jsdom").jsdom;
jQuery = jQuery(jsdom().parentWindow);
}
} catch (ignore) {}
(function ($, rulesEngine) {
"use strict";
var validation = {};
rulesEngine.forbiddenSequences = [
"0123456789", "abcdefghijklmnopqrstuvwxyz", "qwertyuiop", "asdfghjkl",
"zxcvbnm", "!@#$%^&*()_+"
];
validation.wordNotEmail = function (options, word, score) {
if (word.match(/^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i)) {
return score;
}
return 0;
};
validation.wordLength = function (options, word, score) {
var wordlen = word.length,
lenScore = Math.pow(wordlen, options.rules.raisePower);
if (wordlen < options.common.minChar) {
lenScore = (lenScore + score);
}
return lenScore;
};
validation.wordSimilarToUsername = function (options, word, score) {
var username = $(options.common.usernameField).val();
if (username && word.toLowerCase().match(username.toLowerCase())) {
return score;
}
return 0;
};
validation.wordTwoCharacterClasses = function (options, word, score) {
if (word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) ||
(word.match(/([a-zA-Z])/) && word.match(/([0-9])/)) ||
(word.match(/(.[!,@,#,$,%,\^,&,*,?,_,~])/) && word.match(/[a-zA-Z0-9_]/))) {
return score;
}
return 0;
};
validation.wordRepetitions = function (options, word, score) {
if (word.match(/(.)\1\1/)) { return score; }
return 0;
};
validation.wordSequences = function (options, word, score) {
var found = false,
j;
if (word.length > 2) {
$.each(rulesEngine.forbiddenSequences, function (idx, seq) {
var sequences = [seq, seq.split('').reverse().join('')];
$.each(sequences, function (idx, sequence) {
for (j = 0; j < (word.length - 2); j += 1) { // iterate the word trough a sliding window of size 3:
if (sequence.indexOf(word.toLowerCase().substring(j, j + 3)) > -1) {
found = true;
}
}
});
});
if (found) { return score; }
}
return 0;
};
validation.wordLowercase = function (options, word, score) {
return word.match(/[a-z]/) && score;
};
validation.wordUppercase = function (options, word, score) {
return word.match(/[A-Z]/) && score;
};
validation.wordOneNumber = function (options, word, score) {
return word.match(/\d+/) && score;
};
validation.wordThreeNumbers = function (options, word, score) {
return word.match(/(.*[0-9].*[0-9].*[0-9])/) && score;
};
validation.wordOneSpecialChar = function (options, word, score) {
return word.match(/.[!,@,#,$,%,\^,&,*,?,_,~]/) && score;
};
validation.wordTwoSpecialChar = function (options, word, score) {
return word.match(/(.*[!,@,#,$,%,\^,&,*,?,_,~].*[!,@,#,$,%,\^,&,*,?,_,~])/) && score;
};
validation.wordUpperLowerCombo = function (options, word, score) {
return word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) && score;
};
validation.wordLetterNumberCombo = function (options, word, score) {
return word.match(/([a-zA-Z])/) && word.match(/([0-9])/) && score;
};
validation.wordLetterNumberCharCombo = function (options, word, score) {
return word.match(/([a-zA-Z0-9].*[!,@,#,$,%,\^,&,*,?,_,~])|([!,@,#,$,%,\^,&,*,?,_,~].*[a-zA-Z0-9])/) && score;
};
rulesEngine.validation = validation;
rulesEngine.executeRules = function (options, word) {
var totalScore = 0;
$.each(options.rules.activated, function (rule, active) {
if (active) {
var score = options.rules.scores[rule],
funct = rulesEngine.validation[rule],
result,
errorMessage;
if (!$.isFunction(funct)) {
funct = options.rules.extra[rule];
}
if ($.isFunction(funct)) {
result = funct(options, word, score);
if (result) {
totalScore += result;
}
if (result < 0 || (!$.isNumeric(result) && !result)) {
errorMessage = options.ui.spanError(options, rule);
if (errorMessage.length > 0) {
options.instances.errors.push(errorMessage);
}
}
}
}
});
return totalScore;
};
}(jQuery, rulesEngine));
try {
if (module && module.exports) {
module.exports = rulesEngine;
}
} catch (ignore) {}
// Source: src/options.js
var defaultOptions = {};
defaultOptions.common = {};
defaultOptions.common.minChar = 6;
defaultOptions.common.usernameField = "#username";
defaultOptions.common.userInputs = [
// Selectors for input fields with user input
];
defaultOptions.common.onLoad = undefined;
defaultOptions.common.onKeyUp = undefined;
defaultOptions.common.zxcvbn = false;
defaultOptions.common.debug = false;
defaultOptions.rules = {};
defaultOptions.rules.extra = {};
defaultOptions.rules.scores = {
wordNotEmail: -100,
wordLength: -50,
wordSimilarToUsername: -100,
wordSequences: -50,
wordTwoCharacterClasses: 2,
wordRepetitions: -25,
wordLowercase: 1,
wordUppercase: 3,
wordOneNumber: 3,
wordThreeNumbers: 5,
wordOneSpecialChar: 3,
wordTwoSpecialChar: 5,
wordUpperLowerCombo: 2,
wordLetterNumberCombo: 2,
wordLetterNumberCharCombo: 2
};
defaultOptions.rules.activated = {
wordNotEmail: true,
wordLength: true,
wordSimilarToUsername: true,
wordSequences: true,
wordTwoCharacterClasses: false,
wordRepetitions: false,
wordLowercase: true,
wordUppercase: true,
wordOneNumber: true,
wordThreeNumbers: true,
wordOneSpecialChar: true,
wordTwoSpecialChar: true,
wordUpperLowerCombo: true,
wordLetterNumberCombo: true,
wordLetterNumberCharCombo: true
};
defaultOptions.rules.raisePower = 1.4;
defaultOptions.ui = {};
defaultOptions.ui.bootstrap2 = false;
defaultOptions.ui.showProgressBar = true;
defaultOptions.ui.showPopover = false;
defaultOptions.ui.showStatus = false;
defaultOptions.ui.spanError = function (options, key) {
"use strict";
var text = options.ui.errorMessages[key];
if (!text) { return ''; }
return '<span style="color: #d52929">' + text + '</span>';
};
defaultOptions.ui.errorMessages = {
wordLength: "Your password is too short",
wordNotEmail: "Do not use your email as your password",
wordSimilarToUsername: "Your password cannot contain your username",
wordTwoCharacterClasses: "Use different character classes",
wordRepetitions: "Too many repetitions",
wordSequences: "Your password contains sequences"
};
defaultOptions.ui.verdicts = ["Weak", "Normal", "Medium", "Strong", "Very Strong"];
defaultOptions.ui.showVerdicts = true;
defaultOptions.ui.showVerdictsInsideProgressBar = false;
defaultOptions.ui.showErrors = false;
defaultOptions.ui.container = undefined;
defaultOptions.ui.viewports = {
progress: undefined,
verdict: undefined,
errors: undefined
};
defaultOptions.ui.scores = [14, 26, 38, 50];
// Source: src/ui.js
var ui = {};
(function ($, ui) {
"use strict";
var barClasses = ["danger", "warning", "success"],
statusClasses = ["error", "warning", "success"];
ui.getContainer = function (options, $el) {
var $container;
$container = $(options.ui.container);
if (!($container && $container.length === 1)) {
$container = $el.parent();
}
return $container;
};
ui.findElement = function ($container, viewport, cssSelector) {
if (viewport) {
return $container.find(viewport).find(cssSelector);
}
return $container.find(cssSelector);
};
ui.getUIElements = function (options, $el) {
var $container, result;
if (options.instances.viewports) {
return options.instances.viewports;
}
$container = ui.getContainer(options, $el);
result = {};
result.$progressbar = ui.findElement($container, options.ui.viewports.progress, "div.progress");
if (options.ui.showVerdictsInsideProgressBar) {
result.$verdict = result.$progressbar.find("span.password-verdict");
}
if (!options.ui.showPopover) {
if (!options.ui.showVerdictsInsideProgressBar) {
result.$verdict = ui.findElement($container, options.ui.viewports.verdict, "span.password-verdict");
}
result.$errors = ui.findElement($container, options.ui.viewports.errors, "ul.error-list");
}
options.instances.viewports = result;
return result;
};
ui.initProgressBar = function (options, $el) {
var $container = ui.getContainer(options, $el),
progressbar = "<div class='progress'><div class='";
if (!options.ui.bootstrap2) {
progressbar += "progress-";
}
progressbar += "bar'>";
if (options.ui.showVerdictsInsideProgressBar) {
progressbar += "<span class='password-verdict'></span>";
}
progressbar += "</div></div>";
if (options.ui.viewports.progress) {
$container.find(options.ui.viewports.progress).append(progressbar);
} else {
$(progressbar).insertAfter($el);
}
};
ui.initHelper = function (options, $el, html, viewport) {
var $container = ui.getContainer(options, $el);
if (viewport) {
$container.find(viewport).append(html);
} else {
$(html).insertAfter($el);
}
};
ui.initVerdict = function (options, $el) {
ui.initHelper(options, $el, "<span class='password-verdict'></span>",
options.ui.viewports.verdict);
};
ui.initErrorList = function (options, $el) {
ui.initHelper(options, $el, "<ul class='error-list'></ul>",
options.ui.viewports.errors);
};
ui.initPopover = function (options, $el) {
$el.popover("destroy");
$el.popover({
html: true,
placement: "top",
trigger: "manual",
content: " "
});
};
ui.initUI = function (options, $el) {
if (options.ui.showPopover) {
ui.initPopover(options, $el);
} else {
if (options.ui.showErrors) { ui.initErrorList(options, $el); }
if (options.ui.showVerdicts && !options.ui.showVerdictsInsideProgressBar) {
ui.initVerdict(options, $el);
}
}
if (options.ui.showProgressBar) {
ui.initProgressBar(options, $el);
}
};
ui.possibleProgressBarClasses = ["danger", "warning", "success"];
ui.updateProgressBar = function (options, $el, cssClass, percentage) {
var $progressbar = ui.getUIElements(options, $el).$progressbar,
$bar = $progressbar.find(".progress-bar"),
cssPrefix = "progress-";
if (options.ui.bootstrap2) {
$bar = $progressbar.find(".bar");
cssPrefix = "";
}
$.each(ui.possibleProgressBarClasses, function (idx, value) {
$bar.removeClass(cssPrefix + "bar-" + value);
});
$bar.addClass(cssPrefix + "bar-" + barClasses[cssClass]);
$bar.css("width", percentage + '%');
};
ui.updateVerdict = function (options, $el, text) {
var $verdict = ui.getUIElements(options, $el).$verdict;
$verdict.text(text);
};
ui.updateErrors = function (options, $el) {
var $errors = ui.getUIElements(options, $el).$errors,
html = "";
$.each(options.instances.errors, function (idx, err) {
html += "<li>" + err + "</li>";
});
$errors.html(html);
};
ui.updatePopover = function (options, $el, verdictText) {
var popover = $el.data("bs.popover"),
html = "",
hide = true;
if (options.ui.showVerdicts &&
!options.ui.showVerdictsInsideProgressBar &&
verdictText.length > 0) {
html = "<h5><span class='password-verdict'>" + verdictText +
"</span></h5>";
hide = false;
}
if (options.ui.showErrors) {
html += "<div><ul class='error-list' style='margin-bottom: 0; margin-left: -20px'>";
$.each(options.instances.errors, function (idx, err) {
html += "<li>" + err + "</li>";
hide = false;
});
html += "</ul></div>";
}
if (hide) {
$el.popover("hide");
return;
}
if (options.ui.bootstrap2) { popover = $el.data("popover"); }
if (popover.$arrow && popover.$arrow.parents("body").length > 0) {
$el.find("+ .popover .popover-content").html(html);
} else {
// It's hidden
popover.options.content = html;
$el.popover("show");
}
};
ui.updateFieldStatus = function (options, $el, cssClass) {
var targetClass = options.ui.bootstrap2 ? ".control-group" : ".form-group",
$container = $el.parents(targetClass).first();
$.each(statusClasses, function (idx, css) {
if (!options.ui.bootstrap2) { css = "has-" + css; }
$container.removeClass(css);
});
cssClass = statusClasses[cssClass];
if (!options.ui.bootstrap2) { cssClass = "has-" + cssClass; }
$container.addClass(cssClass);
};
ui.percentage = function (score, maximun) {
var result = Math.floor(100 * score / maximun);
result = result < 0 ? 0 : result;
result = result > 100 ? 100 : result;
return result;
};
ui.getVerdictAndCssClass = function (options, score) {
var cssClass, verdictText, level;
if (score <= 0) {
cssClass = 0;
level = -1;
verdictText = options.ui.verdicts[0];
} else if (score < options.ui.scores[0]) {
cssClass = 0;
level = 0;
verdictText = options.ui.verdicts[0];
} else if (score < options.ui.scores[1]) {
cssClass = 0;
level = 1;
verdictText = options.ui.verdicts[1];
} else if (score < options.ui.scores[2]) {
cssClass = 1;
level = 2;
verdictText = options.ui.verdicts[2];
} else if (score < options.ui.scores[3]) {
cssClass = 1;
level = 3;
verdictText = options.ui.verdicts[3];
} else {
cssClass = 2;
level = 4;
verdictText = options.ui.verdicts[4];
}
return [verdictText, cssClass, level];
};
ui.updateUI = function (options, $el, score) {
var cssClass, barPercentage, verdictText;
cssClass = ui.getVerdictAndCssClass(options, score);
verdictText = cssClass[0];
cssClass = cssClass[1];
if (options.ui.showProgressBar) {
barPercentage = ui.percentage(score, options.ui.scores[3]);
ui.updateProgressBar(options, $el, cssClass, barPercentage);
if (options.ui.showVerdictsInsideProgressBar) {
ui.updateVerdict(options, $el, verdictText);
}
}
if (options.ui.showStatus) {
ui.updateFieldStatus(options, $el, cssClass);
}
if (options.ui.showPopover) {
ui.updatePopover(options, $el, verdictText);
} else {
if (options.ui.showVerdicts && !options.ui.showVerdictsInsideProgressBar) {
ui.updateVerdict(options, $el, verdictText);
}
if (options.ui.showErrors) {
ui.updateErrors(options, $el);
}
}
};
}(jQuery, ui));
// Source: src/methods.js
var methods = {};
(function ($, methods) {
"use strict";
var onKeyUp, applyToAll;
onKeyUp = function (event) {
var $el = $(event.target),
options = $el.data("pwstrength-bootstrap"),
word = $el.val(),
userInputs,
verdictText,
verdictLevel,
score;
if (options === undefined) { return; }
options.instances.errors = [];
if (options.common.zxcvbn) {
userInputs = [];
$.each(options.common.userInputs, function (idx, selector) {
userInputs.push($(selector).val());
});
userInputs.push($(options.common.usernameField).val());
score = zxcvbn(word, userInputs).entropy;
} else {
score = rulesEngine.executeRules(options, word);
}
ui.updateUI(options, $el, score);
verdictText = ui.getVerdictAndCssClass(options, score);
verdictLevel = verdictText[2];
verdictText = verdictText[0];
if (options.common.debug) { console.log(score + ' - ' + verdictText); }
if ($.isFunction(options.common.onKeyUp)) {
options.common.onKeyUp(event, {
score: score,
verdictText: verdictText,
verdictLevel: verdictLevel
});
}
};
methods.init = function (settings) {
this.each(function (idx, el) {
// Make it deep extend (first param) so it extends too the
// rules and other inside objects
var clonedDefaults = $.extend(true, {}, defaultOptions),
localOptions = $.extend(true, clonedDefaults, settings),
$el = $(el);
localOptions.instances = {};
$el.data("pwstrength-bootstrap", localOptions);
$el.on("keyup", onKeyUp);
$el.on("change", onKeyUp);
$el.on("onpaste", onKeyUp);
ui.initUI(localOptions, $el);
if ($.trim($el.val())) { // Not empty, calculate the strength
$el.trigger("keyup");
}
if ($.isFunction(localOptions.common.onLoad)) {
localOptions.common.onLoad();
}
});
return this;
};
methods.destroy = function () {
this.each(function (idx, el) {
var $el = $(el),
options = $el.data("pwstrength-bootstrap"),
elements = ui.getUIElements(options, $el);
elements.$progressbar.remove();
elements.$verdict.remove();
elements.$errors.remove();
$el.removeData("pwstrength-bootstrap");
});
};
methods.forceUpdate = function () {
this.each(function (idx, el) {
var event = { target: el };
onKeyUp(event);
});
};
methods.addRule = function (name, method, score, active) {
this.each(function (idx, el) {
var options = $(el).data("pwstrength-bootstrap");
options.rules.activated[name] = active;
options.rules.scores[name] = score;
options.rules.extra[name] = method;
});
};
applyToAll = function (rule, prop, value) {
this.each(function (idx, el) {
$(el).data("pwstrength-bootstrap").rules[prop][rule] = value;
});
};
methods.changeScore = function (rule, score) {
applyToAll.call(this, rule, "scores", score);
};
methods.ruleActive = function (rule, active) {
applyToAll.call(this, rule, "activated", active);
};
$.fn.pwstrength = function (method) {
var result;
if (methods[method]) {
result = methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === "object" || !method) {
result = methods.init.apply(this, arguments);
} else {
$.error("Method " + method + " does not exist on jQuery.pwstrength-bootstrap");
}
return result;
};
}(jQuery, methods));
}(jQuery));
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment