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:
parent
7b000293d6
commit
f89490736e
3 changed files with 146 additions and 49 deletions
2
Makefile
2
Makefile
|
@ -5,7 +5,7 @@ clean:
|
|||
|
||||
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)
|
||||
cat $^ | uglifyjs > $@
|
||||
|
||||
|
|
|
@ -50,53 +50,9 @@ var ELEMENT_NODE = 1, // Node.ELEMENT_NODE,
|
|||
FILTER_ACCEPT = 1, // NodeFilter.FILTER_ACCEPT,
|
||||
FILTER_SKIP = 3; // NodeFilter.FILTER_SKIP;
|
||||
|
||||
var walkForward = function ( current, filter ) {
|
||||
var node;
|
||||
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 isBlock = function ( el ) {
|
||||
return el.isBlock() ? FILTER_ACCEPT : FILTER_SKIP;
|
||||
};
|
||||
|
||||
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 );
|
||||
|
||||
implement( Node, {
|
||||
|
@ -127,10 +83,18 @@ implement( Node, {
|
|||
return parent ? parent.nearest( tag, attributes ) : null;
|
||||
},
|
||||
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 () {
|
||||
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 ) {
|
||||
return node;
|
||||
|
|
133
source/TreeWalker.js
Normal file
133
source/TreeWalker.js
Normal 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 ) );
|
Loading…
Reference in a new issue