From a03d962daaca2d4e1ca8deeb11e3bd7411f34ec2 Mon Sep 17 00:00:00 2001 From: jos Date: Wed, 19 Jul 2017 14:31:26 +0200 Subject: [PATCH] Some refactoring --- src/components/JSONNode.js | 32 ++--------- src/components/menu/Menu.js | 4 +- src/components/menu/ModeMenu.js | 8 ++- src/components/utils/domSelector.js | 75 ++------------------------ src/utils/domUtils.js | 83 +++++++++++++++++++++-------- 5 files changed, 72 insertions(+), 130 deletions(-) diff --git a/src/components/JSONNode.js b/src/components/JSONNode.js index dc56172..9d5cf14 100644 --- a/src/components/JSONNode.js +++ b/src/components/JSONNode.js @@ -4,7 +4,7 @@ import { createElement as h, Component } from 'react' import ActionMenu from './menu/ActionMenu' import { escapeHTML, unescapeHTML } from '../utils/stringUtils' -import { getInnerText, insideRect, findParentNode } from '../utils/domUtils' +import { getInnerText, insideRect, findParentWithAttribute } from '../utils/domUtils' import { stringConvert, valueType, isUrl } from '../utils/typeUtils' import { compileJSONPointer } from '../jsonData' @@ -417,7 +417,7 @@ export default class JSONNode extends Component { handleOpenActionMenu = (event) => { // TODO: don't use refs, find root purely via DOM? - const root = findParentNode(this.refs.actionMenuButton, 'data-jsoneditor', 'true') + const root = findParentWithAttribute(this.refs.actionMenuButton, 'data-jsoneditor', 'true') this.setState({ menu: { @@ -434,7 +434,7 @@ export default class JSONNode extends Component { handleOpenAppendActionMenu = (event) => { // TODO: don't use refs, find root purely via DOM? - const root = findParentNode(this.refs.appendActionMenuButton, 'data-jsoneditor', 'true') + const root = findParentWithAttribute(this.refs.appendActionMenuButton, 'data-jsoneditor', 'true') this.setState({ appendMenu: { @@ -594,32 +594,6 @@ export default class JSONNode extends Component { ? stringValue : stringConvert(stringValue) } - - /** - * Find the root DOM element of the JSONEditor - * Search is done based on the CSS class 'jsoneditor' - * @param event - * @return {*} - */ - // TODO: make redundant and cleanup - static findRootElement (event) { - function isEditorElement (elem) { - // FIXME: this is a bit tricky. can we use a special attribute or something? - return elem.className.split(' ').indexOf('jsoneditor') !== -1 - } - - let elem = event.target - while (elem) { - if (isEditorElement(elem)) { - return elem - } - - elem = elem.parentNode - } - - return null - } - } /** diff --git a/src/components/menu/Menu.js b/src/components/menu/Menu.js index 9feea21..ed3d1eb 100644 --- a/src/components/menu/Menu.js +++ b/src/components/menu/Menu.js @@ -1,5 +1,5 @@ import { createElement as h, Component } from 'react' -import { findParentNode } from '../../utils/domUtils' +import { findParentWithAttribute } from '../../utils/domUtils' export let CONTEXT_MENU_HEIGHT = 240 @@ -178,7 +178,7 @@ export default class Menu extends Component { setTimeout(() => { if (!this.handleRequestClose) { this.handleRequestClose = (event) => { - if (!findParentNode(event.target, 'data-menu', 'true')) { + if (!findParentWithAttribute(event.target, 'data-menu', 'true')) { this.props.onRequestClose() } } diff --git a/src/components/menu/ModeMenu.js b/src/components/menu/ModeMenu.js index df47907..849667a 100644 --- a/src/components/menu/ModeMenu.js +++ b/src/components/menu/ModeMenu.js @@ -1,12 +1,10 @@ import { createElement as h, Component } from 'react' import { toCapital } from '../../utils/stringUtils' -import { findParentNode } from '../../utils/domUtils' +import { findParentWithAttribute } from '../../utils/domUtils' export default class ModeMenu extends Component { /** - * @param {{open, modes, mode, onChangeMode, onRequestClose, onError}} props - * @param {Object} state - * @return {JSX.Element} + * {{open, modes, mode, onChangeMode, onRequestClose, onError}} props */ render () { if (this.props.open) { @@ -65,7 +63,7 @@ export default class ModeMenu extends Component { setTimeout(() => { if (!this.handleRequestClose) { this.handleRequestClose = (event) => { - if (!findParentNode(event.target, 'data-menu', 'true')) { + if (!findParentWithAttribute(event.target, 'data-menu', 'true')) { this.props.onRequestClose() } } diff --git a/src/components/utils/domSelector.js b/src/components/utils/domSelector.js index 1209c02..b7bdf1f 100644 --- a/src/components/utils/domSelector.js +++ b/src/components/utils/domSelector.js @@ -1,4 +1,7 @@ -import { selectContentEditable, getSelection as getDOMSelection } from '../../utils/domUtils' +import { + selectContentEditable, hasClassName, + findParentWithAttribute, findParentWithClassName +} from '../../utils/domUtils' import { compileJSONPointer, parseJSONPointer } from '../../jsonData' // singleton @@ -172,21 +175,6 @@ export function findNode (container, path) { return container.querySelector(`div[${PATH_ATTRIBUTE}="${compileJSONPointer(path)}"]`) } -// TODO: fully implement getSelection, or cleanup if not needed -// function getSelectedElement () { -// const selection = getDOMSelection() -// return selection ? selection.startContainer : null -// } -// -// export function getSelection () { -// const element = getSelectedElement() -// const node = findBaseNode(element) -// -// return { -// path: node.getAttribute(PATH_ATTRIBUTE) // TODO: return parsed JSONPointer instead? -// } -// } - function findContentsContainer (element) { return findParentWithClassName (element, CONTENTS_CONTAINER_CLASS_NAME) } @@ -212,49 +200,6 @@ export function searchHasFocus () { } } -/** - * Find the first parent element having a specific class name - * @param {Element} element - * @param {string} className - * @return {Element} Returns the base element of the node - */ -function findParentWithClassName (element, className) { - let e = element - do { - if (hasClassName(e, className)) { - return e - } - - e = e.parentNode - } - while (e) - - return null -} - -/** - * Find the base element of a node from one of it's childs - * @param {Element} element - * @param {string} attribute - * @param {string} value - * @return {Element} Returns the base element of the node - */ -function findParentWithAttribute (element, attribute, value) { - let e = element - do { - if (e && e.hasAttribute && e.hasAttribute(attribute)) { - if (value === undefined || e.getAttribute(attribute) === value) { - return e - } - } - - e = e.parentNode - } - while (e) - - return null -} - function findPreviousNode (element) { const container = findContentsContainer(element) const node = findBaseNode(element) @@ -341,18 +286,6 @@ function findInputName (node, name) { return null } -/** - * Test whether a HTML element contains a specific className - * @param {Element} element - * @param {boolean} className - * @return {boolean} - */ -function hasClassName (element, className) { - return element && element.className - ? element.className.split(' ').indexOf(className) !== -1 - : false -} - /** * find the closest input that actually exists in this node * @param {Element} node diff --git a/src/utils/domUtils.js b/src/utils/domUtils.js index c8aed23..a31bca1 100644 --- a/src/utils/domUtils.js +++ b/src/utils/domUtils.js @@ -5,12 +5,12 @@ * @return {String} innerText */ export function getInnerText (element, buffer) { - var first = (buffer == undefined) + const first = (buffer === undefined) if (first) { buffer = { 'text': '', 'flush': function () { - var text = this.text + const text = this.text this.text = '' return text }, @@ -27,23 +27,23 @@ export function getInnerText (element, buffer) { // divs or other HTML elements if (element.hasChildNodes()) { - var childNodes = element.childNodes - var innerText = '' + const childNodes = element.childNodes + let innerText = '' - for (var i = 0, iMax = childNodes.length; i < iMax; i++) { - var child = childNodes[i] + for (let i = 0, iMax = childNodes.length; i < iMax; i++) { + const child = childNodes[i] - if (child.nodeName == 'DIV' || child.nodeName == 'P') { - var prevChild = childNodes[i - 1] - var prevName = prevChild ? prevChild.nodeName : undefined - if (prevName && prevName != 'DIV' && prevName != 'P' && prevName != 'BR') { + if (child.nodeName === 'DIV' || child.nodeName === 'P') { + const prevChild = childNodes[i - 1] + const prevName = prevChild ? prevChild.nodeName : undefined + if (prevName && prevName !== 'DIV' && prevName !== 'P' && prevName !== 'BR') { innerText += '\n' buffer.flush() } innerText += getInnerText(child, buffer) buffer.set('\n') } - else if (child.nodeName == 'BR') { + else if (child.nodeName === 'BR') { innerText += buffer.flush() buffer.set('\n') } @@ -55,7 +55,7 @@ export function getInnerText (element, buffer) { return innerText } else { - if (element.nodeName == 'P' && getInternetExplorerVersion() != -1) { + if (element.nodeName === 'P' && getInternetExplorerVersion() !== -1) { // On Internet Explorer, a

with hasChildNodes()==false is // rendered with a new line. Note that a

with // hasChildNodes()==true is rendered without a new line @@ -108,24 +108,61 @@ export function selectContentEditable(contentEditableElement) { * Find the parent node of an element which has an attribute with given value. * Can return the element itself too. * @param {Element} elem - * @param {string} attr - * @param {string} value + * @param {string} attribute + * @param {string} [value] * @return {Element | null} Returns the parent element when found, * or null otherwise */ -export function findParentNode (elem, attr, value) { +export function findParentWithAttribute (elem, attribute, value) { let parent = elem while (parent && parent.getAttribute) { - if (parent.getAttribute(attr) == value) { + const match = (value === undefined) + ? parent.hasAttribute(attribute) + : parent.getAttribute(attribute) === value + + if (match) { return parent } + parent = parent.parentNode } return null } +/** + * Find the first parent element having a specific class name + * @param {Element} element + * @param {string} className + * @return {Element} Returns the base element of the node + */ +export function findParentWithClassName (element, className) { + let parent = element + + while (parent) { + if (hasClassName(parent, className)) { + return parent + } + + parent = parent.parentNode + } + + return null +} + +/** + * Test whether a HTML element contains a specific className + * @param {Element} element + * @param {boolean} className + * @return {boolean} + */ +export function hasClassName (element, className) { + return element && element.className + ? element.className.split(' ').indexOf(className) !== -1 + : false +} + /** * Test whether the child rect fits completely inside the parent rect. * @param {ClientRect} parent @@ -146,13 +183,13 @@ export function insideRect (parent, child, margin = 0) { * @return {Number} Internet Explorer version, or -1 in case of an other browser */ export function getInternetExplorerVersion() { - if (_ieVersion == -1) { - var rv = -1 // Return value assumes failure. - if (navigator.appName == 'Microsoft Internet Explorer') + if (_ieVersion === -1) { + let rv = -1 // Return value assumes failure. + if (navigator.appName === 'Microsoft Internet Explorer') { - var ua = navigator.userAgent - var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})") - if (re.exec(ua) != null) { + const ua = navigator.userAgent + const re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})") + if (re.exec(ua) !== null) { rv = parseFloat( RegExp.$1 ) } } @@ -168,4 +205,4 @@ export function getInternetExplorerVersion() { * @type {Number} * @private */ -var _ieVersion = -1 +let _ieVersion = -1