blob: 2a424eacb36622d716041a34eb2bf07bd252bba8 [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 Walker to listen to DOM events and dispatch events representing
* same-directionality sequences of text in the same block-level element of the
* DOM.
*/
goog.provide('bidichecker.DirChunkWalker');
goog.require('bidichecker.DirChunk');
goog.require('bidichecker.DomWalker');
goog.require('bidichecker.NullDirChunk');
goog.require('goog.events.Event');
goog.require('goog.events.EventHandler');
goog.require('goog.events.EventTarget');
/**
* A walker which dispatches an event for each same-directionality chunk of text
* within a block in the DOM.
* @param {bidichecker.DomWalker} domWalker A walker which dispatches DOM node
* events while walking the DOM of a particular block.
* @constructor
* @extends {goog.events.EventTarget}
*/
bidichecker.DirChunkWalker = function(domWalker) {
goog.events.EventTarget.call(this);
/**
* The current chunk under assembly. When complete, a DirChunk event is
* dispatched and a listener can access the chunk using {@code getChunk()}.
* @type {bidichecker.DirChunk}
* @private
*/
this.chunk_ = bidichecker.NullDirChunk;
/**
* Service object to manage cleanup of event listeners.
* @type {goog.events.EventHandler}
* @private
*/
this.eventHandler_ = new goog.events.EventHandler(this);
// Listen for text node events and the end-of-DOM event.
this.eventHandler_.listen(domWalker,
bidichecker.DomWalker.EventTypes.TEXT_NODE,
this.handleTextNode_);
this.eventHandler_.listenOnce(domWalker,
bidichecker.DomWalker.EventTypes.END_OF_DOM,
this.handleEndOfDom_);
};
goog.inherits(bidichecker.DirChunkWalker, goog.events.EventTarget);
/**
* Event types dispatched by {@code bidichecker.DirChunkWalker}.
* @enum {string}
*/
bidichecker.DirChunkWalker.EventTypes = {
DIR_CHUNK: 'DirChunk',
END_OF_CHUNKS: 'EndOfChunks'
};
/**
* Handles events indicating a text node in the DOM by updating the current
* chunk or creating an new one. Dispatches a DirChunk event if the end of a
* chunk is encountered.
* @param {!goog.events.Event} event A TextNode event.
* @private
*/
bidichecker.DirChunkWalker.prototype.handleTextNode_ = function(event) {
var domWalker = event.target;
var node = domWalker.getNode();
if (this.chunk_.hasSameContext(domWalker.inRtl(),
domWalker.getCurrentBlock(),
domWalker.inDeclaredDir())) {
// No change of directionality context - just accumulate the content.
this.chunk_.append(node.data, node);
} else {
// When the direction changes or the block changes, return the previous
// chunk and start a new one.
// TODO(user): Also handle Unicode BiDi marks in node.data, including
// LRE, RLE, RLO, LRO and PDF.
if (!this.chunk_.isEmpty()) {
this.dispatchEvent(bidichecker.DirChunkWalker.EventTypes.DIR_CHUNK);
}
this.chunk_ = new bidichecker.DirChunk(
node.data,
domWalker.inRtl(),
node,
domWalker.getCurrentBlock(),
domWalker.inDeclaredDir());
}
};
/**
* Handles an end-of-DOM event by dispatching a DirChunk event if a non-empty
* chunk is available, then dispatching an EndOfChunks event (always). Also
* unlistens from all DOM events.
* @param {!goog.events.Event} event An EndOfDom event.
* @private
*/
bidichecker.DirChunkWalker.prototype.handleEndOfDom_ = function(event) {
if (!this.chunk_.isEmpty()) {
// Need to handle leftovers at end.
this.dispatchEvent(bidichecker.DirChunkWalker.EventTypes.DIR_CHUNK);
}
this.dispatchEvent(bidichecker.DirChunkWalker.EventTypes.END_OF_CHUNKS);
// Stop listening to DOM events.
this.eventHandler_.removeAll();
};
/**
* Returns the current chunk (either complete or under construction).
* @return {bidichecker.DirChunk} The chunk.
*/
bidichecker.DirChunkWalker.prototype.getChunk = function() {
return this.chunk_;
};