0
Fork 0
mirror of https://github.com/fastmail/Squire.git synced 2024-12-22 07:13:08 -05:00

Replace buggy TreeWalker implementations.

This commit is contained in:
Neil Jenkins 2011-11-02 14:02:18 +11:00
parent 7b000293d6
commit f89490736e
3 changed files with 146 additions and 49 deletions

View file

@ -5,7 +5,7 @@ clean:
build: build/squire.js build/document.html build: build/squire.js build/document.html
build/squire.js: source/Node.js source/Range.js source/Editor.js build/squire.js: source/TreeWalker.js source/Node.js source/Range.js source/Editor.js
mkdir -p $(@D) mkdir -p $(@D)
cat $^ | uglifyjs > $@ cat $^ | uglifyjs > $@

View file

@ -50,53 +50,9 @@ var ELEMENT_NODE = 1, // Node.ELEMENT_NODE,
FILTER_ACCEPT = 1, // NodeFilter.FILTER_ACCEPT, FILTER_ACCEPT = 1, // NodeFilter.FILTER_ACCEPT,
FILTER_SKIP = 3; // NodeFilter.FILTER_SKIP; FILTER_SKIP = 3; // NodeFilter.FILTER_SKIP;
var walkForward = function ( current, filter ) { var isBlock = function ( el ) {
var node; return el.isBlock() ? FILTER_ACCEPT : FILTER_SKIP;
while ( true ) {
node = current.firstChild;
while ( !node && current ) {
node = current.nextSibling;
if ( !node ) {
current = current.parentNode;
}
}
if ( !node ) {
return null;
}
if ( filter( node ) ) {
return node;
}
current = node;
}
}; };
var walkBackward = function ( current, filter ) {
var node;
while ( true ) {
node = current.previousSibling;
if ( node ) {
while ( current = node.lastChild ) {
node = current;
}
} else {
node = current.parentNode;
}
if ( node.nodeName === 'BODY' ||
node.nodeType === DOCUMENT_FRAGMENT_NODE ) {
return null;
}
if ( filter( node ) ) {
return node;
}
current = node;
}
};
var isBlock = function ( el ) { return el.isBlock(); };
var useTextFixer = !!( window.opera || window.ie ); var useTextFixer = !!( window.opera || window.ie );
implement( Node, { implement( Node, {
@ -127,10 +83,18 @@ implement( Node, {
return parent ? parent.nearest( tag, attributes ) : null; return parent ? parent.nearest( tag, attributes ) : null;
}, },
getPreviousBlock: function () { getPreviousBlock: function () {
return walkBackward( this, isBlock ); var doc = this.ownerDocument,
walker = doc.createTreeWalker(
doc.body, SHOW_ELEMENT, isBlock, false );
walker.currentNode = this;
return walker.previousNode();
}, },
getNextBlock: function () { getNextBlock: function () {
return walkForward( this, isBlock ); var doc = this.ownerDocument,
walker = doc.createTreeWalker(
doc.body, SHOW_ELEMENT, isBlock, false );
walker.currentNode = this;
return walker.nextNode();
}, },
split: function ( node, stopCondition ) { split: function ( node, stopCondition ) {
return node; return node;

133
source/TreeWalker.js Normal file
View file

@ -0,0 +1,133 @@
/* Copyright © 2011 by Neil Jenkins. Licensed under the MIT license. */
( function ( doc ) {
/*global document, window */
"use strict";
// ---
var needsReplacement = !doc.createTreeWalker;
// IE9 sometimes throws errors when calling TreeWalker#nextNode or
// TreeWalker#previousNode. No way to feature detect this.
if ( window.ie === 9 ) {
needsReplacement = true;
}
// Feature detect Opera bug in TreeWalker#previousNode
if ( !needsReplacement ) {
( function () {
var div = doc.createElement( 'div' ),
text = doc.createTextNode( '' );
div.appendChild( text );
var div1 = div.cloneNode( true ),
div2 = div.cloneNode( true ),
div3 = div.cloneNode( true ),
walker = doc.createTreeWalker( div, 1, function ( node ) {
return 1;
}, false );
div.appendChild( div1 );
div.appendChild( div2 );
div.appendChild( div3 );
walker.currentNode = div3;
if ( walker.previousNode() !== div2 ) {
needsReplacement = true;
}
}() );
}
if ( !needsReplacement ) { return; }
// ---
var typeToBitArray = {
// ELEMENT_NODE
1: 1,
// ATTRIBUTE_NODE
2: 2,
// TEXT_NODE
3: 4,
// COMMENT_NODE
8: 128,
// DOCUMENT_NODE
9: 256,
// DOCUMENT_FRAGMENT_NODE
11: 1024
};
var FILTER_ACCEPT = 1;
var TreeWalker = function ( root, nodeType, filter ) {
this.root = this.currentNode = root;
this.nodeType = nodeType;
this.filter = filter;
};
TreeWalker.prototype.nextNode = function () {
var current = this.currentNode,
root = this.root,
nodeType = this.nodeType,
filter = this.filter,
node;
while ( true ) {
node = current.firstChild;
while ( !node && current ) {
node = current.nextSibling;
if ( !node ) {
current = current.parentNode;
if ( current === root ) {
return null;
}
}
}
if ( !node ) {
return null;
}
if ( ( typeToBitArray[ node.nodeType ] & nodeType ) &&
filter( node ) === FILTER_ACCEPT ) {
this.currentNode = node;
return node;
}
current = node;
}
};
TreeWalker.prototype.previousNode = function () {
var current = this.currentNode,
root = this.root,
nodeType = this.nodeType,
filter = this.filter,
node;
while ( true ) {
if ( node === root ) {
return null;
}
node = current.previousSibling;
if ( node ) {
while ( current = node.lastChild ) {
node = current;
}
} else {
node = current.parentNode;
}
if ( ( typeToBitArray[ node.nodeType ] & nodeType ) &&
filter( node ) === FILTER_ACCEPT ) {
this.currentNode = node;
return node;
}
current = node;
}
};
doc.createTreeWalker = function ( root, nodeType, filter ) {
return new TreeWalker( root, nodeType, filter );
};
}( document ) );