Select formatting button on selection (#3301)

[feat] Select button when selection is on formatted text
pull/3309/merge
Joas Souza 2018-01-04 11:28:00 -03:00 committed by Luiza Pagliari
parent f1fcd16894
commit 454f539561
3 changed files with 206 additions and 12 deletions

View File

@ -400,7 +400,19 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
this.removeAttributeOnLine(lineNum, attributeName) :
this.setAttributeOnLine(lineNum, attributeName, attributeValue);
}
},
hasAttributeOnSelectionOrCaretPosition: function(attributeName) {
var hasSelection = ((this.rep.selStart[0] !== this.rep.selEnd[0]) || (this.rep.selEnd[1] !== this.rep.selStart[1]));
var hasAttrib;
if (hasSelection) {
hasAttrib = this.getAttributeOnSelection(attributeName);
}else {
var attributesOnCaretPosition = this.getAttributesOnCaret();
hasAttrib = _.contains(_.flatten(attributesOnCaretPosition), attributeName);
}
return hasAttrib;
},
});
module.exports = AttributeManager;

View File

@ -75,6 +75,9 @@ function Ace2Inner(){
var EDIT_BODY_PADDING_TOP = 8;
var EDIT_BODY_PADDING_LEFT = 8;
var FORMATTING_STYLES = ['bold', 'italic', 'underline', 'strikethrough'];
var SELECT_BUTTON_CLASS = 'selected';
var caughtErrors = [];
var thisAuthor = '';
@ -2472,17 +2475,11 @@ function Ace2Inner(){
}
}
if (selectionAllHasIt)
{
documentAttributeManager.setAttributesOnRange(rep.selStart, rep.selEnd, [
[attributeName, '']
]);
}
else
{
documentAttributeManager.setAttributesOnRange(rep.selStart, rep.selEnd, [
[attributeName, 'true']
]);
var attributeValue = selectionAllHasIt ? '' : 'true';
documentAttributeManager.setAttributesOnRange(rep.selStart, rep.selEnd, [[attributeName, attributeValue]]);
if (attribIsFormattingStyle(attributeName)) {
updateStyleButtonState(attributeName, !selectionAllHasIt); // italic, bold, ...
}
}
editorInfo.ace_toggleAttributeOnSelection = toggleAttributeOnSelection;
@ -2911,6 +2908,9 @@ function Ace2Inner(){
rep.selFocusAtStart = newSelFocusAtStart;
currentCallStack.repChanged = true;
// select the formatting buttons when there is the style applied on selection
selectFormattingButtonIfLineHasStyleApplied(rep);
hooks.callAll('aceSelectionChanged', {
rep: rep,
callstack: currentCallStack,
@ -2939,6 +2939,22 @@ function Ace2Inner(){
return (eventType === 'setup') || (eventType === 'setBaseText') || (eventType === 'importText');
}
function updateStyleButtonState(attribName, hasStyleOnRepSelection) {
var $formattingButton = parent.parent.$('[data-key="' + attribName + '"]').find('a');
$formattingButton.toggleClass(SELECT_BUTTON_CLASS, hasStyleOnRepSelection);
}
function attribIsFormattingStyle(attributeName) {
return _.contains(FORMATTING_STYLES, attributeName);
}
function selectFormattingButtonIfLineHasStyleApplied (rep) {
_.each(FORMATTING_STYLES, function (style) {
var hasStyleOnRepSelection = documentAttributeManager.hasAttributeOnSelectionOrCaretPosition(style);
updateStyleButtonState(style, hasStyleOnRepSelection);
})
}
function doCreateDomLine(nonEmpty)
{
if (browser.msie && (!nonEmpty))

View File

@ -0,0 +1,166 @@
describe("select formatting buttons when selection has style applied", function(){
var STYLES = ['italic', 'bold', 'underline', 'strikethrough'];
var SHORTCUT_KEYS = ['I', 'B', 'U', '5']; // italic, bold, underline, strikethrough
var FIRST_LINE = 0;
before(function(cb){
helper.newPad(cb);
this.timeout(60000);
});
var applyStyleOnLine = function(style, line) {
var chrome$ = helper.padChrome$;
selectLine(line);
var $formattingButton = chrome$('.buttonicon-' + style);
$formattingButton.click();
}
var isButtonSelected = function(style) {
var chrome$ = helper.padChrome$;
var $formattingButton = chrome$('.buttonicon-' + style);
return $formattingButton.parent().hasClass('selected');
}
var selectLine = function(lineNumber, offsetStart, offsetEnd) {
var inner$ = helper.padInner$;
var $line = inner$("div").eq(lineNumber);
helper.selectLines($line, $line, offsetStart, offsetEnd);
}
var placeCaretOnLine = function(lineNumber) {
var inner$ = helper.padInner$;
var $line = inner$("div").eq(lineNumber);
$line.sendkeys('{leftarrow}');
}
var undo = function() {
var $undoButton = helper.padChrome$(".buttonicon-undo");
$undoButton.click();
}
var testIfFormattingButtonIsDeselected = function(style) {
it('deselects the ' + style + ' button', function(done) {
helper.waitFor(function(){
return isButtonSelected(style) === false;
}).done(done)
});
}
var testIfFormattingButtonIsSelected = function(style) {
it('selects the ' + style + ' button', function(done) {
helper.waitFor(function(){
return isButtonSelected(style);
}).done(done)
});
}
var applyStyleOnLineAndSelectIt = function(line, style, cb) {
applyStyleOnLineOnFullLineAndRemoveSelection(line, style, selectLine, cb);
}
var applyStyleOnLineAndPlaceCaretOnit = function(line, style, cb) {
applyStyleOnLineOnFullLineAndRemoveSelection(line, style, placeCaretOnLine, cb);
}
var applyStyleOnLineOnFullLineAndRemoveSelection = function(line, style, selectTarget, cb) {
applyStyleOnLine(style, line);
// we have to give some time to Etherpad detects the selection changed
setTimeout(function() {
// remove selection from previous line
selectLine(line + 1);
setTimeout(function() {
// select the text or place the caret on a position that
// has the formatting text applied previously
selectTarget(line);
cb();
}, 1000);
}, 1000);
}
var pressFormattingShortcutOnSelection = function(key) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
//select this text element
$firstTextElement.sendkeys('{selectall}');
if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE
var evtType = "keypress";
}else{
var evtType = "keydown";
}
var e = inner$.Event(evtType);
e.ctrlKey = true; // Control key
e.which = key.charCodeAt(0); // I, U, B, 5
inner$("#innerdocbody").trigger(e);
}
STYLES.forEach(function(style){
context('when selection is in a text with ' + style + ' applied', function(){
before(function (done) {
this.timeout(4000);
applyStyleOnLineAndSelectIt(FIRST_LINE, style, done);
});
after(function () {
undo();
});
testIfFormattingButtonIsSelected(style);
});
context('when caret is in a position with ' + style + ' applied', function(){
before(function (done) {
this.timeout(4000);
applyStyleOnLineAndPlaceCaretOnit(FIRST_LINE, style, done);
});
after(function () {
undo();
});
testIfFormattingButtonIsSelected(style)
});
});
context('when user applies a style and the selection does not change', function() {
var style = STYLES[0]; // italic
before(function () {
applyStyleOnLine(style, FIRST_LINE);
});
// clean the style applied
after(function () {
applyStyleOnLine(style, FIRST_LINE);
});
it('selects the style button', function (done) {
expect(isButtonSelected(style)).to.be(true);
done();
});
});
SHORTCUT_KEYS.forEach(function(key, index){
var styleOfTheShortcut = STYLES[index]; // italic, bold, ...
context('when user presses CMD + ' + key, function() {
before(function () {
pressFormattingShortcutOnSelection(key);
});
testIfFormattingButtonIsSelected(styleOfTheShortcut);
context('and user presses CMD + ' + key + ' again', function() {
before(function () {
pressFormattingShortcutOnSelection(key);
});
testIfFormattingButtonIsDeselected(styleOfTheShortcut);
});
});
});
});