var app = angular.module('x', ['ui.codemirror']);

/* =XML_Autocomplete
 * Taken directly from http://codemirror.net/demo/xmlcomplete.html
-----------------------------------------------------------------------------*/

app.factory('XML_Autocomplete', function(){
  var dummy = {
    attrs: {
      color: ["red", "green", "blue", "purple", "white", "black", "yellow"],
      size: ["large", "medium", "small"],
      description: null
    },
    children: []
  };

  var tags = {
    "!top": ["top"],
    top: {
      attrs: {
        lang: ["en", "de", "fr", "nl"],
        freeform: null
      },
      children: ["animal", "plant"]
    },
    animal: {
      attrs: {
        name: null,
        isduck: ["yes", "no"]
      },
      children: ["wings", "feet", "body", "head", "tail"]
    },
    plant: {
      attrs: {name: null},
      children: ["leaves", "stem", "flowers"]
    },
    wings: dummy, feet: dummy, body: dummy, head: dummy, tail: dummy,
    leaves: dummy, stem: dummy, flowers: dummy
  };

  function completeAfter(cm, pred) {
    var cur = cm.getCursor();
    if (!pred || pred()) setTimeout(function() {
      if (!cm.state.completionActive)
        CodeMirror.showHint(cm, CodeMirror.hint.xml, {schemaInfo: tags, completeSingle: false});
    }, 100);
    return CodeMirror.Pass;
  }

  function completeIfAfterLt(cm) {
    return completeAfter(cm, function() {
      var cur = cm.getCursor();
      return cm.getRange(CodeMirror.Pos(cur.line, cur.ch - 1), cur) == "<";
    });
  }

  function completeIfInTag(cm) {
    return completeAfter(cm, function() {
      var tok = cm.getTokenAt(cm.getCursor());
      if (tok.type == "string" && (!/['"]/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1)) return false;
      var inner = CodeMirror.innerMode(cm.getMode(), tok.state).state;
      return inner.tagName;
    });
  }
  
  return {
    mode: "xml",
    extraKeys: {
      "'<'": completeAfter,
      "'/'": completeIfAfterLt,
      "' '": completeIfInTag,
      "'='": completeIfInTag,
      "Ctrl-Space": function(cm) {
        CodeMirror.showHint(cm, CodeMirror.hint.xml, {schemaInfo: tags});
      }
    }
  };
});


/* =CodemirrorCtrl
 * Used for the demo
-----------------------------------------------------------------------------*/

app.controller('CodemirrorCtrl', ['$scope', 'XML_Autocomplete',function($scope, xmlAC) {
  $scope.cmModel = '<!-- write some xml below -->';
  $scope.cmOption = angular.extend(xmlAC, {
    lineNumbers: true
  });
}]);
<!DOCTYPE html>
<html ng-app="x">

  <head>
    <meta charset="utf-8" />
    <title>UI.CodeMirror: XML Autocomplete demo</title>
    
    <script>document.write('<base href="' + document.location + '" />');</script>
    
    
    <!-- Le css -->
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/css/bootstrap.min.css">
    
    <!-- Le codemirror things... -->
    <link rel="stylesheet" type="text/css" href="http://codemirror.net/lib/codemirror.css"/>
    <link rel="stylesheet" type="text/css" href="http://codemirror.net/addon/hint/show-hint.css"/>
    
    <link rel="stylesheet" type="text/css" href="style.css"/>
    
  </head>

  <body>
  
    <div class="jumbotron">
      <div class="container">
        <h1>UI.CodeMirror: XML Autocomplete demo</h1>
      </div>
    </div>
    
    <section ng-controller="CodemirrorCtrl">
      <div class="container">
      <div>
        <textarea ui-codemirror="cmOption" ng-model="cmModel"></textarea>
      </div>
      
      <p>Press <strong>ctrl-space</strong>, or type a '<' character to
    activate autocompletion. This demo defines a simple schema that
    guides completion. The schema can be customized—see
    the <a href="../doc/manual.html#addon_xml-hint">manual</a>.</p>

    <p>Development of the <code>xml-hint</code> addon was kindly
    sponsored
    by <a href="http://www.xperiment.mobi">www.xperiment.mobi</a>.</p>
    
      </div>
    </section>
    
  </body>
    
    <!-- Le vendor... -->
    <script src="http://code.jquery.com/jquery.min.js"></script>
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/js/bootstrap.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
    
    <!-- Le codemirror things... -->
    <script src="http://codemirror.net/lib/codemirror.js"></script>
    <script src="http://codemirror.net/addon/hint/show-hint.js"></script>
    <script src="http://codemirror.net/addon/hint/xml-hint.js"></script>
    <script src="http://codemirror.net/mode/xml/xml.js"></script>
    <script src="ui-codemirror.js"></script>
    
    <!-- Le custom js -->
    <script src="app.js"></script>
    
</html>
/* Put your css in here */

.CodeMirror { border: 1px solid #eee; }


/* =TWBS HACK
-----------------------------------------------------------------------------*/

.jumbotron {
  position: relative;
  font-size: 16px;
  color: #fff;
  color: rgba(255,255,255,.75);
  text-align: center;
  background-color: #563d7c;
  border-radius: 0;
}
'use strict';
/**
 * Binds a CodeMirror widget to a <textarea> element.
 */
angular.module('ui.codemirror', []).constant('uiCodemirrorConfig', {}).directive('uiCodemirror', [
  'uiCodemirrorConfig',
  function (uiCodemirrorConfig) {
    return {
      restrict: 'EA',
      require: '?ngModel',
      priority: 1,
      compile: function compile() {
        // Require CodeMirror
        if (angular.isUndefined(window.CodeMirror)) {
          throw new Error('ui-codemirror need CodeMirror to work... (o rly?)');
        }
        return function postLink(scope, iElement, iAttrs, ngModel) {
          var options, opts, codeMirror, initialTextValue;
          initialTextValue = iElement.text();
          options = uiCodemirrorConfig.codemirror || {};
          opts = angular.extend({ value: initialTextValue }, options, scope.$eval(iAttrs.uiCodemirror), scope.$eval(iAttrs.uiCodemirrorOpts));
          if (iElement[0].tagName === 'TEXTAREA') {
            // Might bug but still ...
            codeMirror = window.CodeMirror.fromTextArea(iElement[0], opts);
          } else {
            iElement.html('');
            codeMirror = new window.CodeMirror(function (cm_el) {
              iElement.append(cm_el);
            }, opts);
          }
          if (iAttrs.uiCodemirror || iAttrs.uiCodemirrorOpts) {
            var codemirrorDefaultsKeys = Object.keys(window.CodeMirror.defaults);
            scope.$watch(iAttrs.uiCodemirror || iAttrs.uiCodemirrorOpts, function updateOptions(newValues, oldValue) {
              if (!angular.isObject(newValues)) {
                return;
              }
              codemirrorDefaultsKeys.forEach(function (key) {
                if (newValues.hasOwnProperty(key)) {
                  if (oldValue && newValues[key] === oldValue[key]) {
                    return;
                  }
                  codeMirror.setOption(key, newValues[key]);
                }
              });
            }, true);
          }
          if (ngModel) {
            // CodeMirror expects a string, so make sure it gets one.
            // This does not change the model.
            ngModel.$formatters.push(function (value) {
              if (angular.isUndefined(value) || value === null) {
                return '';
              } else if (angular.isObject(value) || angular.isArray(value)) {
                throw new Error('ui-codemirror cannot use an object or an array as a model');
              }
              return value;
            });
            // Override the ngModelController $render method, which is what gets called when the model is updated.
            // This takes care of the synchronizing the codeMirror element with the underlying model, in the case that it is changed by something else.
            ngModel.$render = function () {
              //Code mirror expects a string so make sure it gets one
              //Although the formatter have already done this, it can be possible that another formatter returns undefined (for example the required directive)
              var safeViewValue = ngModel.$viewValue || '';
              codeMirror.setValue(safeViewValue);
            };
            // Keep the ngModel in sync with changes from CodeMirror
            codeMirror.on('change', function (instance) {
              var newValue = instance.getValue();
              if (newValue !== ngModel.$viewValue) {
                // Changes to the model from a callback need to be wrapped in $apply or angular will not notice them
                scope.$apply(function () {
                  ngModel.$setViewValue(newValue);
                });
              }
            });
          }
          // Watch ui-refresh and refresh the directive
          if (iAttrs.uiRefresh) {
            scope.$watch(iAttrs.uiRefresh, function (newVal, oldVal) {
              // Skip the initial watch firing
              if (newVal !== oldVal) {
                codeMirror.refresh();
              }
            });
          }
          // Allow access to the CodeMirror instance through a broadcasted event
          // eg: $broadcast('CodeMirror', function(cm){...});
          scope.$on('CodeMirror', function (event, callback) {
            if (angular.isFunction(callback)) {
              callback(codeMirror);
            } else {
              throw new Error('the CodeMirror event requires a callback function');
            }
          });
          // onLoad callback
          if (angular.isFunction(opts.onLoad)) {
            opts.onLoad(codeMirror);
          }
        };
      }
    };
  }
]);