<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-NgModelController-production</title>
<link href="style.css" rel="stylesheet" type="text/css">
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0-rc.1/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0-rc.1/angular-sanitize.js"></script>
<script src="https://rawgit.com/gregjacobs/Autolinker.js/master/dist/Autolinker.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rangy/1.3.0/rangy-core.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rangy/1.3.0/rangy-selectionsaverestore.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="customControl">
<h4>Caret Position screws up after pasting or writing a link</h4>
<div contenteditable
name="myWidget"
ng-bind-html ="userContent"
ng-model="userContent"
strip-br="true"
required>Change me </div>
<span ng-show="myForm.myWidget.$error.required">Required </span>
<hr>
Model: <p aria-label="Dynamic textarea">{{userContent}}</p>
<script>
rangy.init();
</script>
</body>
</html>
[contenteditable] {
border: 1px solid black;
background-color: white;
min-height: 20px;
}
.ng-invalid {
border: 1px solid red;
}
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
(function(angular) {
'use strict';
angular.module('customControl', ['ngSanitize']).
directive('contenteditable', ['$sce','$filter','$timeout', function($sce,$filter,$timeout) {
return {
restrict: 'A', // only activate on element attribute
require: '?ngModel', // get a hold of NgModelController
link: function(scope, element, attrs, ngModel) {
if (!ngModel) return; // do nothing if no ng-model
// Specify how UI should be updated
ngModel.$render = function() {
element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
};
// Listen for change events to enable binding
element.on('blur keyup change', function() {
scope.$evalAsync(read);
});
read(); // initialize
// Write data to the model
function read() {
var html = element.html();
// var pos = getCaretCharacterOffsetWithin(element[0]);
// console.log(pos);
var caret_position = rangy.saveSelection();
// When we clear the content editable the browser leaves a <br> behind
// If strip-br attribute is provided then we strip this out
if ( attrs.stripBr && html == '<br>' ) {
html = '';
}
var autoLinked = Autolinker.link(html, "_blank");
ngModel.$setViewValue(autoLinked);
// var el, el2, range, sel;
// 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);
rangy.restoreSelection(caret_position);
}
}
};
}]);
})(window.angular);