Skip to content

Commit

Permalink
Chore: Duplicating any util.js methods used into annotatorUtil.js (#260)
Browse files Browse the repository at this point in the history
  • Loading branch information
pramodsum authored Jul 31, 2017
1 parent 9ab6db4 commit 249e9f1
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 9 deletions.
3 changes: 1 addition & 2 deletions src/lib/annotations/AnnotationDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import EventEmitter from 'events';
import * as annotatorUtil from './annotatorUtil';
import * as constants from './annotationConstants';
import { CLASS_ACTIVE, CLASS_HIDDEN } from '../constants';
import { decodeKeydown } from '../util';
import { ICON_CLOSE, ICON_DELETE } from '../icons/icons';

const CLASS_ANNOTATION_PLAIN_HIGHLIGHT = 'bp-plain-highlight';
Expand Down Expand Up @@ -329,7 +328,7 @@ class AnnotationDialog extends EventEmitter {
keydownHandler(event) {
event.stopPropagation();

const key = decodeKeydown(event);
const key = annotatorUtil.decodeKeydown(event);
if (key === 'Escape') {
this.hide();
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/annotations/AnnotationService.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'whatwg-fetch';
import EventEmitter from 'events';
import autobind from 'autobind-decorator';
import Annotation from './Annotation';
import { getHeaders } from '../util';
import { getHeaders } from './annotatorUtil';

const ANONYMOUS_USER = {
id: '0',
Expand Down
154 changes: 153 additions & 1 deletion src/lib/annotations/__tests__/annotatorUtil-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import {
repositionCaret,
isPending,
validateThreadParams,
eventToLocationHandler
eventToLocationHandler,
decodeKeydown,
getHeaders,
replacePlaceholders
} from '../annotatorUtil';
import {
STATES,
Expand Down Expand Up @@ -400,4 +403,153 @@ describe('lib/annotations/annotatorUtil', () => {
expect(annotator.isChanged).to.be.true;
});
});

describe('decodeKeydown()', () => {
it('should return empty when no key', () => {
assert.equal(
decodeKeydown({
key: ''
}),
''
);
});
it('should return empty when modifier and key are same', () => {
assert.equal(
decodeKeydown({
key: 'Control',
ctrlKey: true
}),
''
);
});
it('should return correct with ctrl modifier', () => {
assert.equal(
decodeKeydown({
key: '1',
ctrlKey: true
}),
'Control+1'
);
});
it('should return correct with shift modifier', () => {
assert.equal(
decodeKeydown({
key: '1',
shiftKey: true
}),
'Shift+1'
);
});
it('should return correct with meta modifier', () => {
assert.equal(
decodeKeydown({
key: '1',
metaKey: true
}),
'Meta+1'
);
});
it('should return space key', () => {
assert.equal(
decodeKeydown({
key: ' '
}),
'Space'
);
});
it('should return right arrow key', () => {
assert.equal(
decodeKeydown({
key: 'Right'
}),
'ArrowRight'
);
});
it('should return left arrow key', () => {
assert.equal(
decodeKeydown({
key: 'Left'
}),
'ArrowLeft'
);
});
it('should return up arrow key', () => {
assert.equal(
decodeKeydown({
key: 'Up'
}),
'ArrowUp'
);
});
it('should return down arrow key', () => {
assert.equal(
decodeKeydown({
key: 'Down'
}),
'ArrowDown'
);
});
it('should return esc key', () => {
assert.equal(
decodeKeydown({
key: 'U+001B'
}),
'Escape'
);
});
it('should decode correct UTF8 key', () => {
assert.equal(
decodeKeydown({
key: 'U+0041'
}),
'A'
);
});
});

/* eslint-disable no-undef */
describe('getHeaders()', () => {
it('should return correct headers', () => {
const sharedLink = 'https://sharename';
const fooHeader = 'bar';
const token = 'someToken';
const headers = getHeaders({ foo: fooHeader }, token, sharedLink);
expect(headers.foo).to.equal(fooHeader);
expect(headers.Authorization).to.equal(`Bearer ${token}`);
expect(headers.BoxApi).to.equal(`shared_link=${sharedLink}`);
expect(headers['X-Box-Client-Name']).to.equal(__NAME__);
expect(headers['X-Box-Client-Version']).to.equal(__VERSION__);
});

it('should return correct headers with password', () => {
const headers = getHeaders({ foo: 'bar' }, 'token', 'https://sharename', 'password');
assert.equal(headers.foo, 'bar');
assert.equal(headers.Authorization, 'Bearer token');
assert.equal(headers.BoxApi, 'shared_link=https://sharename&shared_link_password=password');
assert.equal(headers['X-Box-Client-Name'], __NAME__);
assert.equal(headers['X-Box-Client-Version'], __VERSION__);
});
});

describe('replacePlaceholders()', () => {
it('should replace only the placeholder with the custom value in the given string', () => {
expect(replacePlaceholders('{1} highlighted', ['Bob'])).to.equal('Bob highlighted');
});

it('should replace all placeholders with the custom value in the given string', () => {
expect(replacePlaceholders('{1} highlighted {2}', ['Bob', 'Suzy'])).to.equal('Bob highlighted Suzy');
});

it('should replace only placeholders that have custom value in the given string', () => {
expect(replacePlaceholders('{1} highlighted {2}', ['Bob'])).to.equal('Bob highlighted {2}');
});

it('should respect the order of placeholders when given an arbitrary order', () => {
expect(replacePlaceholders('{2} highlighted {1}', ['Bob', 'Suzy'])).to.equal('Suzy highlighted Bob');
});

it('should replace with the same value if the placeholder is repeated', () => {
expect(replacePlaceholders('{2} highlighted {2}', ['Bob', 'Suzy'])).to.equal('Suzy highlighted Suzy');
});
});
});
141 changes: 141 additions & 0 deletions src/lib/annotations/annotatorUtil.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import 'whatwg-fetch';
import { CLASS_ACTIVE, CLASS_HIDDEN, CLASS_INVISIBLE } from '../constants';
import { TYPES, SELECTOR_ANNOTATION_CARET, PENDING_STATES } from './annotationConstants';

const HEADER_CLIENT_NAME = 'X-Box-Client-Name';
const HEADER_CLIENT_VERSION = 'X-Box-Client-Version';
/* eslint-disable no-undef */
const CLIENT_NAME = __NAME__;
const CLIENT_VERSION = __VERSION__;
/* eslint-enable no-undef */

const AVATAR_COLOR_COUNT = 9; // 9 colors defined in Box React UI avatar code
const THREAD_PARAMS = [
'annotatedElement',
Expand Down Expand Up @@ -379,3 +387,136 @@ export function eventToLocationHandler(locationFunction, callback) {
}
};
}

//------------------------------------------------------------------------------
// General Util Methods
//------------------------------------------------------------------------------

/**
* Function to decode key down events into keys
*
* @param {Event} event - Keydown event
* @return {string} Decoded keydown key
*/
export function decodeKeydown(event) {
let modifier = '';

// KeyboardEvent.key is the new spec supported in Chrome, Firefox and IE.
// KeyboardEvent.keyIdentifier is the old spec supported in Safari.
// Priority is given to the new spec.
let key = event.key || event.keyIdentifier || '';

// Get the modifiers on their own
if (event.ctrlKey) {
modifier = 'Control';
} else if (event.shiftKey) {
modifier = 'Shift';
} else if (event.metaKey) {
modifier = 'Meta';
}

// The key and keyIdentifier specs also include modifiers.
// Since we are manually getting the modifiers above we do
// not want to trap them again here.
if (key === modifier) {
key = '';
}

// keyIdentifier spec returns UTF8 char codes
// Need to convert them back to ascii.
if (key.indexOf('U+') === 0) {
if (key === 'U+001B') {
key = 'Escape';
} else {
key = String.fromCharCode(key.replace('U+', '0x'));
}
}

// If nothing was pressed just return
if (!key) {
return '';
}

// Special casing for space bar
if (key === ' ') {
key = 'Space';
}

// Edge bug which outputs "Esc" instead of "Escape"
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/5290772/
if (key === 'Esc') {
key = 'Escape';
}

// keyIdentifier spec does not prefix the word Arrow.
// Newer key spec does it automatically.
if (key === 'Right' || key === 'Left' || key === 'Down' || key === 'Up') {
key = `Arrow${key}`;
}

if (modifier) {
modifier += '+';
}

return modifier + key;
}

/**
* Builds a list of required XHR headers.
*
* @param {Object} [headers] - Optional headers
* @param {string} [token] - Optional auth token
* @param {string} [sharedLink] - Optional shared link
* @param {string} [password] - Optional shared link password
* @return {Object} Headers
*/
export function getHeaders(headers = {}, token = '', sharedLink = '', password = '') {
/* eslint-disable no-param-reassign */
if (token) {
headers.Authorization = `Bearer ${token}`;
}
if (sharedLink) {
headers.BoxApi = `shared_link=${sharedLink}`;

if (password) {
headers.BoxApi = `${headers.BoxApi}&shared_link_password=${password}`;
}
}

// Following headers are for API analytics
if (CLIENT_NAME) {
headers[HEADER_CLIENT_NAME] = CLIENT_NAME;
}

if (CLIENT_VERSION) {
headers[HEADER_CLIENT_VERSION] = CLIENT_VERSION;
}

/* eslint-enable no-param-reassign */
return headers;
}

/**
* Replaces variable place holders specified between {} in the string with
* specified custom value. Localizes strings that include variables.
*
* @param {string} string - String to be interpolated
* @param {string[]} placeholderValues - Custom values to replace into string
* @return {string} Properly translated string with replaced custom variable
*/
export function replacePlaceholders(string, placeholderValues) {
const regex = /\{\d+\}/g;

if (!string || !string.length) {
return string;
}

return string.replace(regex, (match) => {
// extracting the index that is supposed to replace the matched placeholder
const placeholderIndex = parseInt(match.replace(/^\D+/g, ''), 10) - 1;

/* eslint-disable no-plusplus */
return placeholderValues[placeholderIndex] ? placeholderValues[placeholderIndex] : match;
/* eslint-enable no-plusplus */
});
}
7 changes: 3 additions & 4 deletions src/lib/annotations/doc/DocHighlightDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import AnnotationDialog from '../AnnotationDialog';
import * as annotatorUtil from '../annotatorUtil';
import * as docAnnotatorUtil from './docAnnotatorUtil';
import { CLASS_HIDDEN, CLASS_ACTIVE } from '../../constants';
import { replacePlaceholders, decodeKeydown } from '../../util';
import { ICON_HIGHLIGHT, ICON_HIGHLIGHT_COMMENT } from '../../icons/icons';
import * as constants from '../annotationConstants';

Expand Down Expand Up @@ -36,7 +35,7 @@ class DocHighlightDialog extends AnnotationDialog {
// Will be displayed as '{name} highlighted'
if (annotation.text === '' && annotation.user.id !== '0') {
const highlightLabelEl = this.highlightDialogEl.querySelector(`.${CLASS_HIGHLIGHT_LABEL}`);
highlightLabelEl.textContent = replacePlaceholders(__('annotation_who_highlighted'), [
highlightLabelEl.textContent = annotatorUtil.replacePlaceholders(__('annotation_who_highlighted'), [
annotation.user.name
]);
annotatorUtil.showElement(highlightLabelEl);
Expand Down Expand Up @@ -242,7 +241,7 @@ class DocHighlightDialog extends AnnotationDialog {
// be 'Some User'
if (annotatorUtil.isPlainHighlight(annotations) && annotations[0].user.id !== '0') {
const highlightLabelEl = this.highlightDialogEl.querySelector(`.${CLASS_HIGHLIGHT_LABEL}`);
highlightLabelEl.textContent = replacePlaceholders(__('annotation_who_highlighted'), [
highlightLabelEl.textContent = annotatorUtil.replacePlaceholders(__('annotation_who_highlighted'), [
annotations[0].user.name
]);
annotatorUtil.showElement(highlightLabelEl);
Expand Down Expand Up @@ -309,7 +308,7 @@ class DocHighlightDialog extends AnnotationDialog {
*/
keydownHandler(event) {
event.stopPropagation();
if (decodeKeydown(event) === 'Enter') {
if (annotatorUtil.decodeKeydown(event) === 'Enter') {
this.mousedownHandler(event);
}
super.keydownHandler(event);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import AnnotationDialog from '../../AnnotationDialog';
import * as annotatorUtil from '../../annotatorUtil';
import * as docAnnotatorUtil from '../docAnnotatorUtil';
import { CLASS_HIDDEN, CLASS_ACTIVE } from '../../../constants';
import * as util from '../../../util';
import * as util from '../../annotatorUtil';
import * as constants from '../../annotationConstants';

let dialog;
Expand Down

0 comments on commit 249e9f1

Please sign in to comment.