diff --git a/CHANGELOG b/CHANGELOG index b0f6a0314d9..4bb4db4c47b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -50,6 +50,7 @@ v 8.4.0 (unreleased) - Fix Encoding::CompatibilityError bug when markdown content has some complex URL (Jason Lee) - Add API support for managing build variables of project - Allow broadcast messages to be edited + - Autosize Markdown textareas v 8.3.4 - Use gitlab-workhorse 0.5.4 (fixes API routing bug) diff --git a/app/assets/javascripts/behaviors/autosize.js.coffee b/app/assets/javascripts/behaviors/autosize.js.coffee new file mode 100644 index 00000000000..b32072e61ee --- /dev/null +++ b/app/assets/javascripts/behaviors/autosize.js.coffee @@ -0,0 +1,4 @@ +#= require autosize + +$ -> + autosize($('.js-autosize')) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index fcf50dd1b51..356fb6aa08c 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -1,4 +1,5 @@ #= require autosave +#= require autosize #= require dropzone #= require dropzone_input #= require gfm_auto_complete @@ -246,6 +247,7 @@ class @Notes else previewButton.removeClass("turn-on").addClass "turn-off" + autosize(textarea) new Autosave textarea, [ "Note" form.find("#note_commit_id").val() diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss index c540e527bed..6732343802a 100644 --- a/app/assets/stylesheets/framework/markdown_area.scss +++ b/app/assets/stylesheets/framework/markdown_area.scss @@ -83,6 +83,7 @@ background: #FFF; border: 1px solid #ddd; min-height: 140px; + max-height: 430px; padding: 5px; box-shadow: none; width: 100%; diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index d86259f93fb..2c9a42f9892 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -159,6 +159,7 @@ .edit_note { .markdown-area { min-height: 140px; + max-height: 430px; } .note-form-actions { background: transparent; diff --git a/app/views/projects/_zen.html.haml b/app/views/projects/_zen.html.haml index d5829568275..e701253d7de 100644 --- a/app/views/projects/_zen.html.haml +++ b/app/views/projects/_zen.html.haml @@ -1,6 +1,6 @@ .zennable .zen-backdrop - - classes << ' js-gfm-input markdown-area' + - classes << ' js-gfm-input js-autosize markdown-area' - if defined?(f) && f = f.text_area attr, class: classes - else diff --git a/vendor/assets/javascripts/autosize.js b/vendor/assets/javascripts/autosize.js new file mode 100755 index 00000000000..cfa49e72c50 --- /dev/null +++ b/vendor/assets/javascripts/autosize.js @@ -0,0 +1,243 @@ +/*! + Autosize 3.0.14 + license: MIT + http://www.jacklmoore.com/autosize +*/ +(function (global, factory) { + if (typeof define === 'function' && define.amd) { + define(['exports', 'module'], factory); + } else if (typeof exports !== 'undefined' && typeof module !== 'undefined') { + factory(exports, module); + } else { + var mod = { + exports: {} + }; + factory(mod.exports, mod); + global.autosize = mod.exports; + } +})(this, function (exports, module) { + 'use strict'; + + var set = typeof Set === 'function' ? new Set() : (function () { + var list = []; + + return { + has: function has(key) { + return Boolean(list.indexOf(key) > -1); + }, + add: function add(key) { + list.push(key); + }, + 'delete': function _delete(key) { + list.splice(list.indexOf(key), 1); + } }; + })(); + + function assign(ta) { + var _ref = arguments[1] === undefined ? {} : arguments[1]; + + var _ref$setOverflowX = _ref.setOverflowX; + var setOverflowX = _ref$setOverflowX === undefined ? true : _ref$setOverflowX; + var _ref$setOverflowY = _ref.setOverflowY; + var setOverflowY = _ref$setOverflowY === undefined ? true : _ref$setOverflowY; + + if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || set.has(ta)) return; + + var heightOffset = null; + var overflowY = null; + var clientWidth = ta.clientWidth; + + function init() { + var style = window.getComputedStyle(ta, null); + + overflowY = style.overflowY; + + if (style.resize === 'vertical') { + ta.style.resize = 'none'; + } else if (style.resize === 'both') { + ta.style.resize = 'horizontal'; + } + + if (style.boxSizing === 'content-box') { + heightOffset = -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom)); + } else { + heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth); + } + // Fix when a textarea is not on document body and heightOffset is Not a Number + if (isNaN(heightOffset)) { + heightOffset = 0; + } + + update(); + } + + function changeOverflow(value) { + { + // Chrome/Safari-specific fix: + // When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space + // made available by removing the scrollbar. The following forces the necessary text reflow. + var width = ta.style.width; + ta.style.width = '0px'; + // Force reflow: + /* jshint ignore:start */ + ta.offsetWidth; + /* jshint ignore:end */ + ta.style.width = width; + } + + overflowY = value; + + if (setOverflowY) { + ta.style.overflowY = value; + } + + resize(); + } + + function resize() { + var htmlTop = window.pageYOffset; + var bodyTop = document.body.scrollTop; + var originalHeight = ta.style.height; + + ta.style.height = 'auto'; + + var endHeight = ta.scrollHeight + heightOffset; + + if (ta.scrollHeight === 0) { + // If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM. + ta.style.height = originalHeight; + return; + } + + ta.style.height = endHeight + 'px'; + + // used to check if an update is actually necessary on window.resize + clientWidth = ta.clientWidth; + + // prevents scroll-position jumping + document.documentElement.scrollTop = htmlTop; + document.body.scrollTop = bodyTop; + } + + function update() { + var startHeight = ta.style.height; + + resize(); + + var style = window.getComputedStyle(ta, null); + + if (style.height !== ta.style.height) { + if (overflowY !== 'visible') { + changeOverflow('visible'); + } + } else { + if (overflowY !== 'hidden') { + changeOverflow('hidden'); + } + } + + if (startHeight !== ta.style.height) { + var evt = document.createEvent('Event'); + evt.initEvent('autosize:resized', true, false); + ta.dispatchEvent(evt); + } + } + + var pageResize = function pageResize() { + if (ta.clientWidth !== clientWidth) { + update(); + } + }; + + var destroy = (function (style) { + window.removeEventListener('resize', pageResize, false); + ta.removeEventListener('input', update, false); + ta.removeEventListener('keyup', update, false); + ta.removeEventListener('autosize:destroy', destroy, false); + ta.removeEventListener('autosize:update', update, false); + set['delete'](ta); + + Object.keys(style).forEach(function (key) { + ta.style[key] = style[key]; + }); + }).bind(ta, { + height: ta.style.height, + resize: ta.style.resize, + overflowY: ta.style.overflowY, + overflowX: ta.style.overflowX, + wordWrap: ta.style.wordWrap }); + + ta.addEventListener('autosize:destroy', destroy, false); + + // IE9 does not fire onpropertychange or oninput for deletions, + // so binding to onkeyup to catch most of those events. + // There is no way that I know of to detect something like 'cut' in IE9. + if ('onpropertychange' in ta && 'oninput' in ta) { + ta.addEventListener('keyup', update, false); + } + + window.addEventListener('resize', pageResize, false); + ta.addEventListener('input', update, false); + ta.addEventListener('autosize:update', update, false); + set.add(ta); + + if (setOverflowX) { + ta.style.overflowX = 'hidden'; + ta.style.wordWrap = 'break-word'; + } + + init(); + } + + function destroy(ta) { + if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return; + var evt = document.createEvent('Event'); + evt.initEvent('autosize:destroy', true, false); + ta.dispatchEvent(evt); + } + + function update(ta) { + if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return; + var evt = document.createEvent('Event'); + evt.initEvent('autosize:update', true, false); + ta.dispatchEvent(evt); + } + + var autosize = null; + + // Do nothing in Node.js environment and IE8 (or lower) + if (typeof window === 'undefined' || typeof window.getComputedStyle !== 'function') { + autosize = function (el) { + return el; + }; + autosize.destroy = function (el) { + return el; + }; + autosize.update = function (el) { + return el; + }; + } else { + autosize = function (el, options) { + if (el) { + Array.prototype.forEach.call(el.length ? el : [el], function (x) { + return assign(x, options); + }); + } + return el; + }; + autosize.destroy = function (el) { + if (el) { + Array.prototype.forEach.call(el.length ? el : [el], destroy); + } + return el; + }; + autosize.update = function (el) { + if (el) { + Array.prototype.forEach.call(el.length ? el : [el], update); + } + return el; + }; + } + + module.exports = autosize; +}); \ No newline at end of file