summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitriy Zaporozhets <dzaporozhets@gitlab.com>2015-01-15 22:16:50 +0000
committerDmitriy Zaporozhets <dzaporozhets@gitlab.com>2015-01-15 22:16:50 +0000
commit69852abdbad81f39e7913e3ef9d5fdab62721f2e (patch)
treeadc2706225e3c0325853dd927af08bb2c688fc09
parent46139825d10f3b89d27439a59a6e0e9b34cfaa95 (diff)
parent23255bff9913fc1a58c1cf629a2a030c1cbf8a87 (diff)
downloadgitlab-ce-69852abdbad81f39e7913e3ef9d5fdab62721f2e.tar.gz
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
-rw-r--r--CHANGELOG4
-rw-r--r--app/assets/javascripts/application.js.coffee11
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee4
-rw-r--r--app/assets/javascripts/dropzone_input.js.coffee242
-rw-r--r--app/assets/javascripts/markdown_area.js.coffee241
-rw-r--r--app/assets/javascripts/notes.js.coffee20
-rw-r--r--app/assets/javascripts/password_strength.js.coffee31
-rw-r--r--app/assets/javascripts/zen_mode.js.coffee12
-rw-r--r--app/assets/stylesheets/generic/common.scss4
-rw-r--r--app/assets/stylesheets/generic/forms.scss133
-rw-r--r--app/assets/stylesheets/generic/zen.scss98
-rw-r--r--app/assets/stylesheets/main/layout.scss7
-rw-r--r--app/assets/stylesheets/main/mixins.scss4
-rw-r--r--app/assets/stylesheets/sections/header.scss2
-rw-r--r--app/assets/stylesheets/sections/markdown_area.scss9
-rw-r--r--app/assets/stylesheets/sections/nav_sidebar.scss (renamed from app/assets/stylesheets/sections/sidebar.scss)2
-rw-r--r--app/assets/stylesheets/sections/note_form.scss171
-rw-r--r--app/assets/stylesheets/sections/notes.scss166
-rw-r--r--app/assets/stylesheets/sections/profile.scss17
-rw-r--r--app/views/devise/passwords/edit.html.haml4
-rw-r--r--app/views/devise/registrations/new.html.haml4
-rw-r--r--app/views/layouts/_broadcast.html.haml4
-rw-r--r--app/views/layouts/_head_panel.html.haml2
-rw-r--r--app/views/layouts/_public_head_panel.html.haml2
-rw-r--r--app/views/layouts/notify.html.haml4
-rw-r--r--app/views/profiles/passwords/edit.html.haml2
-rw-r--r--app/views/profiles/passwords/new.html.haml2
-rw-r--r--app/views/projects/_zen.html.haml9
-rw-r--r--app/views/projects/notes/_edit_form.html.haml22
-rw-r--r--app/views/projects/notes/_form.html.haml7
-rw-r--r--app/views/projects/notes/_note.html.haml22
-rw-r--r--docker/Dockerfile2
-rw-r--r--features/profile/profile.feature19
-rw-r--r--features/steps/profile/profile.rb42
-rw-r--r--lib/gitlab/git.rb4
-rw-r--r--lib/gitlab/push_data_builder.rb121
-rw-r--r--lib/tasks/gitlab/mail_google_schema_whitelisting.rake2
-rw-r--r--spec/features/notes_on_merge_requests_spec.rb28
-rw-r--r--spec/features/users_spec.rb2
-rw-r--r--spec/lib/gitlab/push_data_builder_spec.rb5
-rw-r--r--vendor/assets/javascripts/pwstrength-bootstrap-1.2.2.js659
41 files changed, 710 insertions, 1436 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 7bb3c796b54..e34b2546f54 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -24,6 +24,10 @@ v 7.7.0
- Trigger GitLab CI when push tags
- When accept merge request - do merge using sidaekiq job
- 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
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index 4cda8b75d8e..747035a9923 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -18,7 +18,6 @@
#= require jquery.turbolinks
#= require turbolinks
#= require bootstrap
-#= require password_strength
#= require select2
#= require raphael
#= require g.raphael-min
@@ -109,9 +108,19 @@ window.unbindEvents = ->
$(document).unbind('scroll')
$(document).off('scroll')
+window.shiftWindow = ->
+ scrollBy 0, -50
+
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
$(".one_click_select").on 'click', -> $(@).select()
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index e8b71a71945..e5349d80e94 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -33,17 +33,20 @@ class Dispatcher
GitLab.GfmAutoComplete.setup()
shortcut_handler = new ShortcutsNavigation()
new ZenMode()
+ new DropzoneInput($('.issue-form'))
when 'projects:merge_requests:new', 'projects:merge_requests:edit'
GitLab.GfmAutoComplete.setup()
new Diff()
shortcut_handler = new ShortcutsNavigation()
new ZenMode()
+ new DropzoneInput($('.merge-request-form'))
when 'projects:merge_requests:show'
new Diff()
shortcut_handler = new ShortcutsIssueable()
new ZenMode()
when "projects:merge_requests:diffs"
new Diff()
+ new ZenMode()
when 'projects:merge_requests:index'
shortcut_handler = new ShortcutsNavigation()
when 'dashboard:show'
@@ -108,6 +111,7 @@ class Dispatcher
new Wikis()
shortcut_handler = new ShortcutsNavigation()
new ZenMode()
+ new DropzoneInput($('.wiki-form'))
when 'snippets', 'labels', 'graphs'
shortcut_handler = new ShortcutsNavigation()
when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches'
diff --git a/app/assets/javascripts/dropzone_input.js.coffee b/app/assets/javascripts/dropzone_input.js.coffee
new file mode 100644
index 00000000000..a0f0d98a8dc
--- /dev/null
+++ b/app/assets/javascripts/dropzone_input.js.coffee
@@ -0,0 +1,242 @@
+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 + ")"
diff --git a/app/assets/javascripts/markdown_area.js.coffee b/app/assets/javascripts/markdown_area.js.coffee
deleted file mode 100644
index 0ca7070dc8b..00000000000
--- a/app/assets/javascripts/markdown_area.js.coffee
+++ /dev/null
@@ -1,241 +0,0 @@
-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
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index 4d1c81d91d4..d1935d1d007 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -219,6 +219,7 @@ class @Notes
setupNoteForm: (form) ->
disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
form.removeClass "js-new-note-form"
+ form.find('.div-dropzone').remove()
# setup preview buttons
form.find(".js-md-write-button, .js-md-preview-button").tooltip placement: "left"
@@ -233,6 +234,7 @@ class @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"
GitLab.GfmAutoComplete.setup()
+ new DropzoneInput(form)
form.show()
@@ -259,8 +261,10 @@ class @Notes
Updates the current note field.
###
updateNote: (xhr, note, status) =>
- note_li = $("#note_" + note.id)
+ note_li = $(".note-row-" + note.id)
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).each (i, e) ->
hljs.highlightBlock(e)
@@ -276,11 +280,19 @@ class @Notes
e.preventDefault()
note = $(this).closest(".note")
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
note.find(".js-note-attachment-delete").show()
+
+ # Setup markdown form
GitLab.GfmAutoComplete.setup()
- form = note.find(".note-edit-form")
+ new DropzoneInput(form)
+
form.show()
textarea = form.find("textarea")
textarea.focus()
@@ -295,8 +307,8 @@ class @Notes
e.preventDefault()
note = $(this).closest(".note")
note.find(".note-text").show()
- note.find(".js-note-attachment-delete").hide()
- note.find(".note-edit-form").hide()
+ note.find(".note-header").show()
+ note.find(".current-note-edit-form").remove()
###
Called in response to deleting a note of any kind.
diff --git a/app/assets/javascripts/password_strength.js.coffee b/app/assets/javascripts/password_strength.js.coffee
deleted file mode 100644
index 825f5630266..00000000000
--- a/app/assets/javascripts/password_strength.js.coffee
+++ /dev/null
@@ -1,31 +0,0 @@
-#= 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
diff --git a/app/assets/javascripts/zen_mode.js.coffee b/app/assets/javascripts/zen_mode.js.coffee
index 0c9942a4014..0fb8f7ed75f 100644
--- a/app/assets/javascripts/zen_mode.js.coffee
+++ b/app/assets/javascripts/zen_mode.js.coffee
@@ -10,7 +10,15 @@ class @ZenMode
if not @active_checkbox
@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
if checkbox.checked
# Disable other keyboard shortcuts in ZEN mode
@@ -32,8 +40,6 @@ class @ZenMode
@active_zen_area = @active_checkbox.parent().find('textarea')
@active_zen_area.focus()
window.location.hash = ZenMode.fullscreen_prefix + @active_checkbox.prop('id')
- # Disable dropzone in ZEN mode
- Dropzone.forElement('.div-dropzone').disable()
exitZenMode: =>
if @active_zen_area isnt null
diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss
index da708c96b09..cd6352db85f 100644
--- a/app/assets/stylesheets/generic/common.scss
+++ b/app/assets/stylesheets/generic/common.scss
@@ -273,10 +273,6 @@ img.emoji {
height: 220px;
}
-.navless-container {
- margin-top: 68px;
-}
-
.description-block {
@extend .light-well;
@extend .light;
diff --git a/app/assets/stylesheets/generic/forms.scss b/app/assets/stylesheets/generic/forms.scss
index 1a832569953..c8982cdc00d 100644
--- a/app/assets/stylesheets/generic/forms.scss
+++ b/app/assets/stylesheets/generic/forms.scss
@@ -97,136 +97,3 @@ label {
.wiki-content {
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;
- }
-}
diff --git a/app/assets/stylesheets/generic/zen.scss b/app/assets/stylesheets/generic/zen.scss
new file mode 100644
index 00000000000..26afc21a6ab
--- /dev/null
+++ b/app/assets/stylesheets/generic/zen.scss
@@ -0,0 +1,98 @@
+.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;
+ }
+}
diff --git a/app/assets/stylesheets/main/layout.scss b/app/assets/stylesheets/main/layout.scss
index 71522443f10..1085e68b7d4 100644
--- a/app/assets/stylesheets/main/layout.scss
+++ b/app/assets/stylesheets/main/layout.scss
@@ -2,6 +2,10 @@ html {
overflow-y: scroll;
&.touch .tooltip { display: none !important; }
+
+ body {
+ padding-top: 47px;
+ }
}
.container {
@@ -13,3 +17,6 @@ html {
margin: 0 0;
}
+.navless-container {
+ margin-top: 30px;
+}
diff --git a/app/assets/stylesheets/main/mixins.scss b/app/assets/stylesheets/main/mixins.scss
index 5f83913b73b..ebf68850f98 100644
--- a/app/assets/stylesheets/main/mixins.scss
+++ b/app/assets/stylesheets/main/mixins.scss
@@ -65,10 +65,6 @@
max-width: 100%;
}
- *:first-child {
- margin-top: 0;
- }
-
code { padding: 0 4px; }
h1 {
diff --git a/app/assets/stylesheets/sections/header.scss b/app/assets/stylesheets/sections/header.scss
index 32b0b10c649..a5098b6da5b 100644
--- a/app/assets/stylesheets/sections/header.scss
+++ b/app/assets/stylesheets/sections/header.scss
@@ -8,8 +8,6 @@ header {
margin-bottom: 0;
min-height: 40px;
border: none;
- position: fixed;
- top: 0;
width: 100%;
.navbar-inner {
diff --git a/app/assets/stylesheets/sections/markdown_area.scss b/app/assets/stylesheets/sections/markdown_area.scss
new file mode 100644
index 00000000000..8ee8eaa4ee7
--- /dev/null
+++ b/app/assets/stylesheets/sections/markdown_area.scss
@@ -0,0 +1,9 @@
+.markdown-area {
+ background: #FFF;
+ border: 1px solid #ddd;
+ min-height: 100px;
+ padding: 5px;
+ font-size: 14px;
+ box-shadow: none;
+ width: 100%;
+}
diff --git a/app/assets/stylesheets/sections/sidebar.scss b/app/assets/stylesheets/sections/nav_sidebar.scss
index 79441eba6db..edb5f90813f 100644
--- a/app/assets/stylesheets/sections/sidebar.scss
+++ b/app/assets/stylesheets/sections/nav_sidebar.scss
@@ -12,7 +12,6 @@
width: 100%;
padding: 15px;
background: #FFF;
- margin-top: 48px;
}
.nav-sidebar {
@@ -159,3 +158,4 @@
@include expanded-sidebar;
}
+
diff --git a/app/assets/stylesheets/sections/note_form.scss b/app/assets/stylesheets/sections/note_form.scss
new file mode 100644
index 00000000000..26511d799f3
--- /dev/null
+++ b/app/assets/stylesheets/sections/note_form.scss
@@ -0,0 +1,171 @@
+/**
+ * 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;
+}
diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss
index 1550e30fe53..117e5e7f977 100644
--- a/app/assets/stylesheets/sections/notes.scss
+++ b/app/assets/stylesheets/sections/notes.scss
@@ -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;
-}
diff --git a/app/assets/stylesheets/sections/profile.scss b/app/assets/stylesheets/sections/profile.scss
index b9f4e317e9c..086875582f3 100644
--- a/app/assets/stylesheets/sections/profile.scss
+++ b/app/assets/stylesheets/sections/profile.scss
@@ -111,20 +111,3 @@
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;
-}
diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml
index f6cbf9b82ba..1326cc0aac9 100644
--- a/app/views/devise/passwords/edit.html.haml
+++ b/app/views/devise/passwords/edit.html.haml
@@ -6,8 +6,8 @@
.devise-errors
= devise_error_messages!
= f.hidden_field :reset_password_token
- .form-group#password-strength
- = f.password_field :password, class: "form-control top", id: "user_password_recover", placeholder: "New password", required: true
+ %div
+ = f.password_field :password, class: "form-control top", placeholder: "New password", required: true
%div
= f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm new password", required: true
.clearfix.append-bottom-10
diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml
index 123de881f59..d6a952f3dc5 100644
--- a/app/views/devise/registrations/new.html.haml
+++ b/app/views/devise/registrations/new.html.haml
@@ -11,8 +11,8 @@
= f.text_field :username, class: "form-control middle", placeholder: "Username", required: true
%div
= f.email_field :email, class: "form-control middle", placeholder: "Email", required: true
- .form-group#password-strength
- = f.password_field :password, class: "form-control middle", id: "user_password_sign_up", placeholder: "Password", required: true
+ %div
+ = f.password_field :password, class: "form-control middle", placeholder: "Password", required: true
%div
= f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm password", required: true
%div
diff --git a/app/views/layouts/_broadcast.html.haml b/app/views/layouts/_broadcast.html.haml
index e589e34dd23..e7d477c225e 100644
--- a/app/views/layouts/_broadcast.html.haml
+++ b/app/views/layouts/_broadcast.html.haml
@@ -2,7 +2,3 @@
.broadcast-message{ style: broadcast_styling(broadcast_message) }
%i.fa.fa-bullhorn
= broadcast_message.message
- :css
- .sidebar-wrapper .nav-sidebar {
- margin-top: 58px;
- }
diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml
index e98b8ec631d..bdf27562c26 100644
--- a/app/views/layouts/_head_panel.html.haml
+++ b/app/views/layouts/_head_panel.html.haml
@@ -1,4 +1,4 @@
-%header.navbar.navbar-static-top.navbar-gitlab
+%header.navbar.navbar-fixed-top.navbar-gitlab
.navbar-inner
.container
%div.app_logo
diff --git a/app/views/layouts/_public_head_panel.html.haml b/app/views/layouts/_public_head_panel.html.haml
index 1d5bbb2aade..e912fea2aee 100644
--- a/app/views/layouts/_public_head_panel.html.haml
+++ b/app/views/layouts/_public_head_panel.html.haml
@@ -1,4 +1,4 @@
-%header.navbar.navbar-static-top.navbar-gitlab
+%header.navbar.navbar-fixed-top.navbar-gitlab
.navbar-inner
.container
%div.app_logo
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index da451961327..e81cf9e5bf6 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -24,8 +24,8 @@
%p
\—
%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
#{link_to "View it on GitLab", @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.
diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml
index 425200ff523..2a7d317aa3e 100644
--- a/app/views/profiles/passwords/edit.html.haml
+++ b/app/views/profiles/passwords/edit.html.haml
@@ -24,7 +24,7 @@
.form-group
= f.label :password, 'New password', class: 'control-label'
.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
= f.label :password_confirmation, class: 'control-label'
.col-sm-10
diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml
index 42d2d0db29c..aef7348fd20 100644
--- a/app/views/profiles/passwords/new.html.haml
+++ b/app/views/profiles/passwords/new.html.haml
@@ -16,7 +16,7 @@
.col-sm-10= f.password_field :current_password, required: true, class: 'form-control'
.form-group
= 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
= f.label :password_confirmation, class: 'control-label'
.col-sm-10
diff --git a/app/views/projects/_zen.html.haml b/app/views/projects/_zen.html.haml
index 2bbc49e8eb5..cf1c55ecca6 100644
--- a/app/views/projects/_zen.html.haml
+++ b/app/views/projects/_zen.html.haml
@@ -1,7 +1,10 @@
.zennable
- %input#zen-toggle-comment{ tabindex: '-1', type: 'checkbox' }
+ %input#zen-toggle-comment.zen-toggle-comment{ tabindex: '-1', type: 'checkbox' }
.zen-backdrop
- classes << ' js-gfm-input markdown-area'
= f.text_area attr, class: classes, placeholder: 'Leave a comment'
- %label{ for: 'zen-toggle-comment', class: 'expand' } Edit in fullscreen
- %label{ for: 'zen-toggle-comment', class: 'collapse' }
+ = link_to nil, class: 'zen-enter-link', tabindex: '-1' do
+ %i.fa.fa-expand
+ Edit in fullscreen
+ = link_to nil, class: 'zen-leave-link' do
+ %i.fa.fa-compress
diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml
new file mode 100644
index 00000000000..59e2b3f1b0b
--- /dev/null
+++ b/app/views/projects/notes/_edit_form.html.haml
@@ -0,0 +1,22 @@
+.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"
diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml
index 47ffe1fd2f3..3879a0f10da 100644
--- a/app/views/projects/notes/_form.html.haml
+++ b/app/views/projects/notes/_form.html.haml
@@ -7,8 +7,9 @@
= render layout: 'projects/md_preview' do
= render 'projects/zen', f: f, attr: :note,
- classes: 'note_text js-note-text'
- .light.clearfix
+ 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 }.
@@ -24,7 +25,7 @@
%i.fa.fa-paperclip
%span Choose File ...
&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"
:javascript
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index 80e7342455b..691c169b620 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -1,4 +1,4 @@
-%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-icon
- if note.system
@@ -42,25 +42,7 @@
.note-text
= preserve do
= markdown(note.note, {no_header_anchors: true})
-
- .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"
-
+ = render 'projects/notes/edit_form', note: note
- if note.attachment.url
.note-attachment
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 5d0880b8c88..445fdd6d063 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -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 you run GitLab Enterprise Edition point it to a location where you have downloaded it.
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 \
&& rm -f $TMP_FILE
diff --git a/features/profile/profile.feature b/features/profile/profile.feature
index fd132e1cd80..d586167cdf5 100644
--- a/features/profile/profile.feature
+++ b/features/profile/profile.feature
@@ -97,22 +97,3 @@ Feature: Profile
Given I visit profile design page
When I change my code preview theme
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
diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb
index 29fc7e68dac..a907b0b7dcf 100644
--- a/features/steps/profile/profile.rb
+++ b/features/steps/profile/profile.rb
@@ -58,34 +58,16 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
step 'I try change my password w/o old one' 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"
click_button "Save"
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
within '.update-password' do
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"
click_button "Save"
end
@@ -94,7 +76,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
step 'I unsuccessfully change my password' do
within '.update-password' do
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"
click_button "Save"
end
@@ -104,22 +86,6 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
page.should have_content "You must provide a valid current password"
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
page.should have_content "Password confirmation doesn't match"
end
@@ -180,7 +146,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
step 'I submit new password' do
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'
click_button "Set new password"
end
diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb
index 67aca5e36e9..4a712c6345f 100644
--- a/lib/gitlab/git.rb
+++ b/lib/gitlab/git.rb
@@ -1,5 +1,9 @@
module Gitlab
module Git
BLANK_SHA = '0' * 40
+
+ def self.extract_ref_name(ref)
+ ref.gsub(/\Arefs\/(tags|heads)\//, '')
+ end
end
end
diff --git a/lib/gitlab/push_data_builder.rb b/lib/gitlab/push_data_builder.rb
index 72c42a6a254..faea6ae375c 100644
--- a/lib/gitlab/push_data_builder.rb
+++ b/lib/gitlab/push_data_builder.rb
@@ -1,63 +1,80 @@
module Gitlab
class PushDataBuilder
- # Produce a hash of post-receive data
- #
- # data = {
- # before: String,
- # after: String,
- # ref: String,
- # user_id: String,
- # user_name: String,
- # project_id: String,
- # repository: {
- # name: String,
- # url: String,
- # description: String,
- # homepage: String,
- # },
- # commits: Array,
- # total_commits_count: Fixnum
- # }
- #
- def self.build(project, user, oldrev, newrev, ref, commits = [])
- # Total commits count
- commits_count = commits.size
+ class << self
+ # Produce a hash of post-receive data
+ #
+ # data = {
+ # before: String,
+ # after: String,
+ # ref: String,
+ # user_id: String,
+ # user_name: String,
+ # project_id: String,
+ # repository: {
+ # name: String,
+ # url: String,
+ # description: String,
+ # homepage: String,
+ # },
+ # commits: Array,
+ # total_commits_count: Fixnum
+ # }
+ #
+ def build(project, user, oldrev, newrev, ref, commits = [])
+ # Total commits count
+ commits_count = commits.size
- # Get latest 20 commits ASC
- commits_limited = commits.last(20)
+ # Get latest 20 commits ASC
+ commits_limited = commits.last(20)
- # Hash to be passed as post_receive_data
- data = {
- before: oldrev,
- after: newrev,
- ref: ref,
- user_id: user.id,
- user_name: user.name,
- project_id: project.id,
- repository: {
- name: project.name,
- url: project.url_to_repo,
- description: project.description,
- homepage: project.web_url,
- },
- commits: [],
- total_commits_count: commits_count
- }
+ # Hash to be passed as post_receive_data
+ data = {
+ before: oldrev,
+ after: newrev,
+ ref: ref,
+ checkout_sha: checkout_sha(project.repository, newrev, ref),
+ user_id: user.id,
+ user_name: user.name,
+ project_id: project.id,
+ repository: {
+ name: project.name,
+ url: project.url_to_repo,
+ description: project.description,
+ homepage: project.web_url,
+ },
+ commits: [],
+ total_commits_count: commits_count
+ }
- # For performance purposes maximum 20 latest commits
- # will be passed as post receive hook data.
- commits_limited.each do |commit|
- data[:commits] << commit.hook_attrs(project)
+ # For performance purposes maximum 20 latest commits
+ # will be passed as post receive hook data.
+ commits_limited.each do |commit|
+ data[:commits] << commit.hook_attrs(project)
+ end
+
+ data
end
- data
- end
+ # This method provide a sample data generated with
+ # 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
- # existing project and commits to test web hooks
- def self.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)
+ def checkout_sha(repository, newrev, ref)
+ if newrev != Gitlab::Git::BLANK_SHA && ref.start_with?('refs/tags/')
+ tag_name = Gitlab::Git.extract_ref_name(ref)
+ tag = repository.find_tag(tag_name)
+
+ if tag
+ commit = repository.commit(tag.target)
+ commit.try(:sha)
+ end
+ else
+ newrev
+ end
+ end
end
end
end
diff --git a/lib/tasks/gitlab/mail_google_schema_whitelisting.rake b/lib/tasks/gitlab/mail_google_schema_whitelisting.rake
index f40bba24da8..102c6ae55d5 100644
--- a/lib/tasks/gitlab/mail_google_schema_whitelisting.rake
+++ b/lib/tasks/gitlab/mail_google_schema_whitelisting.rake
@@ -54,8 +54,8 @@ namespace :gitlab do
<div class='footer' style='margin-top: 10px;'>
<p>
<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>
+ You're receiving this notification because you are a member of the Base / Rails Project project team.
#{schema}
</p>
</div>
diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb
index cac409b9139..895a11270bc 100644
--- a/spec/features/notes_on_merge_requests_spec.rb
+++ b/spec/features/notes_on_merge_requests_spec.rb
@@ -72,27 +72,23 @@ describe 'Comments' do
it "should show the note edit form and hide the note body" 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-text", visible: false).should_not be_visible
+ find(:css, ".note-text", visible: false).should_not be_visible
end
end
- it "should reset the edit note form textarea with the original content of the note if cancelled" do
- find('.note').hover
- find(".js-note-edit").click
-
- within(".note-edit-form") do
- fill_in "note[note]", with: "Some new content"
- find(".btn-cancel").click
- find(".js-note-text", visible: false).text.should == note.note
- end
- end
+ # TODO: fix after 7.7 release
+ #it "should reset the edit note form textarea with the original content of the note if cancelled" do
+ #within(".current-note-edit-form") do
+ #fill_in "note[note]", with: "Some new content"
+ #find(".btn-cancel").click
+ #find(".js-note-text", visible: false).text.should == note.note
+ #end
+ #end
it "appends the edited at time to the note" do
- find('.note').hover
- find(".js-note-edit").click
-
- within(".note-edit-form") do
+ within(".current-note-edit-form") do
fill_in "note[note]", with: "Some new content"
find(".btn-save").click
end
@@ -119,7 +115,7 @@ describe 'Comments' do
it "removes the attachment div and resets the edit form" do
find(".js-note-attachment-delete").click
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
diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb
index e2b631001c9..8b237199bcc 100644
--- a/spec/features/users_spec.rb
+++ b/spec/features/users_spec.rb
@@ -11,7 +11,7 @@ describe 'Users', feature: true do
fill_in "user_name", with: "Name Surname"
fill_in "user_username", with: "Great"
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"
expect { click_button "Sign up" }.to change {User.count}.by(1)
end
diff --git a/spec/lib/gitlab/push_data_builder_spec.rb b/spec/lib/gitlab/push_data_builder_spec.rb
index fbf767a167f..691fd133637 100644
--- a/spec/lib/gitlab/push_data_builder_spec.rb
+++ b/spec/lib/gitlab/push_data_builder_spec.rb
@@ -21,13 +21,14 @@ describe 'Gitlab::PushDataBuilder' do
Gitlab::PushDataBuilder.build(project,
user,
Gitlab::Git::BLANK_SHA,
- '5937ac0a7beb003549fc5fd26fc247adbce4a52e',
+ '8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b',
'refs/tags/v1.1.0')
end
it { data.should be_a(Hash) }
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[:commits].should be_empty }
it { data[:total_commits_count].should be_zero }
diff --git a/vendor/assets/javascripts/pwstrength-bootstrap-1.2.2.js b/vendor/assets/javascripts/pwstrength-bootstrap-1.2.2.js
deleted file mode 100644
index ee374a07fab..00000000000
--- a/vendor/assets/javascripts/pwstrength-bootstrap-1.2.2.js
+++ /dev/null
@@ -1,659 +0,0 @@
-/*!
- * 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