mirror of
https://github.com/fastmail/Squire.git
synced 2024-12-22 15:23:29 -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: 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 > $@
|
||||||
|
|
||||||
|
|
|
@ -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
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