/** * @see http://docs.angularjs.org/guide/concepts * @see http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController * @see https://github.com/angular/angular.js/issues/528#issuecomment-7573166 */ angular.module('contenteditable', []) .directive('contenteditable', ['$timeout', function($timeout) { return { restrict: 'A', require: '?ngModel', link: function(scope, element, attrs, ngModel) { // don't do anything unless this is actually bound to a model if (!ngModel) { return } // options var opts = {} angular.forEach([ 'stripBr', 'noLineBreaks', 'selectNonEditable', 'moveCaretToEndOnChange', ], function(opt) { var o = attrs[opt] opts[opt] = o && o !== 'false' }) // view -> model element.bind('input', function(e) { scope.$apply(function() { var html, html2, rerender html = element.text() rerender = false if (opts.stripBr) { html = html.replace(/
$/, '') } if (opts.noLineBreaks) { html2 = html.replace(/
/g, '').replace(/
/g, '').replace(/<\/div>/g, '') if (html2 !== html) { rerender = true html = html2 } } ngModel.$setViewValue(html) if (rerender) { ngModel.$render() } if (html === '') { // the cursor disappears if the contents is empty // so we need to refocus $timeout(function(){ element[0].blur() element[0].focus() }) } }) }) // model -> view var oldRender = ngModel.$render ngModel.$render = function() { var el, el2, range, sel if (!!oldRender) { oldRender() } element.html(ngModel.$viewValue || '') if (opts.moveCaretToEndOnChange) { el = element[0] range = document.createRange() sel = window.getSelection() if (el.childNodes.length > 0) { el2 = el.childNodes[el.childNodes.length - 1] range.setStartAfter(el2) } else { range.setStartAfter(el) } range.collapse(true) sel.removeAllRanges() sel.addRange(range) } } if (opts.selectNonEditable) { element.bind('click', function(e) { var range, sel, target target = e.toElement if (target !== this && angular.element(target).attr('contenteditable') === 'false') { range = document.createRange() sel = window.getSelection() range.setStartBefore(target) range.setEndAfter(target) sel.removeAllRanges() sel.addRange(range) } }) } } }}]);