Selaa lähdekoodia

WIP - implementing `areConnectedThroughProperties` util.

Maciej Bukowski 6 vuotta sitten
vanhempi
commit
e67ef6e682
1 muutettua tiedostoa jossa 91 lisäystä ja 0 poistoa
  1. 91 0
      packages/ckeditor5-utils/src/areconnectedthroughproperties.js

+ 91 - 0
packages/ckeditor5-utils/src/areconnectedthroughproperties.js

@@ -0,0 +1,91 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module utils/arestructuresconnected
+ */
+
+/* globals EventTarget */
+
+/**
+ * Traverses both structures to find out whether there's some object that is shared between both structures.
+ *
+ * @param {*} str1
+ * @param {*} str2
+ */
+export default function areConnectedThroughProperties( str1, str2 ) {
+	if ( str1 === str2 && isObject( str1 ) ) {
+		return true;
+	}
+
+	const subNodes1 = getSubNodes( str1 );
+	const subNodes2 = getSubNodes( str2 );
+
+	for ( const node of subNodes1 ) {
+		if ( subNodes2.has( node ) ) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+// Traverses JS structure and stores all sub-nodes, including the head node.
+// It walks into each iterable structures with the `try catch` block to omit errors that might be thrown during
+// tree walking. All primitives, functions and built-ins are skipped.
+function getSubNodes( head ) {
+	const nodes = [ head ];
+
+	// Nodes are stored to prevent infinite looping.
+	const subNodes = new Set();
+
+	while ( nodes.length > 0 ) {
+		const node = nodes.shift();
+
+		if ( subNodes.has( node ) || shouldNodeBeSkipped( node ) ) {
+			continue;
+		}
+
+		subNodes.add( node );
+
+		// Handle arrays, maps, sets, custom collections that implements `[ Symbol.iterator ]()`, etc.
+		if ( node[ Symbol.iterator ] ) {
+			// The custom editor iterators might cause some problems if the editor is crashed.
+			try {
+				nodes.push( ...node );
+			} catch ( err ) {
+				// eslint-disable-line no-empty
+			}
+		} else {
+			nodes.push( ...Object.values( node ) );
+		}
+	}
+
+	return subNodes;
+}
+
+function shouldNodeBeSkipped( node ) {
+	const type = Object.prototype.toString.call( node );
+
+	return (
+		type === '[object Number]' ||
+		type === '[object Boolean]' ||
+		type === '[object String]' ||
+		type === '[object Symbol]' ||
+		type === '[object Function]' ||
+		type === '[object Date]' ||
+		type === '[object RegExp]' ||
+
+		node === undefined ||
+		node === null ||
+
+		// Skip native DOM objects, e.g. Window, nodes, events, etc.
+		node instanceof EventTarget
+	);
+}
+
+function isObject( structure ) {
+	return typeof structure === 'object' && structure !== null;
+}