0
Fork 0
mirror of https://github.com/fastmail/Squire.git synced 2025-01-03 05:00:13 -05:00

Fix ZWS removal when Squire script not in iframe.

This commit is contained in:
Neil Jenkins 2014-10-02 16:36:39 +07:00
parent e7cef49818
commit b944eb3b3b
7 changed files with 170 additions and 344 deletions

View file

@ -3,8 +3,7 @@
( function ( doc, undefined ) { ( function ( doc, undefined ) {
"use strict"; "use strict";
/*global doc, navigator */ /*jshint strict:false, undef:false, unused:false */
/*jshint strict:false */
var DOCUMENT_POSITION_PRECEDING = 2; // Node.DOCUMENT_POSITION_PRECEDING var DOCUMENT_POSITION_PRECEDING = 2; // Node.DOCUMENT_POSITION_PRECEDING
var ELEMENT_NODE = 1; // Node.ELEMENT_NODE; var ELEMENT_NODE = 1; // Node.ELEMENT_NODE;
@ -35,13 +34,13 @@ var ctrlKey = isMac ? 'meta-' : 'ctrl-';
var useTextFixer = isIE8or9or10 || isPresto; var useTextFixer = isIE8or9or10 || isPresto;
var cantFocusEmptyTextNodes = isIE8or9or10 || isWebKit; var cantFocusEmptyTextNodes = isIE8or9or10 || isWebKit;
var losesSelectionOnBlur = isIE8or9or10; var losesSelectionOnBlur = isIE8or9or10;
var hasBuggySplit = ( function () { var hasBuggySplit = function ( doc ) {
var div = doc.createElement( 'DIV' ), var div = doc.createElement( 'DIV' ),
text = doc.createTextNode( '12' ); text = doc.createTextNode( '12' );
div.appendChild( text ); div.appendChild( text );
text.splitText( 2 ); text.splitText( 2 );
return div.childNodes.length !== 2; return div.childNodes.length !== 2;
}() ); };
// Use [^ \t\r\n] instead of \S so that nbsp does not count as white-space // Use [^ \t\r\n] instead of \S so that nbsp does not count as white-space
var notWS = /[^ \t\r\n]/; var notWS = /[^ \t\r\n]/;
@ -137,20 +136,7 @@ TreeWalker.prototype.previousNode = function () {
current = node; current = node;
} }
}; };
/*global /*jshint strict:false, undef:false, unused:false */
ELEMENT_NODE,
TEXT_NODE,
SHOW_ELEMENT,
win,
isOpera,
useTextFixer,
cantFocusEmptyTextNodes,
TreeWalker,
Text
*/
/*jshint strict:false */
var inlineNodeNames = /^(?:#text|A(?:BBR|CRONYM)?|B(?:R|D[IO])?|C(?:ITE|ODE)|D(?:ATA|FN|EL)|EM|FONT|HR|I(?:NPUT|MG|NS)?|KBD|Q|R(?:P|T|UBY)|S(?:U[BP]|PAN|TR(?:IKE|ONG)|MALL|AMP)?|U|VAR|WBR)$/; var inlineNodeNames = /^(?:#text|A(?:BBR|CRONYM)?|B(?:R|D[IO])?|C(?:ITE|ODE)|D(?:ATA|FN|EL)|EM|FONT|HR|I(?:NPUT|MG|NS)?|KBD|Q|R(?:P|T|UBY)|S(?:U[BP]|PAN|TR(?:IKE|ONG)|MALL|AMP)?|U|VAR|WBR)$/;
@ -282,6 +268,29 @@ function empty ( node ) {
return frag; return frag;
} }
function createElement ( doc, tag, props, children ) {
var el = doc.createElement( tag ),
attr, value, i, l;
if ( props instanceof Array ) {
children = props;
props = null;
}
if ( props ) {
for ( attr in props ) {
value = props[ attr ];
if ( value !== undefined ) {
el.setAttribute( attr, props[ attr ] );
}
}
}
if ( children ) {
for ( i = 0, l = children.length; i < l; i += 1 ) {
el.appendChild( children[i] );
}
}
return el;
}
function fixCursor ( node ) { function fixCursor ( node ) {
// In Webkit and Gecko, block level elements are collapsed and // In Webkit and Gecko, block level elements are collapsed and
// unfocussable if they have no content. To remedy this, a <BR> must be // unfocussable if they have no content. To remedy this, a <BR> must be
@ -289,7 +298,8 @@ function fixCursor ( node ) {
// cursor to appear. // cursor to appear.
var doc = node.ownerDocument, var doc = node.ownerDocument,
root = node, root = node,
fixer, child; fixer, child,
l, instance;
if ( node.nodeName === 'BODY' ) { if ( node.nodeName === 'BODY' ) {
if ( !( child = node.firstChild ) || child.nodeName === 'BR' ) { if ( !( child = node.firstChild ) || child.nodeName === 'BR' ) {
@ -315,8 +325,13 @@ function fixCursor ( node ) {
if ( !child ) { if ( !child ) {
if ( cantFocusEmptyTextNodes ) { if ( cantFocusEmptyTextNodes ) {
fixer = doc.createTextNode( '\u200B' ); fixer = doc.createTextNode( '\u200B' );
if ( win.editor ) { // Find the relevant Squire instance and notify
win.editor._didAddZWS(); l = instances.length;
while ( l-- ) {
instance = instances[l];
if ( instance._doc === doc ) {
instance._didAddZWS();
}
} }
} else { } else {
fixer = doc.createTextNode( '' ); fixer = doc.createTextNode( '' );
@ -356,6 +371,42 @@ function fixCursor ( node ) {
return root; return root;
} }
// Recursively examine container nodes and wrap any inline children.
function fixContainer ( container ) {
var children = container.childNodes,
doc = container.ownerDocument,
wrapper = null,
i, l, child, isBR;
for ( i = 0, l = children.length; i < l; i += 1 ) {
child = children[i];
isBR = child.nodeName === 'BR';
if ( !isBR && isInline( child ) ) {
if ( !wrapper ) { wrapper = createElement( doc, 'DIV' ); }
wrapper.appendChild( child );
i -= 1;
l -= 1;
} else if ( isBR || wrapper ) {
if ( !wrapper ) { wrapper = createElement( doc, 'DIV' ); }
fixCursor( wrapper );
if ( isBR ) {
container.replaceChild( wrapper, child );
} else {
container.insertBefore( wrapper, child );
i += 1;
l += 1;
}
wrapper = null;
}
if ( isContainer( child ) ) {
fixContainer( child );
}
}
if ( wrapper ) {
container.appendChild( fixCursor( wrapper ) );
}
return container;
}
function split ( node, offset, stopNode ) { function split ( node, offset, stopNode ) {
var nodeType = node.nodeType, var nodeType = node.nodeType,
parent, clone, next; parent, clone, next;
@ -539,90 +590,7 @@ function mergeContainers ( node ) {
fixCursor( prev ); fixCursor( prev );
} }
} }
/*jshint strict:false, undef:false, unused:false */
// Recursively examine container nodes and wrap any inline children.
function fixContainer ( container ) {
var children = container.childNodes,
doc = container.ownerDocument,
wrapper = null,
i, l, child, isBR;
for ( i = 0, l = children.length; i < l; i += 1 ) {
child = children[i];
isBR = child.nodeName === 'BR';
if ( !isBR && isInline( child ) ) {
if ( !wrapper ) { wrapper = createElement( doc, 'DIV' ); }
wrapper.appendChild( child );
i -= 1;
l -= 1;
} else if ( isBR || wrapper ) {
if ( !wrapper ) { wrapper = createElement( doc, 'DIV' ); }
fixCursor( wrapper );
if ( isBR ) {
container.replaceChild( wrapper, child );
} else {
container.insertBefore( wrapper, child );
i += 1;
l += 1;
}
wrapper = null;
}
if ( isContainer( child ) ) {
fixContainer( child );
}
}
if ( wrapper ) {
container.appendChild( fixCursor( wrapper ) );
}
return container;
}
function createElement ( doc, tag, props, children ) {
var el = doc.createElement( tag ),
attr, value, i, l;
if ( props instanceof Array ) {
children = props;
props = null;
}
if ( props ) {
for ( attr in props ) {
value = props[ attr ];
if ( value !== undefined ) {
el.setAttribute( attr, props[ attr ] );
}
}
}
if ( children ) {
for ( i = 0, l = children.length; i < l; i += 1 ) {
el.appendChild( children[i] );
}
}
return el;
}
/*global
ELEMENT_NODE,
TEXT_NODE,
SHOW_TEXT,
START_TO_START,
START_TO_END,
END_TO_END,
END_TO_START,
notWS,
indexOf,
TreeWalker,
isLeaf,
isInline,
isBlock,
getPreviousBlock,
getNextBlock,
getLength,
fixCursor,
split,
mergeWithBlock,
mergeContainers
*/
/*jshint strict:false */
var getNodeBefore = function ( node, offset ) { var getNodeBefore = function ( node, offset ) {
var children = node.childNodes; var children = node.childNodes;
@ -1115,71 +1083,9 @@ var expandRangeToBlockBoundaries = function ( range ) {
range.setEnd( parent, indexOf.call( parent.childNodes, end ) + 1 ); range.setEnd( parent, indexOf.call( parent.childNodes, end ) + 1 );
} }
}; };
/*global /*jshint strict:false, undef:false, unused:false */
DOCUMENT_POSITION_PRECEDING,
ELEMENT_NODE,
TEXT_NODE,
SHOW_ELEMENT,
SHOW_TEXT,
win,
isIOS,
isMac,
isGecko,
isIE8or9or10,
isIE8,
isOpera,
ctrlKey,
useTextFixer,
cantFocusEmptyTextNodes,
losesSelectionOnBlur,
hasBuggySplit,
notWS,
indexOf,
TreeWalker, var instances = [];
hasTagAttributes,
isLeaf,
isInline,
isBlock,
isContainer,
getBlockWalker,
getPreviousBlock,
getNextBlock,
getNearest,
getPath,
getLength,
detach,
replaceWith,
empty,
fixCursor,
split,
mergeInlines,
mergeWithBlock,
mergeContainers,
fixContainer,
createElement,
forEachTextNodeInRange,
getTextContentInRange,
insertNodeInRange,
extractContentsOfRange,
deleteContentsOfRange,
insertTreeFragmentIntoRange,
isNodeContainedInRange,
moveRangeBoundariesDownTree,
moveRangeBoundariesUpTree,
getStartBlockOfRange,
getEndBlockOfRange,
rangeDoesStartAtBlockBoundary,
rangeDoesEndAtBlockBoundary,
expandRangeToBlockBoundaries,
top,
console,
setTimeout
*/
/*jshint strict:false */
function Squire ( doc ) { function Squire ( doc ) {
var win = doc.defaultView; var win = doc.defaultView;
@ -1238,7 +1144,7 @@ function Squire ( doc ) {
// And even if the split is not at the end, the original node is removed // And even if the split is not at the end, the original node is removed
// from the document and replaced by another, rather than just having its // from the document and replaced by another, rather than just having its
// data shortened. // data shortened.
if ( hasBuggySplit ) { if ( hasBuggySplit( doc ) ) {
win.Text.prototype.splitText = function ( offset ) { win.Text.prototype.splitText = function ( offset ) {
var afterSplit = this.ownerDocument.createTextNode( var afterSplit = this.ownerDocument.createTextNode(
this.data.slice( offset ) ), this.data.slice( offset ) ),
@ -1265,6 +1171,8 @@ function Squire ( doc ) {
doc.execCommand( 'enableObjectResizing', false, 'false' ); doc.execCommand( 'enableObjectResizing', false, 'false' );
doc.execCommand( 'enableInlineTableEditing', false, 'false' ); doc.execCommand( 'enableInlineTableEditing', false, 'false' );
} catch ( error ) {} } catch ( error ) {}
instances.push( this );
} }
var proto = Squire.prototype; var proto = Squire.prototype;
@ -1338,6 +1246,12 @@ proto.destroy = function () {
doc.removeEventListener( type, this, false ); doc.removeEventListener( type, this, false );
} }
} }
var l = instances.length;
while ( l-- ) {
if ( instances[l] === this ) {
instances.splice( l, 1 );
}
}
}; };
proto.handleEvent = function ( event ) { proto.handleEvent = function ( event ) {
@ -3501,7 +3415,6 @@ proto.removeList = command( 'modifyBlocks', removeList );
proto.increaseListLevel = command( 'modifyBlocks', increaseListLevel ); proto.increaseListLevel = command( 'modifyBlocks', increaseListLevel );
proto.decreaseListLevel = command( 'modifyBlocks', decreaseListLevel ); proto.decreaseListLevel = command( 'modifyBlocks', decreaseListLevel );
/*global top, win, doc, Squire */
if ( top !== win ) { if ( top !== win ) {
win.editor = new Squire( doc ); win.editor = new Squire( doc );

File diff suppressed because one or more lines are too long

View file

@ -1,5 +1,4 @@
/*global doc, navigator */ /*jshint strict:false, undef:false, unused:false */
/*jshint strict:false */
var DOCUMENT_POSITION_PRECEDING = 2; // Node.DOCUMENT_POSITION_PRECEDING var DOCUMENT_POSITION_PRECEDING = 2; // Node.DOCUMENT_POSITION_PRECEDING
var ELEMENT_NODE = 1; // Node.ELEMENT_NODE; var ELEMENT_NODE = 1; // Node.ELEMENT_NODE;
@ -30,13 +29,13 @@ var ctrlKey = isMac ? 'meta-' : 'ctrl-';
var useTextFixer = isIE8or9or10 || isPresto; var useTextFixer = isIE8or9or10 || isPresto;
var cantFocusEmptyTextNodes = isIE8or9or10 || isWebKit; var cantFocusEmptyTextNodes = isIE8or9or10 || isWebKit;
var losesSelectionOnBlur = isIE8or9or10; var losesSelectionOnBlur = isIE8or9or10;
var hasBuggySplit = ( function () { var hasBuggySplit = function ( doc ) {
var div = doc.createElement( 'DIV' ), var div = doc.createElement( 'DIV' ),
text = doc.createTextNode( '12' ); text = doc.createTextNode( '12' );
div.appendChild( text ); div.appendChild( text );
text.splitText( 2 ); text.splitText( 2 );
return div.childNodes.length !== 2; return div.childNodes.length !== 2;
}() ); };
// Use [^ \t\r\n] instead of \S so that nbsp does not count as white-space // Use [^ \t\r\n] instead of \S so that nbsp does not count as white-space
var notWS = /[^ \t\r\n]/; var notWS = /[^ \t\r\n]/;

View file

@ -1,68 +1,6 @@
/*global /*jshint strict:false, undef:false, unused:false */
DOCUMENT_POSITION_PRECEDING,
ELEMENT_NODE,
TEXT_NODE,
SHOW_ELEMENT,
SHOW_TEXT,
win,
isIOS,
isMac,
isGecko,
isIE8or9or10,
isIE8,
isOpera,
ctrlKey,
useTextFixer,
cantFocusEmptyTextNodes,
losesSelectionOnBlur,
hasBuggySplit,
notWS,
indexOf,
TreeWalker, var instances = [];
hasTagAttributes,
isLeaf,
isInline,
isBlock,
isContainer,
getBlockWalker,
getPreviousBlock,
getNextBlock,
getNearest,
getPath,
getLength,
detach,
replaceWith,
empty,
fixCursor,
split,
mergeInlines,
mergeWithBlock,
mergeContainers,
fixContainer,
createElement,
forEachTextNodeInRange,
getTextContentInRange,
insertNodeInRange,
extractContentsOfRange,
deleteContentsOfRange,
insertTreeFragmentIntoRange,
isNodeContainedInRange,
moveRangeBoundariesDownTree,
moveRangeBoundariesUpTree,
getStartBlockOfRange,
getEndBlockOfRange,
rangeDoesStartAtBlockBoundary,
rangeDoesEndAtBlockBoundary,
expandRangeToBlockBoundaries,
top,
console,
setTimeout
*/
/*jshint strict:false */
function Squire ( doc ) { function Squire ( doc ) {
var win = doc.defaultView; var win = doc.defaultView;
@ -121,7 +59,7 @@ function Squire ( doc ) {
// And even if the split is not at the end, the original node is removed // And even if the split is not at the end, the original node is removed
// from the document and replaced by another, rather than just having its // from the document and replaced by another, rather than just having its
// data shortened. // data shortened.
if ( hasBuggySplit ) { if ( hasBuggySplit( doc ) ) {
win.Text.prototype.splitText = function ( offset ) { win.Text.prototype.splitText = function ( offset ) {
var afterSplit = this.ownerDocument.createTextNode( var afterSplit = this.ownerDocument.createTextNode(
this.data.slice( offset ) ), this.data.slice( offset ) ),
@ -148,6 +86,8 @@ function Squire ( doc ) {
doc.execCommand( 'enableObjectResizing', false, 'false' ); doc.execCommand( 'enableObjectResizing', false, 'false' );
doc.execCommand( 'enableInlineTableEditing', false, 'false' ); doc.execCommand( 'enableInlineTableEditing', false, 'false' );
} catch ( error ) {} } catch ( error ) {}
instances.push( this );
} }
var proto = Squire.prototype; var proto = Squire.prototype;
@ -221,6 +161,12 @@ proto.destroy = function () {
doc.removeEventListener( type, this, false ); doc.removeEventListener( type, this, false );
} }
} }
var l = instances.length;
while ( l-- ) {
if ( instances[l] === this ) {
instances.splice( l, 1 );
}
}
}; };
proto.handleEvent = function ( event ) { proto.handleEvent = function ( event ) {

View file

@ -1,17 +1,4 @@
/*global /*jshint strict:false, undef:false, unused:false */
ELEMENT_NODE,
TEXT_NODE,
SHOW_ELEMENT,
win,
isOpera,
useTextFixer,
cantFocusEmptyTextNodes,
TreeWalker,
Text
*/
/*jshint strict:false */
var inlineNodeNames = /^(?:#text|A(?:BBR|CRONYM)?|B(?:R|D[IO])?|C(?:ITE|ODE)|D(?:ATA|FN|EL)|EM|FONT|HR|I(?:NPUT|MG|NS)?|KBD|Q|R(?:P|T|UBY)|S(?:U[BP]|PAN|TR(?:IKE|ONG)|MALL|AMP)?|U|VAR|WBR)$/; var inlineNodeNames = /^(?:#text|A(?:BBR|CRONYM)?|B(?:R|D[IO])?|C(?:ITE|ODE)|D(?:ATA|FN|EL)|EM|FONT|HR|I(?:NPUT|MG|NS)?|KBD|Q|R(?:P|T|UBY)|S(?:U[BP]|PAN|TR(?:IKE|ONG)|MALL|AMP)?|U|VAR|WBR)$/;
@ -143,6 +130,29 @@ function empty ( node ) {
return frag; return frag;
} }
function createElement ( doc, tag, props, children ) {
var el = doc.createElement( tag ),
attr, value, i, l;
if ( props instanceof Array ) {
children = props;
props = null;
}
if ( props ) {
for ( attr in props ) {
value = props[ attr ];
if ( value !== undefined ) {
el.setAttribute( attr, props[ attr ] );
}
}
}
if ( children ) {
for ( i = 0, l = children.length; i < l; i += 1 ) {
el.appendChild( children[i] );
}
}
return el;
}
function fixCursor ( node ) { function fixCursor ( node ) {
// In Webkit and Gecko, block level elements are collapsed and // In Webkit and Gecko, block level elements are collapsed and
// unfocussable if they have no content. To remedy this, a <BR> must be // unfocussable if they have no content. To remedy this, a <BR> must be
@ -150,7 +160,8 @@ function fixCursor ( node ) {
// cursor to appear. // cursor to appear.
var doc = node.ownerDocument, var doc = node.ownerDocument,
root = node, root = node,
fixer, child; fixer, child,
l, instance;
if ( node.nodeName === 'BODY' ) { if ( node.nodeName === 'BODY' ) {
if ( !( child = node.firstChild ) || child.nodeName === 'BR' ) { if ( !( child = node.firstChild ) || child.nodeName === 'BR' ) {
@ -176,8 +187,13 @@ function fixCursor ( node ) {
if ( !child ) { if ( !child ) {
if ( cantFocusEmptyTextNodes ) { if ( cantFocusEmptyTextNodes ) {
fixer = doc.createTextNode( '\u200B' ); fixer = doc.createTextNode( '\u200B' );
if ( win.editor ) { // Find the relevant Squire instance and notify
win.editor._didAddZWS(); l = instances.length;
while ( l-- ) {
instance = instances[l];
if ( instance._doc === doc ) {
instance._didAddZWS();
}
} }
} else { } else {
fixer = doc.createTextNode( '' ); fixer = doc.createTextNode( '' );
@ -217,6 +233,42 @@ function fixCursor ( node ) {
return root; return root;
} }
// Recursively examine container nodes and wrap any inline children.
function fixContainer ( container ) {
var children = container.childNodes,
doc = container.ownerDocument,
wrapper = null,
i, l, child, isBR;
for ( i = 0, l = children.length; i < l; i += 1 ) {
child = children[i];
isBR = child.nodeName === 'BR';
if ( !isBR && isInline( child ) ) {
if ( !wrapper ) { wrapper = createElement( doc, 'DIV' ); }
wrapper.appendChild( child );
i -= 1;
l -= 1;
} else if ( isBR || wrapper ) {
if ( !wrapper ) { wrapper = createElement( doc, 'DIV' ); }
fixCursor( wrapper );
if ( isBR ) {
container.replaceChild( wrapper, child );
} else {
container.insertBefore( wrapper, child );
i += 1;
l += 1;
}
wrapper = null;
}
if ( isContainer( child ) ) {
fixContainer( child );
}
}
if ( wrapper ) {
container.appendChild( fixCursor( wrapper ) );
}
return container;
}
function split ( node, offset, stopNode ) { function split ( node, offset, stopNode ) {
var nodeType = node.nodeType, var nodeType = node.nodeType,
parent, clone, next; parent, clone, next;
@ -400,62 +452,3 @@ function mergeContainers ( node ) {
fixCursor( prev ); fixCursor( prev );
} }
} }
// Recursively examine container nodes and wrap any inline children.
function fixContainer ( container ) {
var children = container.childNodes,
doc = container.ownerDocument,
wrapper = null,
i, l, child, isBR;
for ( i = 0, l = children.length; i < l; i += 1 ) {
child = children[i];
isBR = child.nodeName === 'BR';
if ( !isBR && isInline( child ) ) {
if ( !wrapper ) { wrapper = createElement( doc, 'DIV' ); }
wrapper.appendChild( child );
i -= 1;
l -= 1;
} else if ( isBR || wrapper ) {
if ( !wrapper ) { wrapper = createElement( doc, 'DIV' ); }
fixCursor( wrapper );
if ( isBR ) {
container.replaceChild( wrapper, child );
} else {
container.insertBefore( wrapper, child );
i += 1;
l += 1;
}
wrapper = null;
}
if ( isContainer( child ) ) {
fixContainer( child );
}
}
if ( wrapper ) {
container.appendChild( fixCursor( wrapper ) );
}
return container;
}
function createElement ( doc, tag, props, children ) {
var el = doc.createElement( tag ),
attr, value, i, l;
if ( props instanceof Array ) {
children = props;
props = null;
}
if ( props ) {
for ( attr in props ) {
value = props[ attr ];
if ( value !== undefined ) {
el.setAttribute( attr, props[ attr ] );
}
}
}
if ( children ) {
for ( i = 0, l = children.length; i < l; i += 1 ) {
el.appendChild( children[i] );
}
}
return el;
}

View file

@ -1,28 +1,4 @@
/*global /*jshint strict:false, undef:false, unused:false */
ELEMENT_NODE,
TEXT_NODE,
SHOW_TEXT,
START_TO_START,
START_TO_END,
END_TO_END,
END_TO_START,
notWS,
indexOf,
TreeWalker,
isLeaf,
isInline,
isBlock,
getPreviousBlock,
getNextBlock,
getLength,
fixCursor,
split,
mergeWithBlock,
mergeContainers
*/
/*jshint strict:false */
var getNodeBefore = function ( node, offset ) { var getNodeBefore = function ( node, offset ) {
var children = node.childNodes; var children = node.childNodes;

View file

@ -1,4 +1,3 @@
/*global top, win, doc, Squire */
if ( top !== win ) { if ( top !== win ) {
win.editor = new Squire( doc ); win.editor = new Squire( doc );