mirror of
https://github.com/fastmail/Squire.git
synced 2024-12-22 15:23:29 -05:00
Update Squire & Implement Headings in Squire-UI
This commit is contained in:
parent
c582d365a5
commit
1c9f303e75
7 changed files with 318 additions and 312 deletions
File diff suppressed because one or more lines are too long
|
@ -124,6 +124,7 @@ $(document).ready(function() {
|
||||||
testQuote: editor.testPresenceinSelection(
|
testQuote: editor.testPresenceinSelection(
|
||||||
'increaseQuoteLevel', action, 'blockquote', (
|
'increaseQuoteLevel', action, 'blockquote', (
|
||||||
/>blockquote\b/)),
|
/>blockquote\b/)),
|
||||||
|
testHeading: editor.testPresenceinSelection('makeHeading', action, 'H1', (/>H1\b/)),
|
||||||
isNotValue: function (a) {return (a == action && this.value !== ''); }
|
isNotValue: function (a) {return (a == action && this.value !== ''); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -131,20 +132,20 @@ $(document).ready(function() {
|
||||||
editor.alignCenter = function () { editor.setTextAlignment('center'); };
|
editor.alignCenter = function () { editor.setTextAlignment('center'); };
|
||||||
editor.alignLeft = function () { editor.setTextAlignment('left'); };
|
editor.alignLeft = function () { editor.setTextAlignment('left'); };
|
||||||
editor.alignJustify = function () { editor.setTextAlignment('justify'); };
|
editor.alignJustify = function () { editor.setTextAlignment('justify'); };
|
||||||
editor.makeHeading = function () { editor.setFontSize('2em'); editor.bold(); };
|
|
||||||
|
|
||||||
if (test.testBold | test.testItalic | test.testUnderline | test.testOrderedList | test.testLink | test.testQuote) {
|
if (test.testBold | test.testItalic | test.testUnderline | test.testOrderedList | test.testLink | test.testQuote | test.testHeading) {
|
||||||
if (test.testBold) editor.removeBold();
|
if (test.testBold) editor.removeBold();
|
||||||
if (test.testItalic) editor.removeItalic();
|
if (test.testItalic) editor.removeItalic();
|
||||||
if (test.testUnderline) editor.removeUnderline();
|
if (test.testUnderline) editor.removeUnderline();
|
||||||
if (test.testLink) editor.removeLink();
|
if (test.testLink) editor.removeLink();
|
||||||
if (test.testOrderedList) editor.removeList();
|
if (test.testOrderedList) editor.removeList();
|
||||||
if (test.testQuote) editor.decreaseQuoteLevel();
|
if (test.testQuote) editor.decreaseQuoteLevel();
|
||||||
|
if (test.testHeading) editor.removeHeading();
|
||||||
} else if (test.isNotValue('makeLink') | test.isNotValue('insertImage') | test.isNotValue('selectFont')) {
|
} else if (test.isNotValue('makeLink') | test.isNotValue('insertImage') | test.isNotValue('selectFont')) {
|
||||||
// do nothing these are dropdowns.
|
// do nothing these are dropdowns.
|
||||||
} else {
|
} else {
|
||||||
if (editor.getSelectedText() === '' && !(action == 'insertImage' || action == 'makeOrderedList' || action == 'increaseQuoteLevel' || action == 'redo' || action == 'undo')) return;
|
if (editor.getSelectedText() === '' && !(action == 'insertImage' || action == 'makeOrderedList' || action == 'increaseQuoteLevel' || action == 'redo' || action == 'undo' || action == "makeHeading")) return;
|
||||||
editor[action]();
|
editor[action]();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -49,9 +49,6 @@
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!--[if IE 8]>
|
|
||||||
<script type="text/javascript" src="ie8.js"></script>
|
|
||||||
<![endif]-->
|
|
||||||
<script type="text/javascript" src="squire.js"></script>
|
<script type="text/javascript" src="squire.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
20
build/font-awesome/font-awesome.min.css
vendored
20
build/font-awesome/font-awesome.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -3,16 +3,13 @@
|
||||||
( 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;
|
||||||
var TEXT_NODE = 3; // Node.TEXT_NODE;
|
var TEXT_NODE = 3; // Node.TEXT_NODE;
|
||||||
var SHOW_ELEMENT = 1; // NodeFilter.SHOW_ELEMENT;
|
var SHOW_ELEMENT = 1; // NodeFilter.SHOW_ELEMENT;
|
||||||
var SHOW_TEXT = 4; // NodeFilter.SHOW_TEXT;
|
var SHOW_TEXT = 4; // NodeFilter.SHOW_TEXT;
|
||||||
var FILTER_ACCEPT = 1; // NodeFilter.FILTER_ACCEPT;
|
|
||||||
var FILTER_SKIP = 3; // NodeFilter.FILTER_SKIP;
|
|
||||||
|
|
||||||
var START_TO_START = 0; // Range.START_TO_START
|
var START_TO_START = 0; // Range.START_TO_START
|
||||||
var START_TO_END = 1; // Range.START_TO_END
|
var START_TO_END = 1; // Range.START_TO_END
|
||||||
|
@ -29,27 +26,26 @@ var isMac = /Mac OS X/.test( ua );
|
||||||
var isGecko = /Gecko\//.test( ua );
|
var isGecko = /Gecko\//.test( ua );
|
||||||
var isIE8or9or10 = /Trident\/[456]\./.test( ua );
|
var isIE8or9or10 = /Trident\/[456]\./.test( ua );
|
||||||
var isIE8 = ( win.ie === 8 );
|
var isIE8 = ( win.ie === 8 );
|
||||||
var isOpera = !!win.opera;
|
var isPresto = !!win.opera;
|
||||||
var isWebKit = /WebKit\//.test( ua );
|
var isWebKit = /WebKit\//.test( ua );
|
||||||
|
|
||||||
var ctrlKey = isMac ? 'meta-' : 'ctrl-';
|
var ctrlKey = isMac ? 'meta-' : 'ctrl-';
|
||||||
|
|
||||||
var useTextFixer = isIE8or9or10 || isOpera;
|
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]/;
|
||||||
|
|
||||||
var indexOf = Array.prototype.indexOf;
|
var indexOf = Array.prototype.indexOf;
|
||||||
/*global FILTER_ACCEPT */
|
|
||||||
/*jshint strict:false */
|
/*jshint strict:false */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -103,7 +99,7 @@ TreeWalker.prototype.nextNode = function () {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if ( ( typeToBitArray[ node.nodeType ] & nodeType ) &&
|
if ( ( typeToBitArray[ node.nodeType ] & nodeType ) &&
|
||||||
filter( node ) === FILTER_ACCEPT ) {
|
filter( node ) ) {
|
||||||
this.currentNode = node;
|
this.currentNode = node;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -133,31 +129,16 @@ TreeWalker.prototype.previousNode = function () {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if ( ( typeToBitArray[ node.nodeType ] & nodeType ) &&
|
if ( ( typeToBitArray[ node.nodeType ] & nodeType ) &&
|
||||||
filter( node ) === FILTER_ACCEPT ) {
|
filter( node ) ) {
|
||||||
this.currentNode = node;
|
this.currentNode = node;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
current = node;
|
current = node;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/*global
|
/*jshint strict:false, undef:false, unused:false */
|
||||||
ELEMENT_NODE,
|
|
||||||
TEXT_NODE,
|
|
||||||
SHOW_ELEMENT,
|
|
||||||
FILTER_ACCEPT,
|
|
||||||
FILTER_SKIP,
|
|
||||||
win,
|
|
||||||
isOpera,
|
|
||||||
useTextFixer,
|
|
||||||
cantFocusEmptyTextNodes,
|
|
||||||
|
|
||||||
TreeWalker,
|
var inlineNodeNames = /^(?:#text|A(?:BBR|CRONYM)?|B(?:R|D[IO])?|C(?:ITE|ODE)|D(?:ATA|FN|EL)|EM|FONT|HR|H1|I(?:NPUT|MG|NS)?|KBD|Q|R(?:P|T|UBY)|S(?:U[BP]|PAN|TR(?:IKE|ONG)|MALL|AMP)?|U|VAR|WBR)$/;
|
||||||
|
|
||||||
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 leafNodeNames = {
|
var leafNodeNames = {
|
||||||
BR: 1,
|
BR: 1,
|
||||||
|
@ -214,13 +195,10 @@ function isContainer ( node ) {
|
||||||
!isInline( node ) && !isBlock( node );
|
!isInline( node ) && !isBlock( node );
|
||||||
}
|
}
|
||||||
|
|
||||||
function acceptIfBlock ( el ) {
|
|
||||||
return isBlock( el ) ? FILTER_ACCEPT : FILTER_SKIP;
|
|
||||||
}
|
|
||||||
function getBlockWalker ( node ) {
|
function getBlockWalker ( node ) {
|
||||||
var doc = node.ownerDocument,
|
var doc = node.ownerDocument,
|
||||||
walker = new TreeWalker(
|
walker = new TreeWalker(
|
||||||
doc.body, SHOW_ELEMENT, acceptIfBlock, false );
|
doc.body, SHOW_ELEMENT, isBlock, false );
|
||||||
walker.currentNode = node;
|
walker.currentNode = node;
|
||||||
return walker;
|
return walker;
|
||||||
}
|
}
|
||||||
|
@ -290,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
|
||||||
|
@ -297,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' ) {
|
||||||
|
@ -314,11 +316,22 @@ function fixCursor ( node ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isInline( node ) ) {
|
if ( isInline( node ) ) {
|
||||||
if ( !node.firstChild ) {
|
child = node.firstChild;
|
||||||
|
while ( cantFocusEmptyTextNodes && child &&
|
||||||
|
child.nodeType === TEXT_NODE && !child.data ) {
|
||||||
|
node.removeChild( child );
|
||||||
|
child = node.firstChild;
|
||||||
|
}
|
||||||
|
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( '' );
|
||||||
|
@ -358,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;
|
||||||
|
@ -499,7 +548,7 @@ function mergeWithBlock ( block, next, range ) {
|
||||||
// Steps to reproduce bug: Type "a-b-c" (where - is return)
|
// Steps to reproduce bug: Type "a-b-c" (where - is return)
|
||||||
// then backspace twice. The cursor goes to the top instead
|
// then backspace twice. The cursor goes to the top instead
|
||||||
// of after "b".
|
// of after "b".
|
||||||
if ( isOpera && ( last = block.lastChild ) && last.nodeName === 'BR' ) {
|
if ( isPresto && ( last = block.lastChild ) && last.nodeName === 'BR' ) {
|
||||||
block.removeChild( last );
|
block.removeChild( last );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -541,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,
|
|
||||||
FILTER_ACCEPT,
|
|
||||||
START_TO_START,
|
|
||||||
START_TO_END,
|
|
||||||
END_TO_END,
|
|
||||||
END_TO_START,
|
|
||||||
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;
|
||||||
|
@ -661,8 +627,8 @@ var forEachTextNodeInRange = function ( range, fn ) {
|
||||||
endContainer = range.endContainer,
|
endContainer = range.endContainer,
|
||||||
root = range.commonAncestorContainer,
|
root = range.commonAncestorContainer,
|
||||||
walker = new TreeWalker(
|
walker = new TreeWalker(
|
||||||
root, SHOW_TEXT, function ( node ) {
|
root, SHOW_TEXT, function (/* node */) {
|
||||||
return FILTER_ACCEPT;
|
return true;
|
||||||
}, false ),
|
}, false ),
|
||||||
textnode = walker.currentNode = startContainer;
|
textnode = walker.currentNode = startContainer;
|
||||||
|
|
||||||
|
@ -1053,50 +1019,56 @@ var getEndBlockOfRange = function ( range ) {
|
||||||
return block && isNodeContainedInRange( range, block, true ) ? block : null;
|
return block && isNodeContainedInRange( range, block, true ) ? block : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var contentWalker = new TreeWalker( null,
|
||||||
|
SHOW_TEXT|SHOW_ELEMENT,
|
||||||
|
function ( node ) {
|
||||||
|
return node.nodeType === TEXT_NODE ?
|
||||||
|
notWS.test( node.data ) :
|
||||||
|
node.nodeName === 'IMG';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
var rangeDoesStartAtBlockBoundary = function ( range ) {
|
var rangeDoesStartAtBlockBoundary = function ( range ) {
|
||||||
var startContainer = range.startContainer,
|
var startContainer = range.startContainer,
|
||||||
startOffset = range.startOffset,
|
startOffset = range.startOffset;
|
||||||
parent, child;
|
|
||||||
|
|
||||||
while ( isInline( startContainer ) ) {
|
// If in the middle or end of a text node, we're not at the boundary.
|
||||||
|
if ( startContainer.nodeType === TEXT_NODE ) {
|
||||||
if ( startOffset ) {
|
if ( startOffset ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
parent = startContainer.parentNode;
|
contentWalker.currentNode = startContainer;
|
||||||
startOffset = indexOf.call( parent.childNodes, startContainer );
|
} else {
|
||||||
startContainer = parent;
|
contentWalker.currentNode = getNodeAfter( startContainer, startOffset );
|
||||||
}
|
}
|
||||||
// Skip empty text nodes and <br>s.
|
|
||||||
while ( startOffset &&
|
// Otherwise, look for any previous content in the same block.
|
||||||
( child = startContainer.childNodes[ startOffset - 1 ] ) &&
|
contentWalker.root = getStartBlockOfRange( range );
|
||||||
( child.data === '' || child.nodeName === 'BR' ) ) {
|
|
||||||
startOffset -= 1;
|
return !contentWalker.previousNode();
|
||||||
}
|
|
||||||
return !startOffset;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var rangeDoesEndAtBlockBoundary = function ( range ) {
|
var rangeDoesEndAtBlockBoundary = function ( range ) {
|
||||||
var endContainer = range.endContainer,
|
var endContainer = range.endContainer,
|
||||||
endOffset = range.endOffset,
|
endOffset = range.endOffset,
|
||||||
length = getLength( endContainer ),
|
length;
|
||||||
parent, child;
|
|
||||||
|
|
||||||
while ( isInline( endContainer ) ) {
|
// If in a text node with content, and not at the end, we're not
|
||||||
if ( endOffset !== length ) {
|
// at the boundary
|
||||||
|
if ( endContainer.nodeType === TEXT_NODE ) {
|
||||||
|
length = endContainer.data.length;
|
||||||
|
if ( length && endOffset < length ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
parent = endContainer.parentNode;
|
contentWalker.currentNode = endContainer;
|
||||||
endOffset = indexOf.call( parent.childNodes, endContainer ) + 1;
|
} else {
|
||||||
endContainer = parent;
|
contentWalker.currentNode = getNodeBefore( endContainer, endOffset );
|
||||||
length = endContainer.childNodes.length;
|
|
||||||
}
|
}
|
||||||
// Skip empty text nodes and <br>s.
|
|
||||||
while ( endOffset < length &&
|
// Otherwise, look for any further content in the same block.
|
||||||
( child = endContainer.childNodes[ endOffset ] ) &&
|
contentWalker.root = getEndBlockOfRange( range );
|
||||||
( child.data === '' || child.nodeName === 'BR' ) ) {
|
|
||||||
endOffset += 1;
|
return !contentWalker.nextNode();
|
||||||
}
|
|
||||||
return endOffset === length;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var expandRangeToBlockBoundaries = function ( range ) {
|
var expandRangeToBlockBoundaries = function ( range ) {
|
||||||
|
@ -1111,73 +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,
|
|
||||||
FILTER_ACCEPT,
|
|
||||||
FILTER_SKIP,
|
|
||||||
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;
|
||||||
|
@ -1227,7 +1135,7 @@ function Squire ( doc ) {
|
||||||
this.addEventListener( 'keyup', this._ieSelAllClean );
|
this.addEventListener( 'keyup', this._ieSelAllClean );
|
||||||
}
|
}
|
||||||
// Opera does not fire keydown repeatedly.
|
// Opera does not fire keydown repeatedly.
|
||||||
this.addEventListener( isOpera ? 'keypress' : 'keydown', this._onKey );
|
this.addEventListener( isPresto ? 'keypress' : 'keydown', this._onKey );
|
||||||
|
|
||||||
// Fix IE8/9's buggy implementation of Text#splitText.
|
// Fix IE8/9's buggy implementation of Text#splitText.
|
||||||
// If the split is at the end of the node, it doesn't insert the newly split
|
// If the split is at the end of the node, it doesn't insert the newly split
|
||||||
|
@ -1235,7 +1143,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 ) ),
|
||||||
|
@ -1262,6 +1170,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;
|
||||||
|
@ -1323,6 +1233,26 @@ proto.fireEvent = function ( type, event ) {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
proto.destroy = function () {
|
||||||
|
var win = this._win,
|
||||||
|
doc = this._doc,
|
||||||
|
events = this._events,
|
||||||
|
type;
|
||||||
|
win.removeEventListener( 'focus', this, false );
|
||||||
|
win.removeEventListener( 'blur', this, false );
|
||||||
|
for ( type in events ) {
|
||||||
|
if ( !customEvents[ type ] ) {
|
||||||
|
doc.removeEventListener( type, this, true );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var l = instances.length;
|
||||||
|
while ( l-- ) {
|
||||||
|
if ( instances[l] === this ) {
|
||||||
|
instances.splice( l, 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
proto.handleEvent = function ( event ) {
|
proto.handleEvent = function ( event ) {
|
||||||
this.fireEvent( event.type, event );
|
this.fireEvent( event.type, event );
|
||||||
};
|
};
|
||||||
|
@ -1339,7 +1269,7 @@ proto.addEventListener = function ( type, fn ) {
|
||||||
if ( !handlers ) {
|
if ( !handlers ) {
|
||||||
handlers = this._events[ type ] = [];
|
handlers = this._events[ type ] = [];
|
||||||
if ( !customEvents[ type ] ) {
|
if ( !customEvents[ type ] ) {
|
||||||
this._doc.addEventListener( type, this, false );
|
this._doc.addEventListener( type, this, true );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handlers.push( fn );
|
handlers.push( fn );
|
||||||
|
@ -1453,7 +1383,7 @@ proto._removeZWS = function () {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var walker = new TreeWalker( this._body, SHOW_TEXT, function () {
|
var walker = new TreeWalker( this._body, SHOW_TEXT, function () {
|
||||||
return FILTER_ACCEPT;
|
return true;
|
||||||
}, false ),
|
}, false ),
|
||||||
node, index;
|
node, index;
|
||||||
while ( node = walker.nextNode() ) {
|
while ( node = walker.nextNode() ) {
|
||||||
|
@ -1493,11 +1423,10 @@ proto._updatePathOnEvent = function () {
|
||||||
// --- Focus ---
|
// --- Focus ---
|
||||||
|
|
||||||
proto.focus = function () {
|
proto.focus = function () {
|
||||||
// FF seems to need the body to be focussed
|
// FF seems to need the body to be focussed (at least on first load).
|
||||||
// (at least on first load).
|
// Chrome also now needs body to be focussed in order to show the cursor
|
||||||
if ( isGecko ) {
|
// (otherwise it is focussed, but the cursor doesn't appear).
|
||||||
this._body.focus();
|
this._body.focus();
|
||||||
}
|
|
||||||
this._win.focus();
|
this._win.focus();
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
@ -1710,8 +1639,7 @@ proto.hasFormat = function ( tag, attributes, range ) {
|
||||||
// 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 = new TreeWalker( root, SHOW_TEXT, function ( node ) {
|
walker = new TreeWalker( root, SHOW_TEXT, function ( node ) {
|
||||||
return isNodeContainedInRange( range, node, true ) ?
|
return isNodeContainedInRange( range, node, true );
|
||||||
FILTER_ACCEPT : FILTER_SKIP;
|
|
||||||
}, false );
|
}, false );
|
||||||
|
|
||||||
var seenNode = false;
|
var seenNode = false;
|
||||||
|
@ -1729,7 +1657,7 @@ proto._addFormat = function ( tag, attributes, range ) {
|
||||||
// If the range is collapsed we simply insert the node by wrapping
|
// If the range is collapsed we simply insert the node by wrapping
|
||||||
// it round the range and focus it.
|
// it round the range and focus it.
|
||||||
var el, walker, startContainer, endContainer, startOffset, endOffset,
|
var el, walker, startContainer, endContainer, startOffset, endOffset,
|
||||||
textnode, needsFormat;
|
textNode, needsFormat;
|
||||||
|
|
||||||
if ( range.collapsed ) {
|
if ( range.collapsed ) {
|
||||||
el = fixCursor( this.createElement( tag, attributes ) );
|
el = fixCursor( this.createElement( tag, attributes ) );
|
||||||
|
@ -1750,47 +1678,54 @@ proto._addFormat = function ( tag, attributes, range ) {
|
||||||
range.commonAncestorContainer,
|
range.commonAncestorContainer,
|
||||||
SHOW_TEXT,
|
SHOW_TEXT,
|
||||||
function ( node ) {
|
function ( node ) {
|
||||||
return isNodeContainedInRange( range, node, true ) ?
|
return isNodeContainedInRange( range, node, true );
|
||||||
FILTER_ACCEPT : FILTER_SKIP;
|
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
// Start at the beginning node of the range and iterate through
|
// Start at the beginning node of the range and iterate through
|
||||||
// all the nodes in the range that need formatting.
|
// all the nodes in the range that need formatting.
|
||||||
startOffset = 0;
|
startContainer = range.startContainer;
|
||||||
endOffset = 0;
|
startOffset = range.startOffset;
|
||||||
textnode = walker.currentNode = range.startContainer;
|
endContainer = range.endContainer;
|
||||||
|
endOffset = range.endOffset;
|
||||||
|
|
||||||
if ( textnode.nodeType !== TEXT_NODE ) {
|
// Make sure we start inside a text node.
|
||||||
textnode = walker.nextNode();
|
walker.currentNode = startContainer;
|
||||||
|
if ( startContainer.nodeType !== TEXT_NODE ) {
|
||||||
|
startContainer = walker.nextNode();
|
||||||
|
startOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
needsFormat = !getNearest( textnode, tag, attributes );
|
textNode = walker.currentNode;
|
||||||
if ( textnode === range.endContainer ) {
|
needsFormat = !getNearest( textNode, tag, attributes );
|
||||||
if ( needsFormat && textnode.length > range.endOffset ) {
|
|
||||||
textnode.splitText( range.endOffset );
|
|
||||||
} else {
|
|
||||||
endOffset = range.endOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( textnode === range.startContainer ) {
|
|
||||||
if ( needsFormat && range.startOffset ) {
|
|
||||||
textnode = textnode.splitText( range.startOffset );
|
|
||||||
} else {
|
|
||||||
startOffset = range.startOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( needsFormat ) {
|
if ( needsFormat ) {
|
||||||
|
if ( textNode === endContainer &&
|
||||||
|
textNode.length > endOffset ) {
|
||||||
|
textNode.splitText( endOffset );
|
||||||
|
}
|
||||||
|
if ( textNode === startContainer && startOffset ) {
|
||||||
|
textNode = textNode.splitText( startOffset );
|
||||||
|
if ( endContainer === startContainer ) {
|
||||||
|
endContainer = textNode;
|
||||||
|
endOffset -= startOffset;
|
||||||
|
}
|
||||||
|
startContainer = textNode;
|
||||||
|
startOffset = 0;
|
||||||
|
}
|
||||||
el = this.createElement( tag, attributes );
|
el = this.createElement( tag, attributes );
|
||||||
replaceWith( textnode, el );
|
replaceWith( textNode, el );
|
||||||
el.appendChild( textnode );
|
el.appendChild( textNode );
|
||||||
endOffset = textnode.length;
|
|
||||||
}
|
}
|
||||||
endContainer = textnode;
|
} while ( walker.nextNode() );
|
||||||
if ( !startContainer ) { startContainer = endContainer; }
|
|
||||||
} while ( textnode = walker.nextNode() );
|
// Make sure we finish inside a text node. Otherwise offset may have
|
||||||
|
// changed.
|
||||||
|
if ( endContainer.nodeType !== TEXT_NODE ) {
|
||||||
|
endContainer = textNode;
|
||||||
|
endOffset = textNode.length;
|
||||||
|
}
|
||||||
|
|
||||||
// Now set the selection to as it was before
|
// Now set the selection to as it was before
|
||||||
range = this._createRange(
|
range = this._createRange(
|
||||||
|
@ -2125,6 +2060,7 @@ var makeOrderedList = function ( frag ) {
|
||||||
return frag;
|
return frag;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var removeList = function ( frag ) {
|
var removeList = function ( frag ) {
|
||||||
var lists = frag.querySelectorAll( 'UL, OL' ),
|
var lists = frag.querySelectorAll( 'UL, OL' ),
|
||||||
i, l, ll, list, listFrag, children, child;
|
i, l, ll, list, listFrag, children, child;
|
||||||
|
@ -2210,7 +2146,7 @@ var addLinks = function ( frag ) {
|
||||||
var doc = frag.ownerDocument,
|
var doc = frag.ownerDocument,
|
||||||
walker = new TreeWalker( frag, SHOW_TEXT,
|
walker = new TreeWalker( frag, SHOW_TEXT,
|
||||||
function ( node ) {
|
function ( node ) {
|
||||||
return getNearest( node, 'A' ) ? FILTER_SKIP : FILTER_ACCEPT;
|
return !getNearest( node, 'A' );
|
||||||
}, false ),
|
}, false ),
|
||||||
node, data, parent, match, index, endIndex, child;
|
node, data, parent, match, index, endIndex, child;
|
||||||
while ( node = walker.nextNode() ) {
|
while ( node = walker.nextNode() ) {
|
||||||
|
@ -2482,6 +2418,14 @@ var cleanTree = function ( node, allowStyles ) {
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// If we have just white space, it may still be important if it
|
||||||
|
// separates two inline nodes, e.g. "<a>link</a> <a>link</a>".
|
||||||
|
else if ( i && i + 1 < l &&
|
||||||
|
isInline( children[ i - 1 ] ) &&
|
||||||
|
isInline( children[ i + 1 ] ) ) {
|
||||||
|
child.data = ' ';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
node.removeChild( child );
|
node.removeChild( child );
|
||||||
i -= 1;
|
i -= 1;
|
||||||
|
@ -2492,10 +2436,9 @@ var cleanTree = function ( node, allowStyles ) {
|
||||||
};
|
};
|
||||||
|
|
||||||
var notWSTextNode = function ( node ) {
|
var notWSTextNode = function ( node ) {
|
||||||
return ( node.nodeType === ELEMENT_NODE ?
|
return node.nodeType === ELEMENT_NODE ?
|
||||||
node.nodeName === 'BR' :
|
node.nodeName === 'BR' :
|
||||||
notWS.test( node.data ) ) ?
|
notWS.test( node.data );
|
||||||
FILTER_ACCEPT : FILTER_SKIP;
|
|
||||||
};
|
};
|
||||||
var isLineBreak = function ( br ) {
|
var isLineBreak = function ( br ) {
|
||||||
var block = br.parentNode,
|
var block = br.parentNode,
|
||||||
|
@ -2539,17 +2482,23 @@ var cleanupBRs = function ( root ) {
|
||||||
block = block.parentNode;
|
block = block.parentNode;
|
||||||
}
|
}
|
||||||
// If this is not inside a block, replace it by wrapping
|
// If this is not inside a block, replace it by wrapping
|
||||||
// inlines in DIV.
|
// inlines in a <div>.
|
||||||
if ( !isBlock( block ) || !tagAfterSplit[ block.nodeName ] ) {
|
if ( !isBlock( block ) ) {
|
||||||
fixContainer( block );
|
fixContainer( block );
|
||||||
}
|
}
|
||||||
// If in a block we can split, split it instead, but only if there
|
|
||||||
// is actual text content in the block. Otherwise, the <br> is a
|
|
||||||
// placeholder to stop the block from collapsing, so we must leave
|
|
||||||
// it.
|
|
||||||
else {
|
else {
|
||||||
|
// If it doesn't break a line, just remove it; it's not doing
|
||||||
|
// anything useful. We'll add it back later if required by the
|
||||||
|
// browser. If it breaks a line, split the block or leave it as
|
||||||
|
// appropriate.
|
||||||
if ( brBreaksLine[l] ) {
|
if ( brBreaksLine[l] ) {
|
||||||
splitBlock( block, br.parentNode, br );
|
// If in a <div>, split, but anywhere else we might change
|
||||||
|
// the formatting too much (e.g. <li> -> to two list items!)
|
||||||
|
// so just play it safe and leave it.
|
||||||
|
if ( block.nodeName !== 'DIV' ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
split( br.parentNode, br, block.parentNode );
|
||||||
}
|
}
|
||||||
detach( br );
|
detach( br );
|
||||||
}
|
}
|
||||||
|
@ -2631,11 +2580,20 @@ proto._onPaste = function ( event ) {
|
||||||
startContainer = range.startContainer,
|
startContainer = range.startContainer,
|
||||||
startOffset = range.startOffset,
|
startOffset = range.startOffset,
|
||||||
endContainer = range.endContainer,
|
endContainer = range.endContainer,
|
||||||
endOffset = range.endOffset;
|
endOffset = range.endOffset,
|
||||||
|
startBlock = getStartBlockOfRange( range );
|
||||||
|
|
||||||
|
// Record undo checkpoint
|
||||||
|
self._recordUndoState( range );
|
||||||
|
self._getRangeAndRemoveBookmark( range );
|
||||||
|
|
||||||
|
// We need to position the pasteArea in the visible portion of the screen
|
||||||
|
// to stop the browser auto-scrolling.
|
||||||
var pasteArea = this.createElement( 'DIV', {
|
var pasteArea = this.createElement( 'DIV', {
|
||||||
style: 'position: absolute; overflow: hidden; top:' +
|
style: 'position: absolute; overflow: hidden; top:' +
|
||||||
(body.scrollTop + 30) + 'px; left: 0; width: 1px; height: 1px;'
|
( body.scrollTop +
|
||||||
|
( startBlock ? startBlock.getBoundingClientRect().top : 0 ) ) +
|
||||||
|
'px; left: 0; width: 1px; height: 1px;'
|
||||||
});
|
});
|
||||||
body.appendChild( pasteArea );
|
body.appendChild( pasteArea );
|
||||||
range.selectNodeContents( pasteArea );
|
range.selectNodeContents( pasteArea );
|
||||||
|
@ -2794,8 +2752,11 @@ var keyHandlers = {
|
||||||
if ( !range ) { return; }
|
if ( !range ) { return; }
|
||||||
|
|
||||||
// Save undo checkpoint and add any links in the preceding section.
|
// Save undo checkpoint and add any links in the preceding section.
|
||||||
|
// Remove any zws so we don't think there's content in an empty
|
||||||
|
// block.
|
||||||
self._recordUndoState( range );
|
self._recordUndoState( range );
|
||||||
addLinks( range.startContainer );
|
addLinks( range.startContainer );
|
||||||
|
self._removeZWS();
|
||||||
self._getRangeAndRemoveBookmark( range );
|
self._getRangeAndRemoveBookmark( range );
|
||||||
|
|
||||||
// Selected text is overwritten, therefore delete the contents
|
// Selected text is overwritten, therefore delete the contents
|
||||||
|
@ -2903,7 +2864,7 @@ var keyHandlers = {
|
||||||
// If you try to select the contents of a 'BR', FF will not let
|
// If you try to select the contents of a 'BR', FF will not let
|
||||||
// you type anything!
|
// you type anything!
|
||||||
if ( !child || child.nodeName === 'BR' ||
|
if ( !child || child.nodeName === 'BR' ||
|
||||||
( child.nodeType === TEXT_NODE && !isOpera ) ) {
|
( child.nodeType === TEXT_NODE && !isPresto ) ) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
nodeAfterSplit = child;
|
nodeAfterSplit = child;
|
||||||
|
@ -2928,11 +2889,13 @@ var keyHandlers = {
|
||||||
self._docWasChanged();
|
self._docWasChanged();
|
||||||
},
|
},
|
||||||
backspace: function ( self, event ) {
|
backspace: function ( self, event ) {
|
||||||
|
self._removeZWS();
|
||||||
|
// Record undo checkpoint.
|
||||||
var range = self.getSelection();
|
var range = self.getSelection();
|
||||||
|
self._recordUndoState( range );
|
||||||
|
self._getRangeAndRemoveBookmark( range );
|
||||||
// If not collapsed, delete contents
|
// If not collapsed, delete contents
|
||||||
if ( !range.collapsed ) {
|
if ( !range.collapsed ) {
|
||||||
self._recordUndoState( range );
|
|
||||||
self._getRangeAndRemoveBookmark( range );
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
deleteContentsOfRange( range );
|
deleteContentsOfRange( range );
|
||||||
self._ensureBottomLine();
|
self._ensureBottomLine();
|
||||||
|
@ -2941,8 +2904,6 @@ var keyHandlers = {
|
||||||
}
|
}
|
||||||
// If at beginning of block, merge with previous
|
// If at beginning of block, merge with previous
|
||||||
else if ( rangeDoesStartAtBlockBoundary( range ) ) {
|
else if ( rangeDoesStartAtBlockBoundary( range ) ) {
|
||||||
self._recordUndoState( range );
|
|
||||||
self._getRangeAndRemoveBookmark( range );
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
var current = getStartBlockOfRange( range ),
|
var current = getStartBlockOfRange( range ),
|
||||||
previous = current && getPreviousBlock( current );
|
previous = current && getPreviousBlock( current );
|
||||||
|
@ -2985,21 +2946,18 @@ var keyHandlers = {
|
||||||
// Otherwise, leave to browser but check afterwards whether it has
|
// Otherwise, leave to browser but check afterwards whether it has
|
||||||
// left behind an empty inline tag.
|
// left behind an empty inline tag.
|
||||||
else {
|
else {
|
||||||
var text = range.startContainer.data || '';
|
self.setSelection( range );
|
||||||
if ( !notWS.test( text.charAt( range.startOffset - 1 ) ) ) {
|
|
||||||
self._recordUndoState( range );
|
|
||||||
self._getRangeAndRemoveBookmark( range );
|
|
||||||
self.setSelection( range );
|
|
||||||
}
|
|
||||||
setTimeout( function () { afterDelete( self ); }, 0 );
|
setTimeout( function () { afterDelete( self ); }, 0 );
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'delete': function ( self, event ) {
|
'delete': function ( self, event ) {
|
||||||
|
self._removeZWS();
|
||||||
|
// Record undo checkpoint.
|
||||||
var range = self.getSelection();
|
var range = self.getSelection();
|
||||||
|
self._recordUndoState( range );
|
||||||
|
self._getRangeAndRemoveBookmark( range );
|
||||||
// If not collapsed, delete contents
|
// If not collapsed, delete contents
|
||||||
if ( !range.collapsed ) {
|
if ( !range.collapsed ) {
|
||||||
self._recordUndoState( range );
|
|
||||||
self._getRangeAndRemoveBookmark( range );
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
deleteContentsOfRange( range );
|
deleteContentsOfRange( range );
|
||||||
self._ensureBottomLine();
|
self._ensureBottomLine();
|
||||||
|
@ -3008,8 +2966,6 @@ var keyHandlers = {
|
||||||
}
|
}
|
||||||
// If at end of block, merge next into this block
|
// If at end of block, merge next into this block
|
||||||
else if ( rangeDoesEndAtBlockBoundary( range ) ) {
|
else if ( rangeDoesEndAtBlockBoundary( range ) ) {
|
||||||
self._recordUndoState( range );
|
|
||||||
self._getRangeAndRemoveBookmark( range );
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
var current = getStartBlockOfRange( range ),
|
var current = getStartBlockOfRange( range ),
|
||||||
next = current && getNextBlock( current );
|
next = current && getNextBlock( current );
|
||||||
|
@ -3038,17 +2994,12 @@ var keyHandlers = {
|
||||||
// Otherwise, leave to browser but check afterwards whether it has
|
// Otherwise, leave to browser but check afterwards whether it has
|
||||||
// left behind an empty inline tag.
|
// left behind an empty inline tag.
|
||||||
else {
|
else {
|
||||||
// Record undo point if deleting whitespace
|
self.setSelection( range );
|
||||||
var text = range.startContainer.data || '';
|
|
||||||
if ( !notWS.test( text.charAt( range.startOffset ) ) ) {
|
|
||||||
self._recordUndoState( range );
|
|
||||||
self._getRangeAndRemoveBookmark( range );
|
|
||||||
self.setSelection( range );
|
|
||||||
}
|
|
||||||
setTimeout( function () { afterDelete( self ); }, 0 );
|
setTimeout( function () { afterDelete( self ); }, 0 );
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tab: function ( self, event ) {
|
tab: function ( self, event ) {
|
||||||
|
self._removeZWS();
|
||||||
var range = self.getSelection(),
|
var range = self.getSelection(),
|
||||||
node, parent;
|
node, parent;
|
||||||
// If no selection and in an empty block
|
// If no selection and in an empty block
|
||||||
|
@ -3087,6 +3038,7 @@ var keyHandlers = {
|
||||||
self._removeZWS();
|
self._removeZWS();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Firefox incorrectly handles Cmd-left/Cmd-right on Mac:
|
// Firefox incorrectly handles Cmd-left/Cmd-right on Mac:
|
||||||
// it goes back/forward in history! Override to do the right
|
// it goes back/forward in history! Override to do the right
|
||||||
// thing.
|
// thing.
|
||||||
|
@ -3132,7 +3084,7 @@ proto._onKey = function ( event ) {
|
||||||
|
|
||||||
// On keypress, delete and '.' both have event.keyCode 46
|
// On keypress, delete and '.' both have event.keyCode 46
|
||||||
// Must check event.which to differentiate.
|
// Must check event.which to differentiate.
|
||||||
if ( isOpera && event.which === 46 ) {
|
if ( isPresto && event.which === 46 ) {
|
||||||
key = '.';
|
key = '.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3141,9 +3093,15 @@ proto._onKey = function ( event ) {
|
||||||
key = 'f' + ( code - 111 );
|
key = 'f' + ( code - 111 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( event.altKey ) { modifiers += 'alt-'; }
|
// We need to apply the backspace/delete handlers regardless of
|
||||||
if ( event.ctrlKey ) { modifiers += 'ctrl-'; }
|
// control key modifiers.
|
||||||
if ( event.metaKey ) { modifiers += 'meta-'; }
|
if ( key !== 'backspace' && key !== 'delete' ) {
|
||||||
|
if ( event.altKey ) { modifiers += 'alt-'; }
|
||||||
|
if ( event.ctrlKey ) { modifiers += 'ctrl-'; }
|
||||||
|
if ( event.metaKey ) { modifiers += 'meta-'; }
|
||||||
|
}
|
||||||
|
// However, on Windows, shift-delete is apparently "cut" (WTF right?), so
|
||||||
|
// we want to let the browser handle shift-delete.
|
||||||
if ( event.shiftKey ) { modifiers += 'shift-'; }
|
if ( event.shiftKey ) { modifiers += 'shift-'; }
|
||||||
|
|
||||||
key = modifiers + key;
|
key = modifiers + key;
|
||||||
|
@ -3454,6 +3412,23 @@ proto.setTextDirection = function ( direction ) {
|
||||||
return this.focus();
|
return this.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
proto.makeHeading = function () {
|
||||||
|
var range = this.getSelection();
|
||||||
|
this.changeFormat({
|
||||||
|
tag: 'H1',
|
||||||
|
}, null, range );
|
||||||
|
return this.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
proto.removeHeading = function () {
|
||||||
|
var range = this.getSelection();
|
||||||
|
this.changeFormat( null, {
|
||||||
|
tag: 'H1',
|
||||||
|
attributes: {}
|
||||||
|
}, range, false);
|
||||||
|
return this.focus();
|
||||||
|
};
|
||||||
|
|
||||||
proto.increaseQuoteLevel = command( 'modifyBlocks', increaseBlockQuoteLevel );
|
proto.increaseQuoteLevel = command( 'modifyBlocks', increaseBlockQuoteLevel );
|
||||||
proto.decreaseQuoteLevel = command( 'modifyBlocks', decreaseBlockQuoteLevel );
|
proto.decreaseQuoteLevel = command( 'modifyBlocks', decreaseBlockQuoteLevel );
|
||||||
|
|
||||||
|
@ -3463,7 +3438,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
Loading…
Reference in a new issue