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

Add IE10 compatibility.

* All UA detection moved into a separate file.
This commit is contained in:
Neil Jenkins 2012-11-12 17:48:24 +11:00
parent 1d83790a57
commit fe6ffb0ed5
9 changed files with 151 additions and 163 deletions

View file

@ -9,7 +9,7 @@ build/ie8.js: source/ie8types.js source/ie8dom.js source/ie8range.js
mkdir -p $(@D)
cat $^ | uglifyjs > $@
build/squire.js: source/TreeWalker.js source/Node.js source/Range.js source/Editor.js
build/squire.js: source/UA.js source/TreeWalker.js source/Node.js source/Range.js source/Editor.js
mkdir -p $(@D)
cat $^ | uglifyjs > $@

View file

@ -2,6 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<style type="text/css">
html {
@ -51,9 +52,6 @@
<!--[if IE 8]>
<script type="text/javascript" src="ie8.js"></script>
<![endif]-->
<!--[if IE 9]>
<script type="text/javascript">window.ie = 9;</script>
<![endif]-->
<script type="text/javascript" src="squire.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

View file

@ -1,8 +1,9 @@
/* Copyright © 2011-2012 by Neil Jenkins. Licensed under the MIT license. */
/*global Range, navigator, top, window, document, setTimeout */
/*global UA, DOMTreeWalker, Range, navigator,
top, window, document, setTimeout */
( function ( doc ) {
( function ( doc, UA, TreeWalker ) {
"use strict";
@ -18,18 +19,15 @@
var win = doc.defaultView;
var body = doc.body;
// Browser sniffing. Unfortunately necessary.
var ua = navigator.userAgent;
var isOpera = !!win.opera;
var isIE = !!win.ie;
var isIE8 = ( win.ie === 8 );
var isGecko = /Gecko\//.test( ua );
var isWebKit = /WebKit/.test( ua );
var isIOS = /iP(?:ad|hone|od)/.test( ua );
var isOpera = UA.isOpera;
var isGecko = UA.isGecko;
var isIOS = UA.isIOS;
var isIE = UA.isIE;
var isIE8 = UA.isIE8;
var useTextFixer = isIE || isOpera;
var cantFocusEmptyTextNodes = isIE || isWebKit;
var losesSelectionOnBlur = isIE;
var cantFocusEmptyTextNodes = UA.cantFocusEmptyTextNodes;
var losesSelectionOnBlur = UA.losesSelectionOnBlur;
var useTextFixer = UA.useTextFixer;
// --- DOM Sugar ---
@ -500,7 +498,7 @@
// Otherwise, check each text node at least partially contained within
// the selection and make sure all of them have the format we want.
walker = doc.createTreeWalker( root, SHOW_TEXT, function ( node ) {
walker = new TreeWalker( root, SHOW_TEXT, function ( node ) {
return range.containsNode( node, true ) ?
FILTER_ACCEPT : FILTER_SKIP;
}, false );
@ -534,7 +532,7 @@
// Create an iterator to walk over all the text nodes under this
// ancestor which are in the range and not already formatted
// correctly.
var walker = doc.createTreeWalker(
var walker = new TreeWalker(
range.commonAncestorContainer,
SHOW_TEXT,
function ( node ) {
@ -947,7 +945,7 @@
var addLinks = function ( frag ) {
var doc = frag.ownerDocument,
walker = doc.createTreeWalker( frag, SHOW_TEXT,
walker = new TreeWalker( frag, SHOW_TEXT,
function ( node ) {
return node.nearest( 'A' ) ? FILTER_SKIP : FILTER_ACCEPT;
}, false ),
@ -1958,4 +1956,4 @@
win.onEditorLoad = null;
}
}( document ) );
}( document, UA, DOMTreeWalker ) );

View file

@ -1,9 +1,9 @@
/* Copyright © 2011-2012 by Neil Jenkins. Licensed under the MIT license. */
( function () {
/*global Node, Text, Element, HTMLDocument, window, document,
editor, UA, DOMTreeWalker */
/*global Node, Text, Element, HTMLDocument, window, document, navigator,
editor */
( function ( UA, TreeWalker ) {
"use strict";
@ -56,9 +56,6 @@ var ELEMENT_NODE = 1, // Node.ELEMENT_NODE,
var isBlock = function ( el ) {
return el.isBlock() ? FILTER_ACCEPT : FILTER_SKIP;
};
var useTextFixer = !!( window.opera || window.ie );
var cantFocusEmptyTextNodes =
/WebKit/.test( navigator.userAgent ) || !!window.ie;
implement( window.Node ? [ Node ] : [ Text, Element, HTMLDocument ], {
isInline: $False,
@ -89,14 +86,14 @@ implement( window.Node ? [ Node ] : [ Text, Element, HTMLDocument ], {
},
getPreviousBlock: function () {
var doc = this.ownerDocument,
walker = doc.createTreeWalker(
walker = new TreeWalker(
doc.body, SHOW_ELEMENT, isBlock, false );
walker.currentNode = this;
return walker.previousNode();
},
getNextBlock: function () {
var doc = this.ownerDocument,
walker = doc.createTreeWalker(
walker = new TreeWalker(
doc.body, SHOW_ELEMENT, isBlock, false );
walker.currentNode = this;
return walker.nextNode();
@ -379,7 +376,7 @@ implement([ Element ], {
if ( el.isInline() ) {
if ( !el.firstChild ) {
if ( cantFocusEmptyTextNodes ) {
if ( UA.cantFocusEmptyTextNodes ) {
fixer = doc.createTextNode( '\u200B' );
editor._setPlaceholderTextNode( fixer );
} else {
@ -387,7 +384,7 @@ implement([ Element ], {
}
}
} else {
if ( useTextFixer ) {
if ( UA.useTextFixer ) {
while ( el.nodeType !== TEXT_NODE && !el.isLeaf() ) {
child = el.firstChild;
if ( !child ) {
@ -452,4 +449,4 @@ if ( function () {
};
}
}() );
}( UA, DOMTreeWalker ) );

View file

@ -1,18 +1,11 @@
/* Copyright © 2011-2012 by Neil Jenkins. Licensed under the MIT license. */
( function () {
/*global Range, Node, DOMTreeWalker */
/*global Range, Node */
( function ( TreeWalker ) {
"use strict";
var implement = function ( proto, props ) {
var prop;
for ( prop in props ) {
proto[ prop ] = props[ prop ];
}
};
var indexOf = Array.prototype.indexOf;
var ELEMENT_NODE = 1, // Node.ELEMENT_NODE
@ -49,7 +42,7 @@ var getNodeAfter = function ( node, offset ) {
return node;
};
implement( Range.prototype, {
var RangePrototypeExtensions = {
forEachTextNode: function ( fn ) {
var range = this.cloneRange();
@ -58,7 +51,7 @@ implement( Range.prototype, {
var startContainer = range.startContainer,
endContainer = range.endContainer,
root = range.commonAncestorContainer,
walker = root.ownerDocument.createTreeWalker(
walker = new TreeWalker(
root, SHOW_TEXT, function ( node ) {
return FILTER_ACCEPT;
}, false ),
@ -518,6 +511,11 @@ implement( Range.prototype, {
return this;
}
});
};
}() );
var prop;
for ( prop in RangePrototypeExtensions ) {
Range.prototype[ prop ] = RangePrototypeExtensions[ prop ];
}
}( DOMTreeWalker ) );

View file

@ -1,131 +1,102 @@
/* Copyright © 2011-2012 by Neil Jenkins. Licensed under the MIT license. */
( function ( doc ) {
/*global document, window */
"use strict";
/*
Native TreeWalker is buggy in IE and Opera:
* IE9/10 sometimes throw errors when calling TreeWalker#nextNode or
TreeWalker#previousNode. No way to feature detect this.
* Some versions of Opera have a bug in TreeWalker#previousNode which makes
it skip to the wrong node.
Rather than risk further bugs, it's easiest just to implement our own
(subset) of the spec in all browsers.
*/
// ---
var DOMTreeWalker = (function () {
var needsReplacement = !doc.createTreeWalker;
"use strict";
// IE9 sometimes throws errors when calling TreeWalker#nextNode or
// TreeWalker#previousNode. No way to feature detect this.
if ( window.ie === 9 ) {
needsReplacement = true;
}
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
};
// Feature detect Opera bug in TreeWalker#previousNode
if ( !needsReplacement ) {
( function () {
var div = doc.createElement( 'div' ),
text = doc.createTextNode( '' );
var FILTER_ACCEPT = 1;
div.appendChild( text );
var TreeWalker = function ( root, nodeType, filter ) {
this.root = this.currentNode = root;
this.nodeType = nodeType;
this.filter = filter;
};
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;
}
}() );
}
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 ) {
if ( current === root ) {
break;
}
node = current.nextSibling;
if ( !node ) { current = current.parentNode; }
}
if ( !node ) {
return null;
}
if ( ( typeToBitArray[ node.nodeType ] & nodeType ) &&
filter( node ) === FILTER_ACCEPT ) {
this.currentNode = node;
return node;
}
current = node;
}
};
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 ) {
TreeWalker.prototype.previousNode = function () {
var current = this.currentNode,
root = this.root,
nodeType = this.nodeType,
filter = this.filter,
node;
while ( true ) {
if ( current === root ) {
break;
return null;
}
node = current.nextSibling;
if ( !node ) { current = current.parentNode; }
}
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 ( current === root ) {
return null;
}
node = current.previousSibling;
if ( node ) {
while ( current = node.lastChild ) {
node = current;
node = current.previousSibling;
if ( node ) {
while ( current = node.lastChild ) {
node = current;
}
} else {
node = current.parentNode;
}
} else {
node = current.parentNode;
if ( !node ) {
return null;
}
if ( ( typeToBitArray[ node.nodeType ] & nodeType ) &&
filter( node ) === FILTER_ACCEPT ) {
this.currentNode = node;
return node;
}
current = node;
}
if ( !node ) {
return null;
}
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 );
};
return TreeWalker;
}( document ) );
})();

29
source/UA.js Normal file
View file

@ -0,0 +1,29 @@
/* Copyright © 2011-2012 by Neil Jenkins. Licensed under the MIT license. */
/*global navigator, window */
var UA = (function ( win ) {
"use strict";
var ua = navigator.userAgent;
var isOpera = !!win.opera;
var isIE = /Trident\//.test( ua );
var isWebKit = /WebKit\//.test( ua );
return {
// Browser sniffing. Unfortunately necessary.
isOpera: isOpera,
isIE8: ( win.ie === 8 ),
isIE: isIE,
isGecko: /Gecko\//.test( ua ),
isWebKit: isWebKit,
isIOS: /iP(?:ad|hone|od)/.test( ua ),
// Browser quirks
useTextFixer: isIE || isOpera,
cantFocusEmptyTextNodes: isIE || isWebKit,
losesSelectionOnBlur: isIE
};
})( window );

View file

@ -52,9 +52,6 @@
<!--[if IE 8]>
<script type="text/javascript" src="ie8.js"></script>
<![endif]-->
<!--[if IE 9]>
<script type="text/javascript">window.ie = 9;</script>
<![endif]-->
<script type="text/javascript" src="squire.js"></script>
</body>
</html>