mirror of
https://github.com/fastmail/Squire.git
synced 2024-12-22 15:23:29 -05:00
Add IE10 compatibility.
* All UA detection moved into a separate file.
This commit is contained in:
parent
1d83790a57
commit
fe6ffb0ed5
9 changed files with 151 additions and 163 deletions
2
Makefile
2
Makefile
|
@ -9,7 +9,7 @@ build/ie8.js: source/ie8types.js source/ie8dom.js source/ie8range.js
|
||||||
mkdir -p $(@D)
|
mkdir -p $(@D)
|
||||||
cat $^ | uglifyjs > $@
|
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)
|
mkdir -p $(@D)
|
||||||
cat $^ | uglifyjs > $@
|
cat $^ | uglifyjs > $@
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<title></title>
|
<title></title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
html {
|
html {
|
||||||
|
@ -51,9 +52,6 @@
|
||||||
<!--[if IE 8]>
|
<!--[if IE 8]>
|
||||||
<script type="text/javascript" src="ie8.js"></script>
|
<script type="text/javascript" src="ie8.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
<!--[if IE 9]>
|
|
||||||
<script type="text/javascript">window.ie = 9;</script>
|
|
||||||
<![endif]-->
|
|
||||||
<script type="text/javascript" src="squire.js"></script>
|
<script type="text/javascript" src="squire.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
File diff suppressed because one or more lines are too long
|
@ -1,8 +1,9 @@
|
||||||
/* Copyright © 2011-2012 by Neil Jenkins. Licensed under the MIT license. */
|
/* 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";
|
"use strict";
|
||||||
|
|
||||||
|
@ -18,18 +19,15 @@
|
||||||
var win = doc.defaultView;
|
var win = doc.defaultView;
|
||||||
var body = doc.body;
|
var body = doc.body;
|
||||||
|
|
||||||
// Browser sniffing. Unfortunately necessary.
|
var isOpera = UA.isOpera;
|
||||||
var ua = navigator.userAgent;
|
var isGecko = UA.isGecko;
|
||||||
var isOpera = !!win.opera;
|
var isIOS = UA.isIOS;
|
||||||
var isIE = !!win.ie;
|
var isIE = UA.isIE;
|
||||||
var isIE8 = ( win.ie === 8 );
|
var isIE8 = UA.isIE8;
|
||||||
var isGecko = /Gecko\//.test( ua );
|
|
||||||
var isWebKit = /WebKit/.test( ua );
|
|
||||||
var isIOS = /iP(?:ad|hone|od)/.test( ua );
|
|
||||||
|
|
||||||
var useTextFixer = isIE || isOpera;
|
var cantFocusEmptyTextNodes = UA.cantFocusEmptyTextNodes;
|
||||||
var cantFocusEmptyTextNodes = isIE || isWebKit;
|
var losesSelectionOnBlur = UA.losesSelectionOnBlur;
|
||||||
var losesSelectionOnBlur = isIE;
|
var useTextFixer = UA.useTextFixer;
|
||||||
|
|
||||||
// --- DOM Sugar ---
|
// --- DOM Sugar ---
|
||||||
|
|
||||||
|
@ -500,7 +498,7 @@
|
||||||
|
|
||||||
// Otherwise, check each text node at least partially contained within
|
// Otherwise, check each text node at least partially contained within
|
||||||
// the selection and make sure all of them have the format we want.
|
// 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 ) ?
|
return range.containsNode( node, true ) ?
|
||||||
FILTER_ACCEPT : FILTER_SKIP;
|
FILTER_ACCEPT : FILTER_SKIP;
|
||||||
}, false );
|
}, false );
|
||||||
|
@ -534,7 +532,7 @@
|
||||||
// Create an iterator to walk over all the text nodes under this
|
// Create an iterator to walk over all the text nodes under this
|
||||||
// ancestor which are in the range and not already formatted
|
// ancestor which are in the range and not already formatted
|
||||||
// correctly.
|
// correctly.
|
||||||
var walker = doc.createTreeWalker(
|
var walker = new TreeWalker(
|
||||||
range.commonAncestorContainer,
|
range.commonAncestorContainer,
|
||||||
SHOW_TEXT,
|
SHOW_TEXT,
|
||||||
function ( node ) {
|
function ( node ) {
|
||||||
|
@ -947,7 +945,7 @@
|
||||||
|
|
||||||
var addLinks = function ( frag ) {
|
var addLinks = function ( frag ) {
|
||||||
var doc = frag.ownerDocument,
|
var doc = frag.ownerDocument,
|
||||||
walker = doc.createTreeWalker( frag, SHOW_TEXT,
|
walker = new TreeWalker( frag, SHOW_TEXT,
|
||||||
function ( node ) {
|
function ( node ) {
|
||||||
return node.nearest( 'A' ) ? FILTER_SKIP : FILTER_ACCEPT;
|
return node.nearest( 'A' ) ? FILTER_SKIP : FILTER_ACCEPT;
|
||||||
}, false ),
|
}, false ),
|
||||||
|
@ -1958,4 +1956,4 @@
|
||||||
win.onEditorLoad = null;
|
win.onEditorLoad = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}( document ) );
|
}( document, UA, DOMTreeWalker ) );
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* Copyright © 2011-2012 by Neil Jenkins. Licensed under the MIT license. */
|
/* 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,
|
( function ( UA, TreeWalker ) {
|
||||||
editor */
|
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
@ -56,9 +56,6 @@ var ELEMENT_NODE = 1, // Node.ELEMENT_NODE,
|
||||||
var isBlock = function ( el ) {
|
var isBlock = function ( el ) {
|
||||||
return el.isBlock() ? FILTER_ACCEPT : FILTER_SKIP;
|
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 ], {
|
implement( window.Node ? [ Node ] : [ Text, Element, HTMLDocument ], {
|
||||||
isInline: $False,
|
isInline: $False,
|
||||||
|
@ -89,14 +86,14 @@ implement( window.Node ? [ Node ] : [ Text, Element, HTMLDocument ], {
|
||||||
},
|
},
|
||||||
getPreviousBlock: function () {
|
getPreviousBlock: function () {
|
||||||
var doc = this.ownerDocument,
|
var doc = this.ownerDocument,
|
||||||
walker = doc.createTreeWalker(
|
walker = new TreeWalker(
|
||||||
doc.body, SHOW_ELEMENT, isBlock, false );
|
doc.body, SHOW_ELEMENT, isBlock, false );
|
||||||
walker.currentNode = this;
|
walker.currentNode = this;
|
||||||
return walker.previousNode();
|
return walker.previousNode();
|
||||||
},
|
},
|
||||||
getNextBlock: function () {
|
getNextBlock: function () {
|
||||||
var doc = this.ownerDocument,
|
var doc = this.ownerDocument,
|
||||||
walker = doc.createTreeWalker(
|
walker = new TreeWalker(
|
||||||
doc.body, SHOW_ELEMENT, isBlock, false );
|
doc.body, SHOW_ELEMENT, isBlock, false );
|
||||||
walker.currentNode = this;
|
walker.currentNode = this;
|
||||||
return walker.nextNode();
|
return walker.nextNode();
|
||||||
|
@ -379,7 +376,7 @@ implement([ Element ], {
|
||||||
|
|
||||||
if ( el.isInline() ) {
|
if ( el.isInline() ) {
|
||||||
if ( !el.firstChild ) {
|
if ( !el.firstChild ) {
|
||||||
if ( cantFocusEmptyTextNodes ) {
|
if ( UA.cantFocusEmptyTextNodes ) {
|
||||||
fixer = doc.createTextNode( '\u200B' );
|
fixer = doc.createTextNode( '\u200B' );
|
||||||
editor._setPlaceholderTextNode( fixer );
|
editor._setPlaceholderTextNode( fixer );
|
||||||
} else {
|
} else {
|
||||||
|
@ -387,7 +384,7 @@ implement([ Element ], {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ( useTextFixer ) {
|
if ( UA.useTextFixer ) {
|
||||||
while ( el.nodeType !== TEXT_NODE && !el.isLeaf() ) {
|
while ( el.nodeType !== TEXT_NODE && !el.isLeaf() ) {
|
||||||
child = el.firstChild;
|
child = el.firstChild;
|
||||||
if ( !child ) {
|
if ( !child ) {
|
||||||
|
@ -452,4 +449,4 @@ if ( function () {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}() );
|
}( UA, DOMTreeWalker ) );
|
||||||
|
|
|
@ -1,18 +1,11 @@
|
||||||
/* Copyright © 2011-2012 by Neil Jenkins. Licensed under the MIT license. */
|
/* Copyright © 2011-2012 by Neil Jenkins. Licensed under the MIT license. */
|
||||||
|
|
||||||
( function () {
|
/*global Range, Node, DOMTreeWalker */
|
||||||
|
|
||||||
/*global Range, Node */
|
( function ( TreeWalker ) {
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var implement = function ( proto, props ) {
|
|
||||||
var prop;
|
|
||||||
for ( prop in props ) {
|
|
||||||
proto[ prop ] = props[ prop ];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var indexOf = Array.prototype.indexOf;
|
var indexOf = Array.prototype.indexOf;
|
||||||
|
|
||||||
var ELEMENT_NODE = 1, // Node.ELEMENT_NODE
|
var ELEMENT_NODE = 1, // Node.ELEMENT_NODE
|
||||||
|
@ -49,7 +42,7 @@ var getNodeAfter = function ( node, offset ) {
|
||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
|
|
||||||
implement( Range.prototype, {
|
var RangePrototypeExtensions = {
|
||||||
|
|
||||||
forEachTextNode: function ( fn ) {
|
forEachTextNode: function ( fn ) {
|
||||||
var range = this.cloneRange();
|
var range = this.cloneRange();
|
||||||
|
@ -58,7 +51,7 @@ implement( Range.prototype, {
|
||||||
var startContainer = range.startContainer,
|
var startContainer = range.startContainer,
|
||||||
endContainer = range.endContainer,
|
endContainer = range.endContainer,
|
||||||
root = range.commonAncestorContainer,
|
root = range.commonAncestorContainer,
|
||||||
walker = root.ownerDocument.createTreeWalker(
|
walker = new TreeWalker(
|
||||||
root, SHOW_TEXT, function ( node ) {
|
root, SHOW_TEXT, function ( node ) {
|
||||||
return FILTER_ACCEPT;
|
return FILTER_ACCEPT;
|
||||||
}, false ),
|
}, false ),
|
||||||
|
@ -518,6 +511,11 @@ implement( Range.prototype, {
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
}() );
|
var prop;
|
||||||
|
for ( prop in RangePrototypeExtensions ) {
|
||||||
|
Range.prototype[ prop ] = RangePrototypeExtensions[ prop ];
|
||||||
|
}
|
||||||
|
|
||||||
|
}( DOMTreeWalker ) );
|
||||||
|
|
|
@ -1,131 +1,102 @@
|
||||||
/* Copyright © 2011-2012 by Neil Jenkins. Licensed under the MIT license. */
|
/* Copyright © 2011-2012 by Neil Jenkins. Licensed under the MIT license. */
|
||||||
|
|
||||||
( function ( doc ) {
|
|
||||||
|
|
||||||
/*global document, window */
|
/*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 needsReplacement = !doc.createTreeWalker;
|
var DOMTreeWalker = (function () {
|
||||||
|
|
||||||
// IE9 sometimes throws errors when calling TreeWalker#nextNode or
|
"use strict";
|
||||||
// TreeWalker#previousNode. No way to feature detect this.
|
|
||||||
if ( window.ie === 9 ) {
|
|
||||||
needsReplacement = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Feature detect Opera bug in TreeWalker#previousNode
|
var typeToBitArray = {
|
||||||
if ( !needsReplacement ) {
|
// ELEMENT_NODE
|
||||||
( function () {
|
1: 1,
|
||||||
var div = doc.createElement( 'div' ),
|
// ATTRIBUTE_NODE
|
||||||
text = doc.createTextNode( '' );
|
2: 2,
|
||||||
|
// TEXT_NODE
|
||||||
|
3: 4,
|
||||||
|
// COMMENT_NODE
|
||||||
|
8: 128,
|
||||||
|
// DOCUMENT_NODE
|
||||||
|
9: 256,
|
||||||
|
// DOCUMENT_FRAGMENT_NODE
|
||||||
|
11: 1024
|
||||||
|
};
|
||||||
|
|
||||||
div.appendChild( text );
|
var FILTER_ACCEPT = 1;
|
||||||
|
|
||||||
var div1 = div.cloneNode( true ),
|
var TreeWalker = function ( root, nodeType, filter ) {
|
||||||
div2 = div.cloneNode( true ),
|
this.root = this.currentNode = root;
|
||||||
div3 = div.cloneNode( true ),
|
this.nodeType = nodeType;
|
||||||
walker = doc.createTreeWalker( div, 1, function ( node ) {
|
this.filter = filter;
|
||||||
return 1;
|
};
|
||||||
}, false );
|
|
||||||
div.appendChild( div1 );
|
|
||||||
div.appendChild( div2 );
|
|
||||||
div.appendChild( div3 );
|
|
||||||
walker.currentNode = div3;
|
|
||||||
if ( walker.previousNode() !== div2 ) {
|
|
||||||
needsReplacement = true;
|
|
||||||
}
|
|
||||||
}() );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !needsReplacement ) { return; }
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// ---
|
TreeWalker.prototype.previousNode = function () {
|
||||||
|
var current = this.currentNode,
|
||||||
var typeToBitArray = {
|
root = this.root,
|
||||||
// ELEMENT_NODE
|
nodeType = this.nodeType,
|
||||||
1: 1,
|
filter = this.filter,
|
||||||
// ATTRIBUTE_NODE
|
node;
|
||||||
2: 2,
|
while ( true ) {
|
||||||
// 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 ) {
|
|
||||||
if ( current === root ) {
|
if ( current === root ) {
|
||||||
break;
|
return null;
|
||||||
}
|
}
|
||||||
node = current.nextSibling;
|
node = current.previousSibling;
|
||||||
if ( !node ) { current = current.parentNode; }
|
if ( node ) {
|
||||||
}
|
while ( current = node.lastChild ) {
|
||||||
if ( !node ) {
|
node = current;
|
||||||
return null;
|
}
|
||||||
}
|
} else {
|
||||||
if ( ( typeToBitArray[ node.nodeType ] & nodeType ) &&
|
node = current.parentNode;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
} else {
|
if ( !node ) {
|
||||||
node = current.parentNode;
|
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 TreeWalker;
|
||||||
return new TreeWalker( root, nodeType, filter );
|
|
||||||
};
|
|
||||||
|
|
||||||
}( document ) );
|
})();
|
||||||
|
|
29
source/UA.js
Normal file
29
source/UA.js
Normal 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 );
|
|
@ -52,9 +52,6 @@
|
||||||
<!--[if IE 8]>
|
<!--[if IE 8]>
|
||||||
<script type="text/javascript" src="ie8.js"></script>
|
<script type="text/javascript" src="ie8.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
<!--[if IE 9]>
|
|
||||||
<script type="text/javascript">window.ie = 9;</script>
|
|
||||||
<![endif]-->
|
|
||||||
<script type="text/javascript" src="squire.js"></script>
|
<script type="text/javascript" src="squire.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Loading…
Reference in a new issue