diff options
author | Dmitriy Zaporozhets <dzaporozhets@gitlab.com> | 2015-01-15 22:16:50 +0000 |
---|---|---|
committer | Dmitriy Zaporozhets <dzaporozhets@gitlab.com> | 2015-01-15 22:16:50 +0000 |
commit | 69852abdbad81f39e7913e3ef9d5fdab62721f2e (patch) | |
tree | adc2706225e3c0325853dd927af08bb2c688fc09 | |
parent | 46139825d10f3b89d27439a59a6e0e9b34cfaa95 (diff) | |
parent | 23255bff9913fc1a58c1cf629a2a030c1cbf8a87 (diff) | |
download | gitlab-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
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 + ">×</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 + ">×</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 & 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 ... + + %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 & dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }. @@ -24,7 +25,7 @@ %i.fa.fa-paperclip %span Choose File ... - %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 ... - - %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 |