123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- /*
- * angular-elastic v2.4.2
- * (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);
- });
- }
- };
- }
- ]);
|