<!DOCTYPE html>
<html>

  <head>
    <script data-require="jquery@2.1.3" data-semver="2.1.3" src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
    <script data-require="angular.js@1.5.8" data-semver="1.5.8" src="https://code.angularjs.org/1.5.8/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body  ng-app="plunker">
    <div ng-controller="MainCtrl">
      <div>
        <pt-input-event-detector model="model" ng-model="model.text" events="events" set-event="setEvent(event, text)" on-change="updateModel(model)"></pt-input-event-detector>
      </div>
      <h1> &nbsp; {{model.text}}</h1>
      
      <table  cellspacing="0" cellpadding="0" ng-if="model.events.length">
        <thead>
          <tr>
            <th colspan="2">Keyup</th>
            <th colspan="2">Keydown</th>
            <th colspan="2">Paste</th>
          </tr>
          <tr>
            <th>Detected</th>
            <th>Key</th>
            <th>Detected</th>
            <th>Key</th>
            <th>Detected</th>
            <th>Text</th>
          </tr>
        </thead>
        <tbody>
          <tr ng-repeat="event in model.events">
            <td>{{event.keyup.detected}}</td>
            <td>{{event.keyup.text}}</td>
            <td>{{event.keydown.detected}}</td>
            <td>{{event.keydown.text}}</td>
            <td>{{event.paste.detected}}</td>
            <td>{{event.paste.text}}</td>
          </tr>
        </tbody>
      </table>
    </div>
  </body>

</html>
// Code goes here
var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
  
  var EVENTS = ['paste', 'keyup', 'keydown'];
  $scope.model = {
    text: '',
    events: []
  };
  
  $scope.setEvent = function(event, text) {
    var eventList = {};
    for ( var i in EVENTS ) {
      var detected = false;
      var val = '';
      if (EVENTS[i] === event) {
        detected = true;
        val = text;
      }
      
      eventList[EVENTS[i]] = {
          detected: detected,
          text: val
      };
    }

    $scope.model.events.push(eventList);

  }
});

app.directive('ptInputEventDetector', function($compile) {
  return {
    restrict: 'E',
    template: `<div class="position-relative">
  <input type="text" placeholder="Paste text here" ng-model="_model.text" ng-trim="false" ng-change="onTextUpdate(_model)"/>
  <button type="button" ng-if="!listenerActive" ng-click="launchEventListener()">Enable</button>
</div>`,
    replace: true,
    scope: {
      onChange : "&?",
      model: '=', 
      events: '=',
      setEvent: '&'
    },
    link: function(scope, element, attrs, ngModel) {
      var input = element.find('input');
      scope.listenerActive = false;

      scope._model = { text: '' }; 
      scope.launchEventListener = launchEventListener;
      
      scope.onTextUpdate = function(_model) {
        var textUpdate = _model.text;
        scope.model.text = textUpdate;
        scope.onChange({ model: scope.model });
        killEventListener();
        compileDirective();
      }
      
      function compileDirective() {
        $compile(input)(scope);
      }

      var isMeta = false; 
      
      function setText(event, text, type, key) {
        event.preventDefault();
        scope._model.text = text;
        
        scope.onTextUpdate(scope._model);
        scope.setEvent({event: type, text: text || key});
      }

      function handlePaste(event) {
        isMeta = false;
        
        var clipboardText = event.originalEvent.clipboardData.getData('text');
        setText(event, event, 'paste');
      }
      
      function handleKeyup(event) {
        if (event.metaKey || event.ctrlKey) {
          isMeta = false;

          scope.setEvent({event: 'keyup', text: event.key});
        }
      }

      function handleKeydown(event) {
        let key = event.which || event.keyCode;
        if(key == 8 || key == 46) {
          setText(event, '', 'keydown', event.key);
        } else {
          if (event.metaKey || event.ctrlKey) {
            isMeta = true;

            scope.setEvent({event: 'keydown', text: event.key});
          } else if (!isMeta && isVisibleKey(key)) {
            setText(event, String.fromCharCode(key), 'keydown');
          }
        }
      }
      
      function isVisibleKey(keyCode) {
        return (keyCode >= 48 && keyCode <= 90) || (keyCode >= 106 && keyCode <= 111) || (keyCode >= 186 && keyCode <= 192) || (keyCode >= 219 && keyCode <= 222);
      }
      
      function launchEventListener() {
        if(!scope.listenerActive) {
          input.bind('paste.test',  handlePaste);
          input.bind('keyup.test',  handleKeyup);
          input.bind('keydown.test', handleKeydown);
          scope.listenerActive = true;
          scope._model.text = 'Enter Text';
          input[0].focus();
        }
      }

      function killEventListener() {
        if(scope.listenerActive) {
          input.unbind('paste.test');
          input.unbind('keyup.test');
          input.unbind('keydown.test');
          scope.listenerActive = false;
        }
      }
      
      launchEventListener();
    }
  }
});
/* Styles go here */

div {
  margin: 10px;
  
}

input, button {
  border: 1px solid #000;
  border-radius: 2px;
  font-size: 24px;
}

table tr th,
table tr td {
  border: #ccc 1px solid;
  padding: 5px;
  margin: 0;
}