Update to latest version of squire. fixes #30

Already update later version of Fomantic. fixes #30
This commit is contained in:
Max G 2020-03-29 23:08:22 +00:00
parent 6dfe753d38
commit 35605e54d9
2 changed files with 114 additions and 161 deletions

View File

@ -24,22 +24,19 @@ var win = doc.defaultView;
var ua = navigator.userAgent; var ua = navigator.userAgent;
var isAndroid = /Android/.test( ua ); var isAndroid = /Android/.test( ua );
var isIOS = /iP(?:ad|hone|od)/.test( ua );
var isMac = /Mac OS X/.test( ua ); var isMac = /Mac OS X/.test( ua );
var isWin = /Windows NT/.test( ua ); var isWin = /Windows NT/.test( ua );
var isIOS = /iP(?:ad|hone|od)/.test( ua ) ||
( isMac && !!navigator.maxTouchPoints );
var isGecko = /Gecko\//.test( ua ); var isGecko = /Gecko\//.test( ua );
var isIElt11 = /Trident\/[456]\./.test( ua );
var isPresto = !!win.opera;
var isEdge = /Edge\//.test( ua ); var isEdge = /Edge\//.test( ua );
var isWebKit = !isEdge && /WebKit\//.test( ua ); var isWebKit = !isEdge && /WebKit\//.test( ua );
var isIE = /Trident\/[4567]\./.test( ua ); var isIE = /Trident\/[4567]\./.test( ua );
var ctrlKey = isMac ? 'meta-' : 'ctrl-'; var ctrlKey = isMac ? 'meta-' : 'ctrl-';
var useTextFixer = isIElt11 || isPresto; var cantFocusEmptyTextNodes = isWebKit;
var cantFocusEmptyTextNodes = isIElt11 || isWebKit;
var losesSelectionOnBlur = isIElt11;
var canObserveMutations = typeof MutationObserver !== 'undefined'; var canObserveMutations = typeof MutationObserver !== 'undefined';
var canWeakMap = typeof WeakMap !== 'undefined'; var canWeakMap = typeof WeakMap !== 'undefined';
@ -49,15 +46,6 @@ var notWS = /[^ \t\r\n]/;
var indexOf = Array.prototype.indexOf; var indexOf = Array.prototype.indexOf;
// Polyfill for FF3.5
if ( !Object.create ) {
Object.create = function ( proto ) {
var F = function () {};
F.prototype = proto;
return new F();
};
}
/* /*
Native TreeWalker is buggy in IE and Opera: Native TreeWalker is buggy in IE and Opera:
* IE9/10 sometimes throw errors when calling TreeWalker#nextNode or * IE9/10 sometimes throw errors when calling TreeWalker#nextNode or
@ -390,7 +378,7 @@ function createElement ( doc, tag, props, children ) {
for ( attr in props ) { for ( attr in props ) {
value = props[ attr ]; value = props[ attr ];
if ( value !== undefined ) { if ( value !== undefined ) {
el.setAttribute( attr, props[ attr ] ); el.setAttribute( attr, value );
} }
} }
} }
@ -445,31 +433,10 @@ function fixCursor ( node, root ) {
fixer = doc.createTextNode( '' ); fixer = doc.createTextNode( '' );
} }
} }
} else { } else if ( !node.querySelector( 'BR' ) ) {
if ( useTextFixer ) { fixer = createElement( doc, 'BR' );
while ( node.nodeType !== TEXT_NODE && !isLeaf( node ) ) { while ( ( child = node.lastElementChild ) && !isInline( child ) ) {
child = node.firstChild; node = child;
if ( !child ) {
fixer = doc.createTextNode( '' );
break;
}
node = child;
}
if ( node.nodeType === TEXT_NODE ) {
// Opera will collapse the block element if it contains
// just spaces (but not if it contains no data at all).
if ( /^ +$/.test( node.data ) ) {
node.data = '';
}
} else if ( isLeaf( node ) ) {
node.parentNode.insertBefore( doc.createTextNode( '' ), node );
}
}
else if ( !node.querySelector( 'BR' ) ) {
fixer = createElement( doc, 'BR' );
while ( ( child = node.lastElementChild ) && !isInline( child ) ) {
node = child;
}
} }
} }
if ( fixer ) { if ( fixer ) {
@ -500,16 +467,14 @@ function fixContainer ( container, root ) {
isBR = child.nodeName === 'BR'; isBR = child.nodeName === 'BR';
if ( !isBR && isInline( child ) ) { if ( !isBR && isInline( child ) ) {
if ( !wrapper ) { if ( !wrapper ) {
wrapper = createElement( doc, wrapper = createElement( doc, 'div' );
config.blockTag, config.blockAttributes );
} }
wrapper.appendChild( child ); wrapper.appendChild( child );
i -= 1; i -= 1;
l -= 1; l -= 1;
} else if ( isBR || wrapper ) { } else if ( isBR || wrapper ) {
if ( !wrapper ) { if ( !wrapper ) {
wrapper = createElement( doc, wrapper = createElement( doc, 'div' );
config.blockTag, config.blockAttributes );
} }
fixCursor( wrapper, root ); fixCursor( wrapper, root );
if ( isBR ) { if ( isBR ) {
@ -680,18 +645,6 @@ function mergeWithBlock ( block, next, range, root ) {
range.setStart( block, offset ); range.setStart( block, offset );
range.collapse( true ); range.collapse( true );
mergeInlines( block, range ); mergeInlines( block, range );
// Opera inserts a BR if you delete the last piece of text
// in a block-level element. Unfortunately, it then gets
// confused when setting the selection subsequently and
// refuses to accept the range that finishes just before the
// BR. Removing the BR fixes the bug.
// Steps to reproduce bug: Type "a-b-c" (where - is return)
// then backspace twice. The cursor goes to the top instead
// of after "b".
if ( isPresto && ( last = block.lastChild ) && last.nodeName === 'BR' ) {
block.removeChild( last );
}
} }
function mergeContainers ( node, root ) { function mergeContainers ( node, root ) {
@ -921,6 +874,7 @@ var deleteContentsOfRange = function ( range, root ) {
// Contents of range will be deleted. // Contents of range will be deleted.
// After method, range will be around inserted content // After method, range will be around inserted content
var insertTreeFragmentIntoRange = function ( range, frag, root ) { var insertTreeFragmentIntoRange = function ( range, frag, root ) {
var firstInFragIsInline = frag.firstChild && isInline( frag.firstChild );
var node, block, blockContentsAfterSplit, stopPoint, container, offset; var node, block, blockContentsAfterSplit, stopPoint, container, offset;
var replaceBlock, firstBlockInFrag, nodeAfterSplit, nodeBeforeSplit; var replaceBlock, firstBlockInFrag, nodeAfterSplit, nodeBeforeSplit;
var tempRange; var tempRange;
@ -946,11 +900,17 @@ var insertTreeFragmentIntoRange = function ( range, frag, root ) {
// Merge the contents of the first block in the frag with the focused block. // Merge the contents of the first block in the frag with the focused block.
// If there are contents in the block after the focus point, collect this // If there are contents in the block after the focus point, collect this
// up to insert in the last block later. If the block is empty, replace // up to insert in the last block later. This preserves the style that was
// it instead of merging. // present in this bit of the page.
//
// If the block being inserted into is empty though, replace it instead of
// merging if the fragment had block contents.
// e.g. <blockquote><p>Foo</p></blockquote>
// This seems a reasonable approximation of user intent.
block = getStartBlockOfRange( range, root ); block = getStartBlockOfRange( range, root );
firstBlockInFrag = getNextBlock( frag, frag ); firstBlockInFrag = getNextBlock( frag, frag );
replaceBlock = !!block && isEmptyBlock( block ); replaceBlock = !firstInFragIsInline && !!block && isEmptyBlock( block );
if ( block && firstBlockInFrag && !replaceBlock && if ( block && firstBlockInFrag && !replaceBlock &&
// Don't merge table cells or PRE elements into block // Don't merge table cells or PRE elements into block
!getNearest( firstBlockInFrag, frag, 'PRE' ) && !getNearest( firstBlockInFrag, frag, 'PRE' ) &&
@ -1314,12 +1274,6 @@ var onKey = function ( event ) {
} }
} }
// On keypress, delete and '.' both have event.keyCode 46
// Must check event.which to differentiate.
if ( isPresto && event.which === 46 ) {
key = '.';
}
// Function keys // Function keys
if ( 111 < code && code < 124 ) { if ( 111 < code && code < 124 ) {
key = 'f' + ( code - 111 ); key = 'f' + ( code - 111 );
@ -1426,6 +1380,17 @@ var afterDelete = function ( self, range ) {
} }
}; };
var detachUneditableNode = function ( node, root ) {
var parent;
while (( parent = node.parentNode )) {
if ( parent === root || parent.isContentEditable ) {
break;
}
node = parent;
}
detach( node );
};
var handleEnter = function ( self, shiftKey, range ) { var handleEnter = function ( self, shiftKey, range ) {
var root = self._root; var root = self._root;
var block, parent, node, offset, nodeAfterSplit; var block, parent, node, offset, nodeAfterSplit;
@ -1571,7 +1536,7 @@ var handleEnter = function ( self, shiftKey, range ) {
// 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 && !isPresto ) ) { child.nodeType === TEXT_NODE ) {
break; break;
} }
nodeAfterSplit = child; nodeAfterSplit = child;
@ -1639,7 +1604,7 @@ var keyHandlers = {
if ( previous ) { if ( previous ) {
// If not editable, just delete whole block. // If not editable, just delete whole block.
if ( !previous.isContentEditable ) { if ( !previous.isContentEditable ) {
detach( previous ); detachUneditableNode( previous, root );
return; return;
} }
// Otherwise merge. // Otherwise merge.
@ -1706,7 +1671,7 @@ var keyHandlers = {
if ( next ) { if ( next ) {
// If not editable, just delete whole block. // If not editable, just delete whole block.
if ( !next.isContentEditable ) { if ( !next.isContentEditable ) {
detach( next ); detachUneditableNode( next, root );
return; return;
} }
// Otherwise merge. // Otherwise merge.
@ -1882,24 +1847,6 @@ var fontSizes = {
}; };
var styleToSemantic = { var styleToSemantic = {
backgroundColor: {
regexp: notWS,
replace: function ( doc, classNames, colour ) {
return createElement( doc, 'SPAN', {
'class': classNames.highlight,
style: 'background-color:' + colour
});
}
},
color: {
regexp: notWS,
replace: function ( doc, classNames, colour ) {
return createElement( doc, 'SPAN', {
'class': classNames.colour,
style: 'color:' + colour
});
}
},
fontWeight: { fontWeight: {
regexp: /^bold|^700/i, regexp: /^bold|^700/i,
replace: function ( doc ) { replace: function ( doc ) {
@ -1941,6 +1888,12 @@ var styleToSemantic = {
var replaceWithTag = function ( tag ) { var replaceWithTag = function ( tag ) {
return function ( node, parent ) { return function ( node, parent ) {
var el = createElement( node.ownerDocument, tag ); var el = createElement( node.ownerDocument, tag );
var attributes = node.attributes;
var i, l, attribute;
for ( i = 0, l = attributes.length; i < l; i += 1 ) {
attribute = attributes[i];
el.setAttribute( attribute.name, attribute.value );
}
parent.replaceChild( el, node ); parent.replaceChild( el, node );
el.appendChild( empty( node ) ); el.appendChild( empty( node ) );
return el; return el;
@ -1957,6 +1910,10 @@ var replaceStyles = function ( node, parent, config ) {
css = style[ attr ]; css = style[ attr ];
if ( css && converter.regexp.test( css ) ) { if ( css && converter.regexp.test( css ) ) {
el = converter.replace( doc, config.classNames, css ); el = converter.replace( doc, config.classNames, css );
if ( el.nodeName === node.nodeName &&
el.className === node.className ) {
continue;
}
if ( !newTreeTop ) { if ( !newTreeTop ) {
newTreeTop = el; newTreeTop = el;
} }
@ -1970,18 +1927,13 @@ var replaceStyles = function ( node, parent, config ) {
if ( newTreeTop ) { if ( newTreeTop ) {
newTreeBottom.appendChild( empty( node ) ); newTreeBottom.appendChild( empty( node ) );
if ( node.nodeName === 'SPAN' ) { node.appendChild( newTreeTop );
parent.replaceChild( newTreeTop, node );
} else {
node.appendChild( newTreeTop );
}
} }
return newTreeBottom || node; return newTreeBottom || node;
}; };
var stylesRewriters = { var stylesRewriters = {
P: replaceStyles,
SPAN: replaceStyles, SPAN: replaceStyles,
STRONG: replaceWithTag( 'B' ), STRONG: replaceWithTag( 'B' ),
EM: replaceWithTag( 'I' ), EM: replaceWithTag( 'I' ),
@ -2602,12 +2554,6 @@ function Squire ( root, config ) {
this._isFocused = false; this._isFocused = false;
this._lastSelection = null; this._lastSelection = null;
// IE loses selection state of iframe on blur, so make sure we
// cache it just before it loses focus.
if ( losesSelectionOnBlur ) {
this.addEventListener( 'beforedeactivate', this.getSelection );
}
this._hasZWS = false; this._hasZWS = false;
this._lastAnchorNode = null; this._lastAnchorNode = null;
@ -2654,15 +2600,13 @@ function Squire ( root, config ) {
// IE sometimes fires the beforepaste event twice; make sure it is not run // IE sometimes fires the beforepaste event twice; make sure it is not run
// again before our after paste function is called. // again before our after paste function is called.
this._awaitingPaste = false; this._awaitingPaste = false;
this.addEventListener( isIElt11 ? 'beforecut' : 'cut', onCut ); this.addEventListener( 'cut', onCut );
this.addEventListener( 'copy', onCopy ); this.addEventListener( 'copy', onCopy );
this.addEventListener( 'keydown', monitorShiftKey ); this.addEventListener( 'keydown', monitorShiftKey );
this.addEventListener( 'keyup', monitorShiftKey ); this.addEventListener( 'keyup', monitorShiftKey );
this.addEventListener( isIElt11 ? 'beforepaste' : 'paste', onPaste ); this.addEventListener( 'paste', onPaste );
this.addEventListener( 'drop', onDrop ); this.addEventListener( 'drop', onDrop );
this.addEventListener( 'keydown', onKey );
// Opera does not fire keydown repeatedly.
this.addEventListener( isPresto ? 'keypress' : 'keydown', onKey );
// Add key handlers // Add key handlers
this._keyHandlers = Object.create( keyHandlers ); this._keyHandlers = Object.create( keyHandlers );
@ -2670,35 +2614,6 @@ function Squire ( root, config ) {
// Override default properties // Override default properties
this.setConfig( config ); this.setConfig( config );
// Fix IE<10's buggy implementation of Text#splitText.
// If the split is at the end of the node, it doesn't insert the newly split
// node into the document, and sets its value to undefined rather than ''.
// 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
// data shortened.
// We used to feature test for this, but then found the feature test would
// sometimes pass, but later on the buggy behaviour would still appear.
// I think IE10 does not have the same bug, but it doesn't hurt to replace
// its native fn too and then we don't need yet another UA category.
if ( isIElt11 ) {
win.Text.prototype.splitText = function ( offset ) {
var afterSplit = this.ownerDocument.createTextNode(
this.data.slice( offset ) ),
next = this.nextSibling,
parent = this.parentNode,
toDelete = this.length - offset;
if ( next ) {
parent.insertBefore( afterSplit, next );
} else {
parent.appendChild( afterSplit );
}
if ( toDelete ) {
this.deleteData( offset, toDelete );
}
return afterSplit;
};
}
root.setAttribute( 'contenteditable', 'true' ); root.setAttribute( 'contenteditable', 'true' );
// Remove Firefox's built-in controls // Remove Firefox's built-in controls
@ -4218,24 +4133,7 @@ proto.getHTML = function ( withBookMark ) {
if ( withBookMark && ( range = this.getSelection() ) ) { if ( withBookMark && ( range = this.getSelection() ) ) {
this._saveRangeToBookmark( range ); this._saveRangeToBookmark( range );
} }
if ( useTextFixer ) {
root = this._root;
node = root;
while ( node = getNextBlock( node, root ) ) {
if ( !node.textContent && !node.querySelector( 'BR' ) ) {
fixer = this.createElement( 'BR' );
node.appendChild( fixer );
brs.push( fixer );
}
}
}
html = this._getHTML().replace( /\u200B/g, '' ); html = this._getHTML().replace( /\u200B/g, '' );
if ( useTextFixer ) {
l = brs.length;
while ( l-- ) {
detach( brs[l] );
}
}
if ( range ) { if ( range ) {
this._getRangeAndRemoveBookmark( range ); this._getRangeAndRemoveBookmark( range );
} }
@ -4356,7 +4254,57 @@ proto.insertImage = function ( src, attributes ) {
return img; return img;
}; };
proto.linkRegExp = /\b((?:(?:ht|f)tps?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,}\/)(?:[^\s()<>]+|\([^\s()<>]+\))+(?:\((?:[^\s()<>]+|(?:\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))|([\w\-.%+]+@(?:[\w\-]+\.)+[A-Z]{2,}\b)(?:\?[^&?\s]+=[^&?\s]+(?:&[^&?\s]+=[^&?\s]+)*)?/i; /*
const linkRegExp = new RegExp(
// Only look on boundaries
'\\b(?:' +
// Capture group 1: URLs
'(' +
// Add links to URLS
// Starts with:
'(?:' +
// http(s):// or ftp://
'(?:ht|f)tps?:\\/\\/' +
// or
'|' +
// www.
'www\\d{0,3}[.]' +
// or
'|' +
// foo90.com/
'[a-z0-9][a-z0-9.\\-]*[.][a-z]{2,}\\/' +
')' +
// Then we get one or more:
'(?:' +
// Run of non-spaces, non ()<>
'[^\\s()<>]+' +
// or
'|' +
// balanced parentheses (one level deep only)
'\\([^\\s()<>]+\\)' +
')+' +
// And we finish with
'(?:' +
// Not a space or punctuation character
'[^\\s?&`!()\\[\\]{};:\'".,<>«»“”‘’]' +
// or
'|' +
// Balanced parentheses.
'\\([^\\s()<>]+\\)' +
')' +
// Capture group 2: Emails
')|(' +
// Add links to emails
'[\\w\\-.%+]+@(?:[\\w\\-]+\\.)+[a-z]{2,}\\b' +
// Allow query parameters in the mailto: style
'(?:' +
'[?][^&?\\s]+=[^\\s?&`!()\\[\\]{};:\'".,<>«»“”‘’]+' +
'(?:&[^&?\\s]+=[^\\s?&`!()\\[\\]{};:\'".,<>«»“”‘’]+)*' +
')?' +
'))', 'i' );
*/
proto.linkRegExp = /\b(?:((?:(?:ht|f)tps?:\/\/|www\d{0,3}[.]|[a-z0-9][a-z0-9.\-]*[.][a-z]{2,}\/)(?:[^\s()<>]+|\([^\s()<>]+\))+(?:[^\s?&`!()\[\]{};:'".,<>«»“”‘’]|\([^\s()<>]+\)))|([\w\-.%+]+@(?:[\w\-]+\.)+[a-z]{2,}\b(?:[?][^&?\s]+=[^\s?&`!()\[\]{};:'".,<>«»“”‘’]+(?:&[^&?\s]+=[^\s?&`!()\[\]{};:'".,<>«»“”‘’]+)*)?))/i;
var addLinks = function ( frag, root, self ) { var addLinks = function ( frag, root, self ) {
var doc = frag.ownerDocument; var doc = frag.ownerDocument;
@ -4482,11 +4430,11 @@ proto.insertHTML = function ( html, isPaste ) {
return this; return this;
}; };
var escapeHTMLFragement = function ( text ) { var escapeHTML = function ( text ) {
return text.split( '&' ).join( '&amp;' ) return text.split( '&' ).join( '&amp;' )
.split( '<' ).join( '&lt;' ) .split( '<' ).join( '&lt;' )
.split( '>' ).join( '&gt;' ) .split( '>' ).join( '&gt;' )
.split( '"' ).join( '&quot;' ); .split( '"' ).join( '&quot;' );
}; };
proto.insertPlainText = function ( plainText, isPaste ) { proto.insertPlainText = function ( plainText, isPaste ) {
@ -4532,16 +4480,21 @@ proto.insertPlainText = function ( plainText, isPaste ) {
for ( attr in attributes ) { for ( attr in attributes ) {
openBlock += ' ' + attr + '="' + openBlock += ' ' + attr + '="' +
escapeHTMLFragement( attributes[ attr ] ) + escapeHTML( attributes[ attr ] ) +
'"'; '"';
} }
openBlock += '>'; openBlock += '>';
for ( i = 0, l = lines.length; i < l; i += 1 ) { for ( i = 0, l = lines.length; i < l; i += 1 ) {
line = lines[i]; line = lines[i];
line = escapeHTMLFragement( line ).replace( / (?= )/g, '&nbsp;' ); line = escapeHTML( line ).replace( / (?= )/g, '&nbsp;' );
// We don't wrap the first line in the block, so if it gets inserted
// into a blank line it keeps that line's formatting.
// Wrap each line in <div></div> // Wrap each line in <div></div>
lines[i] = openBlock + ( line || '<BR>' ) + closeBlock; if ( i ) {
line = openBlock + ( line || '<BR>' ) + closeBlock;
}
lines[i] = line;
} }
return this.insertHTML( lines.join( '' ), isPaste ); return this.insertHTML( lines.join( '' ), isPaste );
}; };

View File

@ -65,10 +65,10 @@
} }
/*.fade-enter-active {*/ .fade-enter-active {
animation: fade-in .3s; animation: fade-in .3s;
} }
/*.fade-leave-active {*/ .fade-leave-active {
animation: fade-in .1s reverse; animation: fade-in .1s reverse;
} }
@keyframes fade-in { @keyframes fade-in {
@ -83,7 +83,7 @@
</style> </style>
<template> <template>
<!-- <transition name="fade"> --> <transition name="fade">
<div> <div>
<div class="slide-container" :style="{ 'background-color':bgColor, 'color':textColor}"> <div class="slide-container" :style="{ 'background-color':bgColor, 'color':textColor}">