217 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
			
		
		
	
	
			217 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
/*
 | 
						|
 * 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);
 | 
						|
          });
 | 
						|
        }
 | 
						|
      };
 | 
						|
    }
 | 
						|
  ]);
 |