mirror of
https://github.com/element-hq/synapse
synced 2024-09-04 14:50:06 +00:00
SYWEB-12: You'll be needing this.
This commit is contained in:
parent
864de6a7a4
commit
86d3180666
1 changed files with 216 additions and 0 deletions
216
webclient/js/elastic.js
Normal file
216
webclient/js/elastic.js
Normal file
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* angular-elastic v2.4.0
|
||||
* (c) 2014 Monospaced http://monospaced.com
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
angular.module('monospaced.elastic', [])
|
||||
|
||||
.constant('msdElasticConfig', {
|
||||
append: ''
|
||||
})
|
||||
|
||||
.directive('msdElastic', [
|
||||
'$timeout', '$window', 'msdElasticConfig',
|
||||
function($timeout, $window, config) {
|
||||
'use strict';
|
||||
|
||||
return {
|
||||
require: 'ngModel',
|
||||
restrict: 'A, C',
|
||||
link: function(scope, element, attrs, ngModel) {
|
||||
|
||||
// cache a reference to the DOM element
|
||||
var ta = element[0],
|
||||
$ta = element;
|
||||
|
||||
// ensure the element is a textarea, and browser is capable
|
||||
if (ta.nodeName !== 'TEXTAREA' || !$window.getComputedStyle) {
|
||||
return;
|
||||
}
|
||||
|
||||
// set these properties before measuring dimensions
|
||||
$ta.css({
|
||||
'overflow': 'hidden',
|
||||
'overflow-y': 'hidden',
|
||||
'word-wrap': 'break-word'
|
||||
});
|
||||
|
||||
// force text reflow
|
||||
var text = ta.value;
|
||||
ta.value = '';
|
||||
ta.value = text;
|
||||
|
||||
var append = attrs.msdElastic ? attrs.msdElastic.replace(/\\n/g, '\n') : config.append,
|
||||
$win = angular.element($window),
|
||||
mirrorInitStyle = 'position: absolute; top: -999px; right: auto; bottom: auto;' +
|
||||
'left: 0; overflow: hidden; -webkit-box-sizing: content-box;' +
|
||||
'-moz-box-sizing: content-box; box-sizing: content-box;' +
|
||||
'min-height: 0 !important; height: 0 !important; padding: 0;' +
|
||||
'word-wrap: break-word; border: 0;',
|
||||
$mirror = angular.element('<textarea tabindex="-1" ' +
|
||||
'style="' + mirrorInitStyle + '"/>').data('elastic', true),
|
||||
mirror = $mirror[0],
|
||||
taStyle = getComputedStyle(ta),
|
||||
resize = taStyle.getPropertyValue('resize'),
|
||||
borderBox = taStyle.getPropertyValue('box-sizing') === 'border-box' ||
|
||||
taStyle.getPropertyValue('-moz-box-sizing') === 'border-box' ||
|
||||
taStyle.getPropertyValue('-webkit-box-sizing') === 'border-box',
|
||||
boxOuter = !borderBox ? {width: 0, height: 0} : {
|
||||
width: parseInt(taStyle.getPropertyValue('border-right-width'), 10) +
|
||||
parseInt(taStyle.getPropertyValue('padding-right'), 10) +
|
||||
parseInt(taStyle.getPropertyValue('padding-left'), 10) +
|
||||
parseInt(taStyle.getPropertyValue('border-left-width'), 10),
|
||||
height: parseInt(taStyle.getPropertyValue('border-top-width'), 10) +
|
||||
parseInt(taStyle.getPropertyValue('padding-top'), 10) +
|
||||
parseInt(taStyle.getPropertyValue('padding-bottom'), 10) +
|
||||
parseInt(taStyle.getPropertyValue('border-bottom-width'), 10)
|
||||
},
|
||||
minHeightValue = parseInt(taStyle.getPropertyValue('min-height'), 10),
|
||||
heightValue = parseInt(taStyle.getPropertyValue('height'), 10),
|
||||
minHeight = Math.max(minHeightValue, heightValue) - boxOuter.height,
|
||||
maxHeight = parseInt(taStyle.getPropertyValue('max-height'), 10),
|
||||
mirrored,
|
||||
active,
|
||||
copyStyle = ['font-family',
|
||||
'font-size',
|
||||
'font-weight',
|
||||
'font-style',
|
||||
'letter-spacing',
|
||||
'line-height',
|
||||
'text-transform',
|
||||
'word-spacing',
|
||||
'text-indent'];
|
||||
|
||||
// exit if elastic already applied (or is the mirror element)
|
||||
if ($ta.data('elastic')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Opera returns max-height of -1 if not set
|
||||
maxHeight = maxHeight && maxHeight > 0 ? maxHeight : 9e4;
|
||||
|
||||
// append mirror to the DOM
|
||||
if (mirror.parentNode !== document.body) {
|
||||
angular.element(document.body).append(mirror);
|
||||
}
|
||||
|
||||
// set resize and apply elastic
|
||||
$ta.css({
|
||||
'resize': (resize === 'none' || resize === 'vertical') ? 'none' : 'horizontal'
|
||||
}).data('elastic', true);
|
||||
|
||||
/*
|
||||
* methods
|
||||
*/
|
||||
|
||||
function initMirror() {
|
||||
var mirrorStyle = mirrorInitStyle;
|
||||
|
||||
mirrored = ta;
|
||||
// copy the essential styles from the textarea to the mirror
|
||||
taStyle = getComputedStyle(ta);
|
||||
angular.forEach(copyStyle, function(val) {
|
||||
mirrorStyle += val + ':' + taStyle.getPropertyValue(val) + ';';
|
||||
});
|
||||
mirror.setAttribute('style', mirrorStyle);
|
||||
}
|
||||
|
||||
function adjust() {
|
||||
var taHeight,
|
||||
taComputedStyleWidth,
|
||||
mirrorHeight,
|
||||
width,
|
||||
overflow;
|
||||
|
||||
if (mirrored !== ta) {
|
||||
initMirror();
|
||||
}
|
||||
|
||||
// active flag prevents actions in function from calling adjust again
|
||||
if (!active) {
|
||||
active = true;
|
||||
|
||||
mirror.value = ta.value + append; // optional whitespace to improve animation
|
||||
mirror.style.overflowY = ta.style.overflowY;
|
||||
|
||||
taHeight = ta.style.height === '' ? 'auto' : parseInt(ta.style.height, 10);
|
||||
|
||||
taComputedStyleWidth = getComputedStyle(ta).getPropertyValue('width');
|
||||
|
||||
// ensure getComputedStyle has returned a readable 'used value' pixel width
|
||||
if (taComputedStyleWidth.substr(taComputedStyleWidth.length - 2, 2) === 'px') {
|
||||
// update mirror width in case the textarea width has changed
|
||||
width = parseInt(taComputedStyleWidth, 10) - boxOuter.width;
|
||||
mirror.style.width = width + 'px';
|
||||
}
|
||||
|
||||
mirrorHeight = mirror.scrollHeight;
|
||||
|
||||
if (mirrorHeight > maxHeight) {
|
||||
mirrorHeight = maxHeight;
|
||||
overflow = 'scroll';
|
||||
} else if (mirrorHeight < minHeight) {
|
||||
mirrorHeight = minHeight;
|
||||
}
|
||||
mirrorHeight += boxOuter.height;
|
||||
|
||||
ta.style.overflowY = overflow || 'hidden';
|
||||
|
||||
if (taHeight !== mirrorHeight) {
|
||||
ta.style.height = mirrorHeight + 'px';
|
||||
scope.$emit('elastic:resize', $ta);
|
||||
}
|
||||
|
||||
// small delay to prevent an infinite loop
|
||||
$timeout(function() {
|
||||
active = false;
|
||||
}, 1);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function forceAdjust() {
|
||||
active = false;
|
||||
adjust();
|
||||
}
|
||||
|
||||
/*
|
||||
* initialise
|
||||
*/
|
||||
|
||||
// listen
|
||||
if ('onpropertychange' in ta && 'oninput' in ta) {
|
||||
// IE9
|
||||
ta['oninput'] = ta.onkeyup = adjust;
|
||||
} else {
|
||||
ta['oninput'] = adjust;
|
||||
}
|
||||
|
||||
$win.bind('resize', forceAdjust);
|
||||
|
||||
scope.$watch(function() {
|
||||
return ngModel.$modelValue;
|
||||
}, function(newValue) {
|
||||
forceAdjust();
|
||||
});
|
||||
|
||||
scope.$on('elastic:adjust', function() {
|
||||
initMirror();
|
||||
forceAdjust();
|
||||
});
|
||||
|
||||
$timeout(adjust);
|
||||
|
||||
/*
|
||||
* destroy
|
||||
*/
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
$mirror.remove();
|
||||
$win.unbind('resize', forceAdjust);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
]);
|
Loading…
Reference in a new issue