We use cookies to make our website more effective. By using our website you agree to our privacy policy.

Source: searching.js

/**
 * searching.js is part of Aloha Editor project http://www.alohaeditor.org
 *
 * Aloha Editor ● JavaScript Content Editing Library
 * Copyright (c) 2010-2015 Gentics Software GmbH, Vienna, Austria.
 * Contributors http://www.alohaeditor.org/docs/contributing.html
 *
 * @overview
 * Module for searching for strings of token in markup
 * @namespace searching
 */
define([
	'dom',
	'boundaries',
	'functions'
], function (
	Dom,
	Boundaries,
	Fn
) {
	'use strict';

	/**
	 * Joins the given list of text nodes' text strings into a single string.
	 *
	 * @private
	 * @param  {Array.<Node>} nodes
	 * @return {string}
	 */
	function joinText(nodes) {
		return nodes.reduce(function (list, node) {
			return list.concat(node.data);
		}, []).join('');
	}

	/**
	 * Given a list of text nodes, will return a boundary of the position that
	 * is `index` offsets into the cumulative node lengths.
	 *
	 * @private
	 * @param  {Array.<Node>} List of text nodes
	 * @param  {number}       index
	 * @return {?Boundary}
	 */
	function boundaryInNodeList(nodes, index) {
		var cumulative = 0;
		var node;
		for (var i = 0; i < nodes.length; i++) {
			node = nodes[i];
			if (cumulative + Dom.nodeLength(node) >= index) {
				return Boundaries.create(node, index - cumulative);
			}
			cumulative += node.length;
		}
		return null;
	}

	/**
	 * Collects all preceeding text node along with the given in document order.
	 *
	 * @private
	 * @param  {!Node} node
	 * @return {Array.<Node>}
	 */
	function collectContiguiousTextNodes(node, collect) {
		return collect(node, function (node) {
			return !Dom.isTextNode(node) || Dom.isEditingHost(node);
		});
	}

	function searchBackward(boundary, regex) {
		var offset;
		var start = Boundaries.nodeBefore(boundary);
		if (start) {
			offset = Dom.nodeLength(start);
		} else {
			start = Boundaries.container(boundary);
			offset = Boundaries.offset(boundary);
		}
		var node = start;
		do {
			if (Dom.isTextNode(node)) {
				var nodes = collectContiguiousTextNodes(node, Dom.nodeAndPrevSiblings).reverse();
				var text = joinText(nodes);
				var index = text.search(regex);
				if (index > -1) {
					if (start !== node || index < offset) {
						return boundaryInNodeList(nodes, index);
					}
					index = text.substr(0, offset).search(regex);
					if (index > -1) {
						return boundaryInNodeList(nodes, index);
					}
				}
			}
			node = Dom.backward(node);
		} while (node && !Dom.isEditingHost(node));
		return null;
	}

	function searchForward(boundary, regex) {
		var offset;
		var start = Boundaries.nodeAfter(boundary);
		if (start) {
			offset = 0;
		} else {
			start = Boundaries.container(boundary);
			offset = Boundaries.offset(boundary);
		}
		var node = start;
		do {
			if (Dom.isTextNode(node)) {
				var nodes = collectContiguiousTextNodes(node, Dom.nodeAndNextSiblings);
				var text = joinText(nodes);
				if (node === start) {
					text = text.substr(offset);
				}
				var index = text.search(regex);
				if (index > -1) {
					return boundaryInNodeList(nodes, offset + index);
				}
			}
			node = Dom.forward(node);
		} while (node && !Dom.isEditingHost(node));
		return null;
	}

	/**
	 * Collects all preceeding text node along with the given in document order.
	 *
	 * @param  {!Boundary} node
	 * @param  {!RexExp}   regex
	 * @param  {string}    direction "forward" or "backward"
	 * @return {?Boundary}
	 * @memberOf searching
	 */
	function search(boundary, regex, direction) {
		return ('backward' === direction)
		     ? searchBackward(boundary, regex)
		     : searchForward(boundary, regex);
	}

	/**
	 * Find the given string backward of the given boundary.
	 *
	 * @param  {!Boundary} boundary
	 * @param  {string}    str
	 * @return {?Boundary}
	 * @memberOf searching
	 */
	function backward(boundary, str) {
		return searchBackward(boundary, new RegExp(str + '(?!.*' + str + ')'));
	}
	/**
	 * Find the given string forward of the given boundary.
	 *
	 * @param  {!Boundary} boundary
	 * @param  {string}    str
	 * @return {?Boundary}
	 * @memberOf searching
	 */
	function forward(boundary, str) {
		return searchForward(boundary, new RegExp(str));
	}

	return {
		search   : search,
		forward  : forward,
		backward : backward
	};
});
comments powered by Disqus