summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoas Souza <joassouzasantos@gmail.com>2018-01-04 11:28:00 -0300
committerLuiza Pagliari <lpagliari@gmail.com>2018-01-04 12:28:00 -0200
commit454f539561a8d9de51ed107a29d974eb79198bc6 (patch)
tree6c1847daa5cb131e2bfe4bc520e995234d883269
parentf1fcd16894e562903caf02b30ac238592dac0bf8 (diff)
downloadetherpad-lite-454f539561a8d9de51ed107a29d974eb79198bc6.zip
Select formatting button on selection (#3301)
[feat] Select button when selection is on formatted text
-rw-r--r--src/static/js/AttributeManager.js14
-rw-r--r--src/static/js/ace2_inner.js38
-rw-r--r--tests/frontend/specs/select_formatting_buttons.js166
3 files changed, 206 insertions, 12 deletions
diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js
index 0342408c..53b233e0 100644
--- a/src/static/js/AttributeManager.js
+++ b/src/static/js/AttributeManager.js
@@ -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;
diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js
index 83f947ba..df9c9642 100644
--- a/src/static/js/ace2_inner.js
+++ b/src/static/js/ace2_inner.js
@@ -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))
diff --git a/tests/frontend/specs/select_formatting_buttons.js b/tests/frontend/specs/select_formatting_buttons.js
new file mode 100644
index 00000000..5fb97600
--- /dev/null
+++ b/tests/frontend/specs/select_formatting_buttons.js
@@ -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);
+ });
+ });
+ });
+});