diff --git a/app/assets/javascripts/behaviors/autosize.js b/app/assets/javascripts/behaviors/autosize.js index a6bc262b657..7e6c44fa1cd 100644 --- a/app/assets/javascripts/behaviors/autosize.js +++ b/app/assets/javascripts/behaviors/autosize.js @@ -1,7 +1,6 @@ /* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, consistent-return, max-len */ /* global autosize */ -/*= require jquery.ba-resize */ /*= require autosize */ (function() { diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6 index 502b41c4a25..edec21e3b63 100644 --- a/app/assets/javascripts/dispatcher.js.es6 +++ b/app/assets/javascripts/dispatcher.js.es6 @@ -8,7 +8,6 @@ /* global ShortcutsIssuable */ /* global ZenMode */ /* global Milestone */ -/* global GLForm */ /* global IssuableForm */ /* global LabelsSelect */ /* global MilestoneSelect */ @@ -99,7 +98,7 @@ case 'projects:milestones:edit': new ZenMode(); new gl.DueDateSelectors(); - new GLForm($('.milestone-form')); + new gl.GLForm($('.milestone-form')); break; case 'groups:milestones:new': new ZenMode(); @@ -110,7 +109,7 @@ case 'projects:issues:new': case 'projects:issues:edit': shortcut_handler = new ShortcutsNavigation(); - new GLForm($('.issue-form')); + new gl.GLForm($('.issue-form')); new IssuableForm($('.issue-form')); new LabelsSelect(); new MilestoneSelect(); @@ -120,7 +119,7 @@ case 'projects:merge_requests:edit': new gl.Diff(); shortcut_handler = new ShortcutsNavigation(); - new GLForm($('.merge-request-form')); + new gl.GLForm($('.merge-request-form')); new IssuableForm($('.merge-request-form')); new LabelsSelect(); new MilestoneSelect(); @@ -128,11 +127,11 @@ break; case 'projects:tags:new': new ZenMode(); - new GLForm($('.tag-form')); + new gl.GLForm($('.tag-form')); break; case 'projects:releases:edit': new ZenMode(); - new GLForm($('.release-form')); + new gl.GLForm($('.release-form')); break; case 'projects:merge_requests:show': new gl.Diff(); @@ -332,7 +331,7 @@ new gl.Wikis(); shortcut_handler = new ShortcutsNavigation(); new ZenMode(); - new GLForm($('.wiki-form')); + new gl.GLForm($('.wiki-form')); break; case 'snippets': shortcut_handler = new ShortcutsNavigation(); diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js deleted file mode 100644 index 08b2494f3df..00000000000 --- a/app/assets/javascripts/gl_form.js +++ /dev/null @@ -1,62 +0,0 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-new, max-len */ -/* global GitLab */ -/* global DropzoneInput */ -/* global autosize */ - -(function() { - this.GLForm = (function() { - function GLForm(form) { - this.form = form; - this.textarea = this.form.find('textarea.js-gfm-input'); - // Before we start, we should clean up any previous data for this form - this.destroy(); - // Setup the form - this.setupForm(); - this.form.data('gl-form', this); - } - - GLForm.prototype.destroy = function() { - // Clean form listeners - this.clearEventListeners(); - return this.form.data('gl-form', null); - }; - - GLForm.prototype.setupForm = function() { - var isNewForm; - isNewForm = this.form.is(':not(.gfm-form)'); - this.form.removeClass('js-new-note-form'); - if (isNewForm) { - this.form.find('.div-dropzone').remove(); - this.form.addClass('gfm-form'); - // remove notify commit author checkbox for non-commit notes - gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button')); - gl.GfmAutoComplete.setup(this.form.find('.js-gfm-input')); - new DropzoneInput(this.form); - autosize(this.textarea); - // form and textarea event listeners - this.addEventListeners(); - } - gl.text.init(this.form); - // hide discard button - this.form.find('.js-note-discard').hide(); - return this.form.show(); - }; - - GLForm.prototype.clearEventListeners = function() { - this.textarea.off('focus'); - this.textarea.off('blur'); - return gl.text.removeListeners(this.form); - }; - - GLForm.prototype.addEventListeners = function() { - this.textarea.on('focus', function() { - return $(this).closest('.md-area').addClass('is-focused'); - }); - return this.textarea.on('blur', function() { - return $(this).closest('.md-area').removeClass('is-focused'); - }); - }; - - return GLForm; - })(); -}).call(this); diff --git a/app/assets/javascripts/gl_form.js.es6 b/app/assets/javascripts/gl_form.js.es6 new file mode 100644 index 00000000000..0b446ff364a --- /dev/null +++ b/app/assets/javascripts/gl_form.js.es6 @@ -0,0 +1,92 @@ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-new, max-len */ +/* global GitLab */ +/* global DropzoneInput */ +/* global autosize */ + +(() => { + const global = window.gl || (window.gl = {}); + + function GLForm(form) { + this.form = form; + this.textarea = this.form.find('textarea.js-gfm-input'); + // Before we start, we should clean up any previous data for this form + this.destroy(); + // Setup the form + this.setupForm(); + this.form.data('gl-form', this); + } + + GLForm.prototype.destroy = function() { + // Clean form listeners + this.clearEventListeners(); + return this.form.data('gl-form', null); + }; + + GLForm.prototype.setupForm = function() { + var isNewForm; + isNewForm = this.form.is(':not(.gfm-form)'); + this.form.removeClass('js-new-note-form'); + if (isNewForm) { + this.form.find('.div-dropzone').remove(); + this.form.addClass('gfm-form'); + // remove notify commit author checkbox for non-commit notes + gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button')); + gl.GfmAutoComplete.setup(this.form.find('.js-gfm-input')); + new DropzoneInput(this.form); + autosize(this.textarea); + // form and textarea event listeners + this.addEventListeners(); + } + gl.text.init(this.form); + // hide discard button + this.form.find('.js-note-discard').hide(); + this.form.show(); + if (this.isAutosizeable) this.setupAutosize(); + }; + + GLForm.prototype.setupAutosize = function () { + this.textarea.off('autosize:resized') + .on('autosize:resized', this.setHeightData.bind(this)); + + this.textarea.off('mouseup.autosize') + .on('mouseup.autosize', this.destroyAutosize.bind(this)); + + setTimeout(() => { + autosize(this.textarea); + this.textarea.css('resize', 'vertical'); + }, 0); + }; + + GLForm.prototype.setHeightData = function () { + this.textarea.data('height', this.textarea.outerHeight()); + }; + + GLForm.prototype.destroyAutosize = function () { + const outerHeight = this.textarea.outerHeight(); + + if (this.textarea.data('height') === outerHeight) return; + + autosize.destroy(this.textarea); + + this.textarea.data('height', outerHeight); + this.textarea.outerHeight(outerHeight); + this.textarea.css('max-height', window.outerHeight); + }; + + GLForm.prototype.clearEventListeners = function() { + this.textarea.off('focus'); + this.textarea.off('blur'); + return gl.text.removeListeners(this.form); + }; + + GLForm.prototype.addEventListeners = function() { + this.textarea.on('focus', function() { + return $(this).closest('.md-area').addClass('is-focused'); + }); + return this.textarea.on('blur', function() { + return $(this).closest('.md-area').removeClass('is-focused'); + }); + }; + + global.GLForm = GLForm; +})(); diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 9db830a7ada..c4722be3625 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -1,6 +1,5 @@ /* eslint-disable no-restricted-properties, func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape */ /* global Flash */ -/* global GLForm */ /* global Autosave */ /* global ResolveService */ /* global mrRefreshWidgetUrl */ @@ -420,7 +419,7 @@ Notes.prototype.setupNoteForm = function(form) { var textarea; - new GLForm(form); + new gl.GLForm(form); textarea = form.find(".js-note-text"); return new Autosave(textarea, ["Note", form.find("#note_noteable_type").val(), form.find("#note_noteable_id").val(), form.find("#note_commit_id").val(), form.find("#note_type").val(), form.find("#note_line_code").val(), form.find("#note_position").val()]); }; @@ -884,7 +883,7 @@ var targetId = $originalContentEl.data('target-id'); var targetType = $originalContentEl.data('target-type'); - new GLForm($editForm.find('form')); + new gl.GLForm($editForm.find('form')); $editForm.find('form') .attr('action', postUrl) diff --git a/spec/javascripts/gl_form_spec.js.es6 b/spec/javascripts/gl_form_spec.js.es6 new file mode 100644 index 00000000000..b5f99483bfb --- /dev/null +++ b/spec/javascripts/gl_form_spec.js.es6 @@ -0,0 +1,122 @@ +/* global autosize */ +/*= require gl_form */ +/*= require autosize */ +/*= require lib/utils/text_utility */ +/*= require lib/utils/common_utils */ + +describe('GLForm', () => { + const global = window.gl || (window.gl = {}); + const GLForm = global.GLForm; + + it('should be defined in the global scope', () => { + expect(GLForm).toBeDefined(); + }); + + describe('when instantiated', function () { + beforeEach((done) => { + this.form = $('
'); + this.textarea = this.form.find('textarea'); + spyOn($.prototype, 'off').and.returnValue(this.textarea); + spyOn($.prototype, 'on').and.returnValue(this.textarea); + spyOn($.prototype, 'css'); + spyOn(window, 'autosize'); + + this.glForm = new GLForm(this.form); + setTimeout(() => { + $.prototype.off.calls.reset(); + $.prototype.on.calls.reset(); + $.prototype.css.calls.reset(); + autosize.calls.reset(); + done(); + }); + }); + + describe('.setupAutosize', () => { + beforeEach((done) => { + this.glForm.setupAutosize(); + setTimeout(() => { + done(); + }); + }); + + it('should register an autosize event handler on the textarea', () => { + expect($.prototype.off).toHaveBeenCalledWith('autosize:resized'); + expect($.prototype.on).toHaveBeenCalledWith('autosize:resized', jasmine.any(Function)); + }); + + it('should register a mouseup event handler on the textarea', () => { + expect($.prototype.off).toHaveBeenCalledWith('mouseup.autosize'); + expect($.prototype.on).toHaveBeenCalledWith('mouseup.autosize', jasmine.any(Function)); + }); + + it('should autosize the textarea', () => { + expect(autosize).toHaveBeenCalledWith(jasmine.any(Object)); + }); + + it('should set the resize css property to vertical', () => { + expect($.prototype.css).toHaveBeenCalledWith('resize', 'vertical'); + }); + }); + + describe('.setHeightData', () => { + beforeEach(() => { + spyOn($.prototype, 'data'); + spyOn($.prototype, 'outerHeight').and.returnValue(200); + this.glForm.setHeightData(); + }); + + it('should set the height data attribute', () => { + expect($.prototype.data).toHaveBeenCalledWith('height', 200); + }); + + it('should call outerHeight', () => { + expect($.prototype.outerHeight).toHaveBeenCalled(); + }); + }); + + describe('.destroyAutosize', () => { + describe('when called', () => { + beforeEach(() => { + spyOn($.prototype, 'data'); + spyOn($.prototype, 'outerHeight').and.returnValue(200); + spyOn(window, 'outerHeight').and.returnValue(400); + spyOn(autosize, 'destroy'); + + this.glForm.destroyAutosize(); + }); + + it('should call outerHeight', () => { + expect($.prototype.outerHeight).toHaveBeenCalled(); + }); + + it('should get data-height attribute', () => { + expect($.prototype.data).toHaveBeenCalledWith('height'); + }); + + it('should call autosize destroy', () => { + expect(autosize.destroy).toHaveBeenCalledWith(this.textarea); + }); + + it('should set the data-height attribute', () => { + expect($.prototype.data).toHaveBeenCalledWith('height', 200); + }); + + it('should set the outerHeight', () => { + expect($.prototype.outerHeight).toHaveBeenCalledWith(200); + }); + + it('should set the css', () => { + expect($.prototype.css).toHaveBeenCalledWith('max-height', window.outerHeight); + }); + }); + + it('should return undefined if the data-height equals the outerHeight', () => { + spyOn($.prototype, 'outerHeight').and.returnValue(200); + spyOn($.prototype, 'data').and.returnValue(200); + spyOn(autosize, 'destroy'); + expect(this.glForm.destroyAutosize()).toBeUndefined(); + expect(autosize.destroy).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/vendor/assets/javascripts/jquery.ba-resize.js b/vendor/assets/javascripts/jquery.ba-resize.js deleted file mode 100644 index 1f41d379153..00000000000 --- a/vendor/assets/javascripts/jquery.ba-resize.js +++ /dev/null @@ -1,246 +0,0 @@ -/*! - * jQuery resize event - v1.1 - 3/14/2010 - * http://benalman.com/projects/jquery-resize-plugin/ - * - * Copyright (c) 2010 "Cowboy" Ben Alman - * Dual licensed under the MIT and GPL licenses. - * http://benalman.com/about/license/ - */ - -// Script: jQuery resize event -// -// *Version: 1.1, Last updated: 3/14/2010* -// -// Project Home - http://benalman.com/projects/jquery-resize-plugin/ -// GitHub - http://github.com/cowboy/jquery-resize/ -// Source - http://github.com/cowboy/jquery-resize/raw/master/jquery.ba-resize.js -// (Minified) - http://github.com/cowboy/jquery-resize/raw/master/jquery.ba-resize.min.js (1.0kb) -// -// About: License -// -// Copyright (c) 2010 "Cowboy" Ben Alman, -// Dual licensed under the MIT and GPL licenses. -// http://benalman.com/about/license/ -// -// About: Examples -// -// This working example, complete with fully commented code, illustrates a few -// ways in which this plugin can be used. -// -// resize event - http://benalman.com/code/projects/jquery-resize/examples/resize/ -// -// About: Support and Testing -// -// Information about what version or versions of jQuery this plugin has been -// tested with, what browsers it has been tested in, and where the unit tests -// reside (so you can test it yourself). -// -// jQuery Versions - 1.3.2, 1.4.1, 1.4.2 -// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.6, Safari 3-4, Chrome, Opera 9.6-10.1. -// Unit Tests - http://benalman.com/code/projects/jquery-resize/unit/ -// -// About: Release History -// -// 1.1 - (3/14/2010) Fixed a minor bug that was causing the event to trigger -// immediately after bind in some circumstances. Also changed $.fn.data -// to $.data to improve performance. -// 1.0 - (2/10/2010) Initial release - -(function($,window,undefined){ - '$:nomunge'; // Used by YUI compressor. - - // A jQuery object containing all non-window elements to which the resize - // event is bound. - var elems = $([]), - - // Extend $.resize if it already exists, otherwise create it. - jq_resize = $.resize = $.extend( $.resize, {} ), - - timeout_id, - - // Reused strings. - str_setTimeout = 'setTimeout', - str_resize = 'resize', - str_data = str_resize + '-special-event', - str_delay = 'delay', - str_throttle = 'throttleWindow'; - - // Property: jQuery.resize.delay - // - // The numeric interval (in milliseconds) at which the resize event polling - // loop executes. Defaults to 250. - - jq_resize[ str_delay ] = 250; - - // Property: jQuery.resize.throttleWindow - // - // Throttle the native window object resize event to fire no more than once - // every