Skip to content

Commit 452c7f0

Browse files
SimeonCSimeonC
SimeonC
authored and
SimeonC
committedFeb 5, 2015
feat(taBind): Allow updates while focussed.
Fixes #38 Updates while the editor is focussed will now allow overwriting of the content and caret will be put to the end of the content.
1 parent e1008df commit 452c7f0

File tree

5 files changed

+70
-23
lines changed

5 files changed

+70
-23
lines changed
 

‎lib/main.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ textAngular.directive("textAngular", [
468468
scope.displayElements.forminput.val(ngModel.$viewValue);
469469
// if the editors aren't focused they need to be updated, otherwise they are doing the updating
470470
/* istanbul ignore else: don't care */
471-
if(!scope._elementSelectTriggered && $document[0].activeElement !== scope.displayElements.html[0] && $document[0].activeElement !== scope.displayElements.text[0]){
471+
if(!scope._elementSelectTriggered){
472472
// catch model being null or undefined
473473
scope.html = ngModel.$viewValue || '';
474474
}

‎lib/taBind.js

+23-10
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ angular.module('textAngular.taBind', ['textAngular.factories', 'textAngular.DOM'
4444
var _isInputFriendly = _isContentEditable || element[0].tagName.toLowerCase() === 'textarea' || element[0].tagName.toLowerCase() === 'input';
4545
var _isReadonly = false;
4646
var _focussed = false;
47+
var _skipRender = false;
4748
var _disableSanitizer = attrs.taUnsafeSanitizer || taOptions.disableSanitizer;
4849
var _lastKey;
4950
var BLOCKED_KEYS = /^(9|19|20|27|33|34|35|36|37|38|39|40|45|112|113|114|115|116|117|118|119|120|121|122|123|144|145)$/i;
@@ -150,6 +151,7 @@ angular.module('textAngular.taBind', ['textAngular.factories', 'textAngular.DOM'
150151
};
151152

152153
var _setViewValue = function(_val, triggerUndo){
154+
_skipRender = true;
153155
if(typeof triggerUndo === "undefined" || triggerUndo === null) triggerUndo = true && _isContentEditable; // if not contentEditable then the native undo/redo is fine
154156
if(typeof _val === "undefined" || _val === null) _val = _compileHtml();
155157
if(_blankTest(_val)){
@@ -530,6 +532,7 @@ angular.module('textAngular.taBind', ['textAngular.factories', 'textAngular.DOM'
530532
if(!_isReadonly){
531533
_setViewValue();
532534
}
535+
_skipRender = true; // don't redo the whole thing, just check the placeholder logic
533536
ngModel.$render();
534537
});
535538

@@ -546,7 +549,7 @@ angular.module('textAngular.taBind', ['textAngular.factories', 'textAngular.DOM'
546549

547550
element.on('focus', scope.events.focus = function(){
548551
_focussed = true;
549-
ngModel.$render();
552+
element.removeClass('placeholder-text');
550553
});
551554

552555
element.on('mouseup', scope.events.mouseup = function(){
@@ -634,20 +637,27 @@ angular.module('textAngular.taBind', ['textAngular.factories', 'textAngular.DOM'
634637
ngModel.$render = function(){
635638
// catch model being null or undefined
636639
var val = ngModel.$viewValue || '';
640+
637641
// if the editor isn't focused it needs to be updated, otherwise it's receiving user input
638-
if($document[0].activeElement !== element[0]){
639-
// Not focussed
642+
if(!_skipRender){
643+
/* istanbul ignore else: in other cases we don't care */
644+
if(_isContentEditable && _focussed){
645+
// element is focussed, test for placeholder
646+
element.removeClass('placeholder-text');
647+
element[0].blur();
648+
$timeout(function(){
649+
element[0].focus();
650+
taSelection.setSelectionToElementEnd(element.children()[element.children().length - 1]);
651+
}, 1);
652+
}
640653
if(_isContentEditable){
641654
// WYSIWYG Mode
642655
if(attrs.placeholder){
643656
if(val === ''){
644657
// blank
645-
if(_focussed) element.removeClass('placeholder-text');
646-
else element.addClass('placeholder-text');
647658
_setInnerHTML(_defaultVal);
648659
}else{
649660
// not-blank
650-
element.removeClass('placeholder-text');
651661
_setInnerHTML(val);
652662
}
653663
}else{
@@ -667,13 +677,16 @@ angular.module('textAngular.taBind', ['textAngular.factories', 'textAngular.DOM'
667677
// only for input and textarea inputs
668678
element.val(val);
669679
}
670-
}else{
671-
/* istanbul ignore else: in other cases we don't care */
672-
if(_isContentEditable){
673-
// element is focussed, test for placeholder
680+
}
681+
if(_isContentEditable && attrs.placeholder){
682+
if(val === ''){
683+
if(_focussed) element.removeClass('placeholder-text');
684+
else element.addClass('placeholder-text');
685+
}else{
674686
element.removeClass('placeholder-text');
675687
}
676688
}
689+
_skipRender = false;
677690
};
678691

679692
if(attrs.taReadonly){

‎src/textAngular.js

+28-12
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,9 @@ function($window, $document, taDOM){
655655

656656
range.selectNodeContents(el);
657657
range.collapse(false);
658-
658+
if(el.childNodes && el.childNodes[el.childNodes.length - 1] && el.childNodes[el.childNodes.length - 1].nodeName === 'br'){
659+
range.startOffset = range.endOffset = range.startOffset - 1;
660+
}
659661
rangy.getSelection().setSingleRange(range);
660662
},
661663
// from http://stackoverflow.com/questions/6690752/insert-html-at-caret-in-a-contenteditable-div
@@ -760,6 +762,7 @@ function($window, $document, taDOM){
760762
range.deleteContents();
761763
}
762764
}
765+
763766
range.insertNode(frag);
764767
if(lastNode){
765768
api.setSelectionToElementEnd(lastNode);
@@ -933,6 +936,7 @@ angular.module('textAngular.taBind', ['textAngular.factories', 'textAngular.DOM'
933936
var _isInputFriendly = _isContentEditable || element[0].tagName.toLowerCase() === 'textarea' || element[0].tagName.toLowerCase() === 'input';
934937
var _isReadonly = false;
935938
var _focussed = false;
939+
var _skipRender = false;
936940
var _disableSanitizer = attrs.taUnsafeSanitizer || taOptions.disableSanitizer;
937941
var _lastKey;
938942
var BLOCKED_KEYS = /^(9|19|20|27|33|34|35|36|37|38|39|40|45|112|113|114|115|116|117|118|119|120|121|122|123|144|145)$/i;
@@ -1039,6 +1043,7 @@ angular.module('textAngular.taBind', ['textAngular.factories', 'textAngular.DOM'
10391043
};
10401044

10411045
var _setViewValue = function(_val, triggerUndo){
1046+
_skipRender = true;
10421047
if(typeof triggerUndo === "undefined" || triggerUndo === null) triggerUndo = true && _isContentEditable; // if not contentEditable then the native undo/redo is fine
10431048
if(typeof _val === "undefined" || _val === null) _val = _compileHtml();
10441049
if(_blankTest(_val)){
@@ -1419,6 +1424,7 @@ angular.module('textAngular.taBind', ['textAngular.factories', 'textAngular.DOM'
14191424
if(!_isReadonly){
14201425
_setViewValue();
14211426
}
1427+
_skipRender = true; // don't redo the whole thing, just check the placeholder logic
14221428
ngModel.$render();
14231429
});
14241430

@@ -1435,7 +1441,7 @@ angular.module('textAngular.taBind', ['textAngular.factories', 'textAngular.DOM'
14351441

14361442
element.on('focus', scope.events.focus = function(){
14371443
_focussed = true;
1438-
ngModel.$render();
1444+
element.removeClass('placeholder-text');
14391445
});
14401446

14411447
element.on('mouseup', scope.events.mouseup = function(){
@@ -1523,20 +1529,27 @@ angular.module('textAngular.taBind', ['textAngular.factories', 'textAngular.DOM'
15231529
ngModel.$render = function(){
15241530
// catch model being null or undefined
15251531
var val = ngModel.$viewValue || '';
1532+
15261533
// if the editor isn't focused it needs to be updated, otherwise it's receiving user input
1527-
if($document[0].activeElement !== element[0]){
1528-
// Not focussed
1534+
if(!_skipRender){
1535+
/* istanbul ignore else: in other cases we don't care */
1536+
if(_isContentEditable && _focussed){
1537+
// element is focussed, test for placeholder
1538+
element.removeClass('placeholder-text');
1539+
element[0].blur();
1540+
$timeout(function(){
1541+
element[0].focus();
1542+
taSelection.setSelectionToElementEnd(element.children()[element.children().length - 1]);
1543+
}, 1);
1544+
}
15291545
if(_isContentEditable){
15301546
// WYSIWYG Mode
15311547
if(attrs.placeholder){
15321548
if(val === ''){
15331549
// blank
1534-
if(_focussed) element.removeClass('placeholder-text');
1535-
else element.addClass('placeholder-text');
15361550
_setInnerHTML(_defaultVal);
15371551
}else{
15381552
// not-blank
1539-
element.removeClass('placeholder-text');
15401553
_setInnerHTML(val);
15411554
}
15421555
}else{
@@ -1556,13 +1569,16 @@ angular.module('textAngular.taBind', ['textAngular.factories', 'textAngular.DOM'
15561569
// only for input and textarea inputs
15571570
element.val(val);
15581571
}
1559-
}else{
1560-
/* istanbul ignore else: in other cases we don't care */
1561-
if(_isContentEditable){
1562-
// element is focussed, test for placeholder
1572+
}
1573+
if(_isContentEditable && attrs.placeholder){
1574+
if(val === ''){
1575+
if(_focussed) element.removeClass('placeholder-text');
1576+
else element.addClass('placeholder-text');
1577+
}else{
15631578
element.removeClass('placeholder-text');
15641579
}
15651580
}
1581+
_skipRender = false;
15661582
};
15671583

15681584
if(attrs.taReadonly){
@@ -2109,7 +2125,7 @@ textAngular.directive("textAngular", [
21092125
scope.displayElements.forminput.val(ngModel.$viewValue);
21102126
// if the editors aren't focused they need to be updated, otherwise they are doing the updating
21112127
/* istanbul ignore else: don't care */
2112-
if(!scope._elementSelectTriggered && $document[0].activeElement !== scope.displayElements.html[0] && $document[0].activeElement !== scope.displayElements.text[0]){
2128+
if(!scope._elementSelectTriggered){
21132129
// catch model being null or undefined
21142130
scope.html = ngModel.$viewValue || '';
21152131
}

‎test/taBind/taBind.display.spec.js

+10
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@ describe('taBind.display', function () {
9393
$rootScope.$digest();
9494
expect(element.hasClass('placeholder-text')).toBe(false);
9595
});
96+
it('should not add the placeholder text back if focussed and blank', function () {
97+
element.triggerHandler('focus');
98+
$rootScope.$digest();
99+
$rootScope.html = '<p>Lorem Ipsum</p>';
100+
$rootScope.$digest();
101+
$rootScope.html = '';
102+
$rootScope.$digest();
103+
expect($window.getComputedStyle(element[0], ':before').getPropertyValue('display')).toBe("");
104+
expect(element.html()).toEqual('<p><br></p>');
105+
});
96106
});
97107
describe('as contenteditable div initially with content', function(){
98108
var $rootScope, element, $window;

‎test/taBind/taBind.spec.js

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)