Jonathan Rosenbaum
7 years ago
12 changed files with 542 additions and 0 deletions
@ -0,0 +1 @@ |
|||
node_modules |
@ -0,0 +1,7 @@ |
|||
bower_components/ |
|||
bower.json |
|||
node_modules/ |
|||
npm-debug.log |
|||
gulpfile.cofee |
|||
src/ |
|||
.github |
@ -0,0 +1,89 @@ |
|||
### Automatically adjust textarea width/height based on user input. |
|||
#### Non-sucking version, still works great and used by a lot of people with no issues! |
|||
|
|||
|
|||
The only reason I wrote this jQuery plugin is that other plugins suck. They are wide-spread, but outdated and buggy. |
|||
|
|||
Advantages of jquery.ns-autogrow: |
|||
|
|||
* Grows vertically, horizontally or both |
|||
* Correctly handles 2 or more spaces |
|||
* Copies more css and font styles to shadow div |
|||
* Correctly handles long words on one line |
|||
* Flickering can be disabled on Enter |
|||
* Doesn't add more than one handler to textarea |
|||
* Handles textarea scrollbar if any |
|||
* Improved support of special characters |
|||
|
|||
## Download |
|||
Run one of these commands in your bash according to your needs. |
|||
|
|||
`git clone https://github.com/ro31337/jquery.ns-autogrow.git` |
|||
|
|||
`bower install jquery.ns-autogrow` |
|||
|
|||
`npm install jquery.ns-autogrow` |
|||
|
|||
Or download the latest version from the [releases](https://github.com/ro31337/jquery.ns-autogrow/releases) page. |
|||
|
|||
### Options |
|||
|
|||
You can provide multiple options to autogrow like: |
|||
|
|||
``` |
|||
$('.example2 textarea').autogrow({vertical: true, horizontal: false}); |
|||
``` |
|||
|
|||
List of options: |
|||
|
|||
Option | Description |
|||
-------|------------ |
|||
vertical | (true/false) - Enable/Disable vertical autogrow (true by default) |
|||
horizontal | (true/false) - Enable/Disable horizontal autogrow (true by default) |
|||
postGrowCallback | Post grow callback. Executes after dimensions of textarea have been adjusted. |
|||
flickering | (true/false) - Enable/Disable flickering. If flickering is disabled, extra line will be added to textarea. Flickering is _enabled_ by default. |
|||
|
|||
There are few more options reserved for debugging purposes. All debugging options start with `debug` prefix: |
|||
|
|||
Option | Description |
|||
-------|------------ |
|||
debugx | X position of shadow element (-10000 by default) |
|||
debugy | Y position of shadow element (-10000 by default) |
|||
debugcolor | Color of shadow element (yellow by default) |
|||
|
|||
|
|||
### Demo |
|||
|
|||
[Click here](http://htmlpreview.github.io/?https://raw.githubusercontent.com/ro31337/jquery.ns-autogrow/master/demo/index.html) |
|||
|
|||
### Plans: |
|||
|
|||
* Test and support arabic languages |
|||
|
|||
### :heart: Like it? :heart: |
|||
|
|||
:star: Star it! :star: |
|||
|
|||
### The MIT License (MIT) |
|||
|
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2015 Roman Pushkin |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in |
|||
all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
THE SOFTWARE. |
@ -0,0 +1,29 @@ |
|||
{ |
|||
"name": "jquery.ns-autogrow", |
|||
"description": "Automatically adjust textarea height based on user input. Non-sucking version.", |
|||
"main": "dist/jquery.ns-autogrow.js", |
|||
"keywords": [ |
|||
"autogrow", |
|||
"textarea", |
|||
"form", |
|||
"autosize", |
|||
"ui", |
|||
"jquery-plugin", |
|||
"ecosystem:jquery" |
|||
], |
|||
"author": "Roman Pushkin <roman.pushkin@gmail.com>", |
|||
"homepage": "https://github.com/ro31337/jquery.ns-autogrow", |
|||
"license": "MIT", |
|||
"ignore": [ |
|||
"**/.*", |
|||
"bower_components", |
|||
"node_modules", |
|||
"npm-debug.log", |
|||
"gulpfile.coffee", |
|||
"package.json", |
|||
"src" |
|||
], |
|||
"dependencies": { |
|||
"jquery": "1" |
|||
} |
|||
} |
@ -0,0 +1,31 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
|
|||
<head> |
|||
<script data-require="jquery@2.1.4" data-semver="2.1.4" src="http://code.jquery.com/jquery-2.1.4.min.js"></script> |
|||
<script src="../dist/jquery.ns-autogrow.min.js"></script> |
|||
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css" /> |
|||
<link rel="stylesheet" href="style.css" /> |
|||
<script src="script.js"></script> |
|||
</head> |
|||
|
|||
<body> |
|||
<div class="example1"> |
|||
<textarea placeholder="horizontal and vertical"></textarea> |
|||
</div> |
|||
|
|||
<div class="example2"> |
|||
<textarea placeholder="vertical only"></textarea> |
|||
</div> |
|||
|
|||
<div class="example3"> |
|||
<textarea placeholder="horizontal only"></textarea> |
|||
</div> |
|||
|
|||
<div class="example4"> |
|||
<textarea placeholder="horizontal and vertical, no flickering"></textarea> |
|||
</div> |
|||
|
|||
</body> |
|||
|
|||
</html> |
@ -0,0 +1,6 @@ |
|||
$(function(){ |
|||
$('.example1 textarea').autogrow(); |
|||
$('.example2 textarea').autogrow({vertical: true, horizontal: false}); |
|||
$('.example3 textarea').autogrow({vertical: false, horizontal: true}); |
|||
$('.example4 textarea').autogrow({flickering: false}); |
|||
}); |
@ -0,0 +1,3 @@ |
|||
body { padding: 20px; } |
|||
textarea { min-width: 240px; font-size: 18px; } |
|||
.example3 textarea { min-height: 50px; } |
@ -0,0 +1,139 @@ |
|||
/*! |
|||
Non-Sucking Autogrow 1.1.6 |
|||
license: MIT |
|||
author: Roman Pushkin |
|||
https://github.com/ro31337/jquery.ns-autogrow
|
|||
*/ |
|||
(function() { |
|||
var getVerticalScrollbarWidth; |
|||
|
|||
(function($, window) { |
|||
return $.fn.autogrow = function(options) { |
|||
if (options == null) { |
|||
options = {}; |
|||
} |
|||
if (options.horizontal == null) { |
|||
options.horizontal = true; |
|||
} |
|||
if (options.vertical == null) { |
|||
options.vertical = true; |
|||
} |
|||
if (options.debugx == null) { |
|||
options.debugx = -10000; |
|||
} |
|||
if (options.debugy == null) { |
|||
options.debugy = -10000; |
|||
} |
|||
if (options.debugcolor == null) { |
|||
options.debugcolor = 'yellow'; |
|||
} |
|||
if (options.flickering == null) { |
|||
options.flickering = true; |
|||
} |
|||
if (options.postGrowCallback == null) { |
|||
options.postGrowCallback = function() {}; |
|||
} |
|||
if (options.verticalScrollbarWidth == null) { |
|||
options.verticalScrollbarWidth = getVerticalScrollbarWidth(); |
|||
} |
|||
if (options.horizontal === false && options.vertical === false) { |
|||
return; |
|||
} |
|||
return this.filter('textarea').each(function() { |
|||
var $e, $shadow, fontSize, heightPadding, minHeight, minWidth, update; |
|||
$e = $(this); |
|||
if ($e.data('autogrow-enabled')) { |
|||
return; |
|||
} |
|||
$e.data('autogrow-enabled'); |
|||
minHeight = $e.height(); |
|||
minWidth = $e.width(); |
|||
heightPadding = $e.css('lineHeight') * 1 || 0; |
|||
$e.hasVerticalScrollBar = function() { |
|||
return $e[0].clientHeight < $e[0].scrollHeight; |
|||
}; |
|||
$shadow = $('<div class="autogrow-shadow"></div>').css({ |
|||
position: 'absolute', |
|||
display: 'inline-block', |
|||
'background-color': options.debugcolor, |
|||
top: options.debugy, |
|||
left: options.debugx, |
|||
'max-width': $e.css('max-width'), |
|||
'padding': $e.css('padding'), |
|||
fontSize: $e.css('fontSize'), |
|||
fontFamily: $e.css('fontFamily'), |
|||
fontWeight: $e.css('fontWeight'), |
|||
lineHeight: $e.css('lineHeight'), |
|||
resize: 'none', |
|||
'word-wrap': 'break-word' |
|||
}).appendTo(document.body); |
|||
if (options.horizontal === false) { |
|||
$shadow.css({ |
|||
'width': $e.width() |
|||
}); |
|||
} else { |
|||
fontSize = $e.css('font-size'); |
|||
$shadow.css('padding-right', '+=' + fontSize); |
|||
$shadow.normalPaddingRight = $shadow.css('padding-right'); |
|||
} |
|||
update = (function(_this) { |
|||
return function(event) { |
|||
var height, val, width; |
|||
val = _this.value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\n /g, '<br/> ').replace(/"/g, '"').replace(/'/g, ''').replace(/\n$/, '<br/> ').replace(/\n/g, '<br/>').replace(/ {2,}/g, function(space) { |
|||
return Array(space.length - 1).join(' ') + ' '; |
|||
}); |
|||
if (/(\n|\r)/.test(_this.value)) { |
|||
val += '<br />'; |
|||
if (options.flickering === false) { |
|||
val += '<br />'; |
|||
} |
|||
} |
|||
$shadow.html(val); |
|||
if (options.vertical === true) { |
|||
height = Math.max($shadow.height() + heightPadding, minHeight); |
|||
$e.height(height); |
|||
} |
|||
if (options.horizontal === true) { |
|||
$shadow.css('padding-right', $shadow.normalPaddingRight); |
|||
if (options.vertical === false && $e.hasVerticalScrollBar()) { |
|||
$shadow.css('padding-right', "+=" + options.verticalScrollbarWidth + "px"); |
|||
} |
|||
width = Math.max($shadow.outerWidth(), minWidth); |
|||
$e.width(width); |
|||
} |
|||
return options.postGrowCallback($e); |
|||
}; |
|||
})(this); |
|||
$e.change(update).keyup(update).keydown(update); |
|||
$(window).resize(update); |
|||
return update(); |
|||
}); |
|||
}; |
|||
})(window.jQuery, window); |
|||
|
|||
getVerticalScrollbarWidth = function() { |
|||
var inner, outer, w1, w2; |
|||
inner = document.createElement('p'); |
|||
inner.style.width = "100%"; |
|||
inner.style.height = "200px"; |
|||
outer = document.createElement('div'); |
|||
outer.style.position = "absolute"; |
|||
outer.style.top = "0px"; |
|||
outer.style.left = "0px"; |
|||
outer.style.visibility = "hidden"; |
|||
outer.style.width = "200px"; |
|||
outer.style.height = "150px"; |
|||
outer.style.overflow = "hidden"; |
|||
outer.appendChild(inner); |
|||
document.body.appendChild(outer); |
|||
w1 = inner.offsetWidth; |
|||
outer.style.overflow = 'scroll'; |
|||
w2 = inner.offsetWidth; |
|||
if (w1 === w2) { |
|||
w2 = outer.clientWidth; |
|||
} |
|||
document.body.removeChild(outer); |
|||
return w1 - w2; |
|||
}; |
|||
|
|||
}).call(this); |
@ -0,0 +1,7 @@ |
|||
/*! |
|||
Non-Sucking Autogrow 1.1.6 |
|||
license: MIT |
|||
author: Roman Pushkin |
|||
https://github.com/ro31337/jquery.ns-autogrow
|
|||
*/ |
|||
(function(){var e;!function(t,l){return t.fn.autogrow=function(i){if(null==i&&(i={}),null==i.horizontal&&(i.horizontal=!0),null==i.vertical&&(i.vertical=!0),null==i.debugx&&(i.debugx=-1e4),null==i.debugy&&(i.debugy=-1e4),null==i.debugcolor&&(i.debugcolor="yellow"),null==i.flickering&&(i.flickering=!0),null==i.postGrowCallback&&(i.postGrowCallback=function(){}),null==i.verticalScrollbarWidth&&(i.verticalScrollbarWidth=e()),i.horizontal!==!1||i.vertical!==!1)return this.filter("textarea").each(function(){var e,n,r,o,a,c,d;if(e=t(this),!e.data("autogrow-enabled"))return e.data("autogrow-enabled"),a=e.height(),c=e.width(),o=1*e.css("lineHeight")||0,e.hasVerticalScrollBar=function(){return e[0].clientHeight<e[0].scrollHeight},n=t('<div class="autogrow-shadow"></div>').css({position:"absolute",display:"inline-block","background-color":i.debugcolor,top:i.debugy,left:i.debugx,"max-width":e.css("max-width"),padding:e.css("padding"),fontSize:e.css("fontSize"),fontFamily:e.css("fontFamily"),fontWeight:e.css("fontWeight"),lineHeight:e.css("lineHeight"),resize:"none","word-wrap":"break-word"}).appendTo(document.body),i.horizontal===!1?n.css({width:e.width()}):(r=e.css("font-size"),n.css("padding-right","+="+r),n.normalPaddingRight=n.css("padding-right")),d=function(t){return function(l){var r,d,s;return d=t.value.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/\n /g,"<br/> ").replace(/"/g,""").replace(/'/g,"'").replace(/\n$/,"<br/> ").replace(/\n/g,"<br/>").replace(/ {2,}/g,function(e){return Array(e.length-1).join(" ")+" "}),/(\n|\r)/.test(t.value)&&(d+="<br />",i.flickering===!1&&(d+="<br />")),n.html(d),i.vertical===!0&&(r=Math.max(n.height()+o,a),e.height(r)),i.horizontal===!0&&(n.css("padding-right",n.normalPaddingRight),i.vertical===!1&&e.hasVerticalScrollBar()&&n.css("padding-right","+="+i.verticalScrollbarWidth+"px"),s=Math.max(n.outerWidth(),c),e.width(s)),i.postGrowCallback(e)}}(this),e.change(d).keyup(d).keydown(d),t(l).resize(d),d()})}}(window.jQuery,window),e=function(){var e,t,l,i;return e=document.createElement("p"),e.style.width="100%",e.style.height="200px",t=document.createElement("div"),t.style.position="absolute",t.style.top="0px",t.style.left="0px",t.style.visibility="hidden",t.style.width="200px",t.style.height="150px",t.style.overflow="hidden",t.appendChild(e),document.body.appendChild(t),l=e.offsetWidth,t.style.overflow="scroll",i=e.offsetWidth,l===i&&(i=t.clientWidth),document.body.removeChild(t),l-i}}).call(this); |
@ -0,0 +1,54 @@ |
|||
gulp = require 'gulp' |
|||
del = require 'del' |
|||
coffee = require 'gulp-coffee' |
|||
coffeelint = require 'gulp-coffeelint' |
|||
header = require 'gulp-header' |
|||
uglify = require 'gulp-uglify' |
|||
rename = require 'gulp-rename' |
|||
pjson = require './package.json' |
|||
copyright = """ |
|||
/*! |
|||
Non-Sucking Autogrow #{pjson.version} |
|||
license: #{pjson.license} |
|||
author: Roman Pushkin |
|||
#{pjson.homepage} |
|||
*/ |
|||
|
|||
""" |
|||
|
|||
dest = 'dist/' |
|||
source = 'src/' |
|||
code = |
|||
in: "#{source}*.coffee" |
|||
out: "#{dest}" |
|||
|
|||
gulp.task 'clean', -> |
|||
del [dest + '*'] |
|||
|
|||
gulp.task 'build', -> |
|||
gulp |
|||
.src code.in |
|||
.pipe coffeelint() |
|||
.pipe coffeelint.reporter() # Show coffeelint errors |
|||
.pipe coffeelint.reporter('fail') # Make sure it fails in case of error |
|||
.pipe coffee() |
|||
.pipe header copyright |
|||
.pipe gulp.dest(code.out) |
|||
|
|||
gulp |
|||
.src code.in |
|||
.pipe coffeelint() |
|||
.pipe coffeelint.reporter() # Show coffeelint errors |
|||
.pipe coffeelint.reporter('fail') # Make sure it fails in case of error |
|||
.pipe coffee() |
|||
.pipe uglify() |
|||
.pipe rename({ suffix: '.min' }) |
|||
.pipe header copyright |
|||
.pipe gulp.dest(code.out) |
|||
|
|||
gulp.task 'watch', -> |
|||
gulp |
|||
.watch code.in, ['build'] |
|||
|
|||
gulp.task 'default', ['clean', 'build', 'watch'], -> |
|||
|
@ -0,0 +1,45 @@ |
|||
{ |
|||
"name": "jquery.ns-autogrow", |
|||
"version": "1.1.6", |
|||
"description": "Automatically adjust textarea height based on user input. Non-sucking version.", |
|||
"main": "dist/jquery.ns-autogrow.js", |
|||
"keywords": [ |
|||
"autogrow", |
|||
"textarea", |
|||
"form", |
|||
"autosize", |
|||
"ui", |
|||
"jquery-plugin", |
|||
"ecosystem:jquery" |
|||
], |
|||
"author": "Roman Pushkin <roman.pushkin@gmail.com>", |
|||
"homepage": "https://github.com/ro31337/jquery.ns-autogrow", |
|||
"license": "MIT", |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "https://github.com/ro31337/jquery.ns-autogrow.git" |
|||
}, |
|||
"files": [ |
|||
"demo", |
|||
"dist" |
|||
], |
|||
"directories": { |
|||
"example": "demo" |
|||
}, |
|||
"bugs": { |
|||
"url": "https://github.com/ro31337/jquery.ns-autogrow/issues" |
|||
}, |
|||
"dependencies": { |
|||
"jquery": "^1.12.4" |
|||
}, |
|||
"devDependencies": { |
|||
"coffee-script": "^1.10.0", |
|||
"del": "^2.0.2", |
|||
"gulp": "^3.9.0", |
|||
"gulp-coffee": "^2.3.1", |
|||
"gulp-coffeelint": "^0.5.0", |
|||
"gulp-header": "^1.7.1", |
|||
"gulp-rename": "^1.2.2", |
|||
"gulp-uglify": "^1.4.1" |
|||
} |
|||
} |
@ -0,0 +1,131 @@ |
|||
(($, window) -> |
|||
|
|||
$.fn.autogrow = (options) -> |
|||
options ?= {} |
|||
options.horizontal ?= true |
|||
options.vertical ?= true |
|||
options.debugx ?= -10000 |
|||
options.debugy ?= -10000 |
|||
options.debugcolor ?= 'yellow' |
|||
options.flickering ?= true |
|||
options.postGrowCallback ?= -> |
|||
options.verticalScrollbarWidth ?= getVerticalScrollbarWidth() |
|||
|
|||
if options.horizontal is false and options.vertical is false |
|||
return |
|||
|
|||
@.filter('textarea').each -> |
|||
|
|||
$e = $(@) |
|||
|
|||
return if $e.data 'autogrow-enabled' |
|||
$e.data 'autogrow-enabled' |
|||
|
|||
minHeight = $e.height() |
|||
minWidth = $e.width() |
|||
heightPadding = $e.css('lineHeight') * 1 || 0 |
|||
$e.hasVerticalScrollBar = -> |
|||
$e[0].clientHeight < $e[0].scrollHeight |
|||
|
|||
$shadow = $('<div class="autogrow-shadow"></div>') |
|||
.css ( |
|||
position: 'absolute' |
|||
display: 'inline-block' |
|||
'background-color': options.debugcolor |
|||
top: options.debugy |
|||
left: options.debugx |
|||
'max-width': $e.css 'max-width' |
|||
'padding': $e.css 'padding' |
|||
fontSize: $e.css 'fontSize' |
|||
fontFamily: $e.css 'fontFamily' |
|||
fontWeight: $e.css 'fontWeight' |
|||
lineHeight: $e.css 'lineHeight' |
|||
resize: 'none' |
|||
'word-wrap': 'break-word' ) |
|||
.appendTo document.body |
|||
|
|||
if options.horizontal is false |
|||
# fix width of shadow div, so it will remain unchanged |
|||
$shadow.css({'width': $e.width()}) |
|||
else |
|||
# make sure we have right padding to avoid flickering |
|||
fontSize = $e.css 'font-size' # => 20px |
|||
$shadow.css('padding-right', '+=' + fontSize) |
|||
$shadow.normalPaddingRight = $shadow.css 'padding-right' |
|||
|
|||
update = (event) => |
|||
val = @ |
|||
.value |
|||
.replace /&/g, '&' |
|||
.replace /</g, '<' |
|||
.replace />/g, '>' |
|||
.replace /\n /g, '<br/> ' |
|||
.replace /"/g, '"' |
|||
.replace /'/g, ''' |
|||
.replace /\n$/, '<br/> ' |
|||
.replace /\n/g, '<br/>' |
|||
.replace( |
|||
/ {2,}/g |
|||
(space) -> Array(space.length - 1).join(' ') + ' ' |
|||
) |
|||
|
|||
if /(\n|\r)/.test @.value |
|||
val += '<br />' |
|||
|
|||
# no flickering, but one extra line will be added |
|||
if options.flickering is false |
|||
val += '<br />' |
|||
|
|||
$shadow.html val |
|||
|
|||
if options.vertical is true |
|||
height = Math.max($shadow.height() + heightPadding, minHeight) |
|||
$e.height height |
|||
|
|||
if options.horizontal is true |
|||
$shadow.css 'padding-right', $shadow.normalPaddingRight |
|||
|
|||
# if it should not grow vertically and if we have scrollbar, |
|||
# add additional padding to shadow div to emulate the scrollbar |
|||
if options.vertical is false and $e.hasVerticalScrollBar() |
|||
$shadow.css 'padding-right', "+=#{options.verticalScrollbarWidth}px" |
|||
|
|||
# outerWidth is width with padding |
|||
width = Math.max $shadow.outerWidth(), minWidth |
|||
$e.width width |
|||
|
|||
options.postGrowCallback $e |
|||
|
|||
$e.change update |
|||
.keyup update |
|||
.keydown update |
|||
|
|||
$(window).resize update |
|||
update() |
|||
|
|||
) window.jQuery, window |
|||
|
|||
getVerticalScrollbarWidth = -> |
|||
inner = document.createElement('p') |
|||
inner.style.width = "100%" |
|||
inner.style.height = "200px" |
|||
|
|||
outer = document.createElement('div') |
|||
outer.style.position = "absolute" |
|||
outer.style.top = "0px" |
|||
outer.style.left = "0px" |
|||
outer.style.visibility = "hidden" |
|||
outer.style.width = "200px" |
|||
outer.style.height = "150px" |
|||
outer.style.overflow = "hidden" |
|||
outer.appendChild (inner) |
|||
|
|||
document.body.appendChild (outer) |
|||
w1 = inner.offsetWidth |
|||
outer.style.overflow = 'scroll' |
|||
w2 = inner.offsetWidth |
|||
if w1 is w2 |
|||
w2 = outer.clientWidth |
|||
|
|||
document.body.removeChild (outer) |
|||
w1 - w2 |
Loading…
Reference in new issue