blob: ff91ab90e65ac4316ce934eb62cfad31c05164dd [file] [log] [blame]
// Copyright 2009 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A highlightable text area of the DOM.
*/
goog.provide('bidichecker.HighlightableText');
goog.require('bidichecker.HighlightableArea');
goog.require('goog.dom');
goog.require('goog.style');
/**
* Class representing the highlightable area of a BiDi error associated with
* text.
* <p>Note that this code may not work if more than one error is highlighted
* simultaneously, if the highlightable text areas share one or more more of the
* same text nodes in the DOM.
* @param {Array.<Node>} nodes An array of the text nodes containing the error
* text (corresponding to the {@code atText} field of {@code
* bidichecker.Error}).
* @param {number} startOffset The character offset of the error location from
* the start of the first node.
* @param {number} endOffset The character position within the last node to
* which the error location extends.
* @implements {bidichecker.HighlightableArea}
* @constructor
*/
bidichecker.HighlightableText = function(nodes, startOffset, endOffset) {
/**
* The text nodes containing the error text.
* @type {Array.<Node>}
* @private
*/
this.nodes_ = nodes;
/**
* The character offset from the start of the first node to the start of the
* error text.
* @type {number}
* @private
*/
this.startOffset_ = startOffset;
/**
* The character offset from the start of the last node to the end of the
* error text.
* @type {number}
* @private
*/
this.endOffset_ = endOffset;
/**
* The DOM nodes which replace the original text nodes (corresponding by
* array index to the text nodes in {@code this.nodes_}.
* @type {Array.<Node>}
* @private
*/
this.newNodes_ = [];
};
/** @inheritDoc */
bidichecker.HighlightableText.prototype.highlightOnPage = function() {
if (this.newNodes_.length == 0) {
// Wrap each text node in a highlighted span.
//
// TODO(user): What should we do if this fails to display the error, e.g.
// if the background color is already yellow, or if the error location is
// concealed behind another DOM element or scrolled out of view in a
// scrollable element?
for (var i = 0; i < this.nodes_.length; ++i) {
// Adjust the boundary character positions for the first and last nodes.
var startOffset = i == 0 ? this.startOffset_ : 0;
var endOffset = i == this.nodes_.length - 1 ? this.endOffset_ :
this.nodes_[i].length;
var newNode = bidichecker.HighlightableText.highlightNodeText_(
this.nodes_[i], startOffset, endOffset);
this.newNodes_.push(newNode);
}
} else {
// We've already highlighted this area before; instead of rebuilding the
// highlighted nodes, we can just swap the cached highlighted nodes for the
// original ones.
for (var i = 0; i < this.newNodes_.length; ++i) {
goog.dom.replaceNode(this.newNodes_[i], this.nodes_[i]);
}
}
return goog.style.getPageOffset(/** @type {Element} */ (this.newNodes_[0]));
};
/** @inheritDoc */
bidichecker.HighlightableText.prototype.unhighlightOnPage = function() {
if (this.newNodes_.length != 0) {
// Swap back the original nodes for the highlighted ones.
for (var i = 0; i < this.nodes_.length; ++i) {
goog.dom.replaceNode(this.nodes_[i], this.newNodes_[i]);
}
}
};
/**
* Highlights a single text node on the web page by wrapping its text in a
* yellow highlight span. Optionally, highlights just a substring of the text.
* @param {Node} node A text node.
* @param {number=} opt_start The starting character to highlight.
* @param {number=} opt_end One past the final character to highlight.
* @return {Node} The replacement node.
* @private
*/
bidichecker.HighlightableText.highlightNodeText_ = function(node, opt_start,
opt_end) {
var start = opt_start || 0;
var end = opt_end || node.data.length;
/** The children of the new span to replace the original text node. */
var children = [];
if (start > 0) {
// The unhighlighted text prefix, if any.
children.push(node.data.substring(0, start));
}
// The highlighted substring, wrapped in a yellow-highlighted span.
children.push(goog.dom.createDom('span',
{style: 'color:red; background-color:yellow'},
node.data.substring(start, end)));
if (end < node.data.length) {
// The unhighlighted text suffix, if any.
children.push(node.data.substring(end));
}
// And an outer span to hold all the children.
var newNode = goog.dom.createDom('span', null, children);
goog.dom.replaceNode(newNode, node);
return newNode;
};