mirror of
https://github.com/the-djmaze/snappymail.git
synced 2024-12-26 00:51:24 +08:00
Squire
* drop isWin and isEdge (legacy) * simplify isWebKit * some code to ES 2015
This commit is contained in:
parent
d4f55a02a4
commit
5efbcf5ad3
1 changed files with 61 additions and 122 deletions
183
vendors/squire/build/squire-raw.js
vendored
183
vendors/squire/build/squire-raw.js
vendored
|
@ -32,12 +32,10 @@ const
|
|||
ua = navigator.userAgent,
|
||||
|
||||
isMac = /Mac OS X/.test( ua ),
|
||||
isWin = /Windows NT/.test( ua ),
|
||||
isIOS = /iP(?:ad|hone|od)/.test( ua ) || ( isMac && !!navigator.maxTouchPoints ),
|
||||
|
||||
isGecko = /Gecko\//.test( ua ),
|
||||
isEdge = /Edge\//.test( ua ),
|
||||
isWebKit = !isEdge && /WebKit\//.test( ua ),
|
||||
isWebKit = /WebKit\//.test( ua ),
|
||||
|
||||
ctrlKey = isMac ? 'meta-' : 'ctrl-',
|
||||
osKey = isMac ? 'metaKey' : 'ctrlKey',
|
||||
|
@ -143,15 +141,7 @@ const
|
|||
);
|
||||
},
|
||||
hasTagAttributes = ( node, tag, attributes ) => {
|
||||
if ( node.nodeName !== tag ) {
|
||||
return false;
|
||||
}
|
||||
for ( let attr in attributes ) {
|
||||
if ( node.getAttribute( attr ) !== attributes[ attr ] ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return node.nodeName === tag && Object.entries(attributes).every(([k,v]) => node.getAttribute(k) === v);
|
||||
},
|
||||
getClosest = ( node, root, selector ) => {
|
||||
node = (!node || node.closest ? node : node.parentElement);
|
||||
|
@ -213,31 +203,25 @@ const
|
|||
|
||||
setStyle = ( node, style ) => {
|
||||
if (typeof style === 'object') {
|
||||
for ( let p in style ) {
|
||||
node.style[p] = style[p];
|
||||
}
|
||||
Object.entries(style).forEach(([k,v]) => node.style[k] = v);
|
||||
} else if ( style !== undefined ) {
|
||||
node.setAttribute( 'style', style );
|
||||
}
|
||||
},
|
||||
|
||||
createElement = ( tag, props, children ) => {
|
||||
let el = doc.createElement( tag ),
|
||||
attr, value;
|
||||
let el = doc.createElement( tag );
|
||||
if ( props instanceof Array ) {
|
||||
children = props;
|
||||
props = null;
|
||||
}
|
||||
if ( props ) {
|
||||
for ( attr in props ) {
|
||||
value = props[ attr ];
|
||||
if ('style' === attr) {
|
||||
setStyle( el, value );
|
||||
} else if ( value !== undefined ) {
|
||||
el.setAttribute( attr, value );
|
||||
}
|
||||
props && Object.entries(props).forEach(([k,v]) => {
|
||||
if ('style' === k) {
|
||||
setStyle( el, v );
|
||||
} else if ( v !== undefined ) {
|
||||
el.setAttribute( k, v );
|
||||
}
|
||||
}
|
||||
});
|
||||
children && el.append( ...children );
|
||||
return el;
|
||||
},
|
||||
|
@ -530,13 +514,12 @@ const
|
|||
|
||||
if ( prev && areAlike( prev, node ) ) {
|
||||
if ( !isContainer( prev ) ) {
|
||||
if ( isListItem ) {
|
||||
block = createElement( 'DIV' );
|
||||
block.append( empty( prev ) );
|
||||
prev.append( block );
|
||||
} else {
|
||||
if ( !isListItem ) {
|
||||
return;
|
||||
}
|
||||
block = createElement( 'DIV' );
|
||||
block.append( empty( prev ) );
|
||||
prev.append( block );
|
||||
}
|
||||
detach( node );
|
||||
needsFix = !isContainer( node );
|
||||
|
@ -727,11 +710,11 @@ const
|
|||
|
||||
// Ensure root has a block-level element in it.
|
||||
child = root.firstChild;
|
||||
if ( !child || child.nodeName === 'BR' ) {
|
||||
if ( child && child.nodeName !== 'BR' ) {
|
||||
range.collapse( true );
|
||||
} else {
|
||||
fixCursor( root, root );
|
||||
range.selectNodeContents( root.firstChild );
|
||||
} else {
|
||||
range.collapse( true );
|
||||
}
|
||||
return frag;
|
||||
},
|
||||
|
@ -861,24 +844,13 @@ const
|
|||
|
||||
nodeRange.selectNode( node );
|
||||
|
||||
if ( partial ) {
|
||||
// Node must not finish before range starts or start after range
|
||||
// finishes.
|
||||
let nodeEndBeforeStart = ( range.compareBoundaryPoints(
|
||||
END_TO_START, nodeRange ) > -1 ),
|
||||
nodeStartAfterEnd = ( range.compareBoundaryPoints(
|
||||
START_TO_END, nodeRange ) < 1 );
|
||||
return ( !nodeEndBeforeStart && !nodeStartAfterEnd );
|
||||
}
|
||||
else {
|
||||
// Node must start after range starts and finish before range
|
||||
// finishes
|
||||
let nodeStartAfterStart = ( range.compareBoundaryPoints(
|
||||
START_TO_START, nodeRange ) < 1 ),
|
||||
nodeEndBeforeEnd = ( range.compareBoundaryPoints(
|
||||
END_TO_END, nodeRange ) > -1 );
|
||||
return ( nodeStartAfterStart && nodeEndBeforeEnd );
|
||||
}
|
||||
return partial
|
||||
// Node must not finish before range starts or start after range finishes.
|
||||
? range.compareBoundaryPoints( END_TO_START, nodeRange ) < 0
|
||||
&& range.compareBoundaryPoints( START_TO_END, nodeRange ) > 0
|
||||
// Node must start after range starts and finish before range finishes
|
||||
: range.compareBoundaryPoints( START_TO_START, nodeRange ) < 1
|
||||
&& range.compareBoundaryPoints( END_TO_END, nodeRange ) > -1;
|
||||
},
|
||||
|
||||
moveRangeBoundariesDownTree = range => {
|
||||
|
@ -1002,8 +974,7 @@ const
|
|||
} else if ( container !== root && isBlock( container ) ) {
|
||||
block = container;
|
||||
} else {
|
||||
block = getNodeBefore( container, range.startOffset );
|
||||
block = getNextBlock( block, root );
|
||||
block = getNextBlock( getNodeBefore( container, range.startOffset ), root );
|
||||
}
|
||||
// Check the block actually intersects the range
|
||||
return block && isNodeContainedInRange( range, block ) ? block : null;
|
||||
|
@ -1414,16 +1385,15 @@ const
|
|||
|
||||
replaceStyles = node => {
|
||||
let style = node.style;
|
||||
let attr, converter, css, newTreeBottom, newTreeTop, el;
|
||||
let css, newTreeBottom, newTreeTop, el;
|
||||
|
||||
for ( attr in styleToSemantic ) {
|
||||
converter = styleToSemantic[ attr ];
|
||||
Object.entries(styleToSemantic).forEach(([attr,converter])=>{
|
||||
css = style[ attr ];
|
||||
if ( css && converter.regexp.test( css ) ) {
|
||||
el = converter.replace( doc, css );
|
||||
if ( el.nodeName === node.nodeName &&
|
||||
el.className === node.className ) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
if ( !newTreeTop ) {
|
||||
newTreeTop = el;
|
||||
|
@ -1434,7 +1404,7 @@ const
|
|||
newTreeBottom = el;
|
||||
node.style[ attr ] = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if ( newTreeTop ) {
|
||||
newTreeBottom.append( empty( node ) );
|
||||
|
@ -1681,13 +1651,6 @@ const
|
|||
text = text.replace( NBSP, ' ' ); // Replace nbsp with regular space
|
||||
node.remove( );
|
||||
|
||||
// Firefox (and others?) returns unix line endings (\n) even on Windows.
|
||||
// If on Windows, normalise to \r\n, since Notepad and some other crappy
|
||||
// apps do not understand just \n.
|
||||
if ( isWin ) {
|
||||
text = text.replace( /\r?\n/g, '\r\n' );
|
||||
}
|
||||
|
||||
if ( text !== html ) {
|
||||
clipboardData.setData( 'text/html', html );
|
||||
}
|
||||
|
@ -1696,20 +1659,14 @@ const
|
|||
},
|
||||
|
||||
mergeObjects = ( base, extras, mayOverride ) => {
|
||||
let prop, value;
|
||||
if ( !base ) {
|
||||
base = {};
|
||||
}
|
||||
if ( extras ) {
|
||||
for ( prop in extras ) {
|
||||
if ( mayOverride || !( prop in base ) ) {
|
||||
value = extras[ prop ];
|
||||
base[ prop ] = ( value && value.constructor === Object ) ?
|
||||
mergeObjects( base[ prop ], value, mayOverride ) :
|
||||
value;
|
||||
}
|
||||
base = base || {};
|
||||
extras && Object.entries(extras).forEach(([prop,value])=>{
|
||||
if ( mayOverride || !( prop in base ) ) {
|
||||
base[ prop ] = ( value && value.constructor === Object ) ?
|
||||
mergeObjects( base[ prop ], value, mayOverride ) :
|
||||
value;
|
||||
}
|
||||
}
|
||||
});
|
||||
return base;
|
||||
},
|
||||
|
||||
|
@ -1777,13 +1734,8 @@ const
|
|||
},
|
||||
|
||||
splitBlock = ( self, block, node, offset ) => {
|
||||
let splitTag = tagAfterSplit[ block.nodeName ],
|
||||
nodeAfterSplit = split( node, offset, block.parentNode, self._root ),
|
||||
config = self._config;
|
||||
|
||||
if ( !splitTag ) {
|
||||
splitTag = config.blockTag;
|
||||
}
|
||||
let splitTag = tagAfterSplit[ block.nodeName ] || self._config.blockTag,
|
||||
nodeAfterSplit = split( node, offset, block.parentNode, self._root );
|
||||
|
||||
// Make sure the new node is the correct type.
|
||||
if ( !hasTagAttributes( nodeAfterSplit, splitTag, {} ) ) {
|
||||
|
@ -1964,11 +1916,6 @@ function onKey ( event ) {
|
|||
if ( event[osKey] ) { modifiers += ctrlKey; }
|
||||
if ( event.shiftKey ) { modifiers += 'shift-'; }
|
||||
}
|
||||
// However, on Windows, shift-delete is apparently "cut" (WTF right?), so
|
||||
// we want to let the browser handle shift-delete in this situation.
|
||||
if ( isWin && event.shiftKey && key === 'delete' ) {
|
||||
modifiers += 'shift-';
|
||||
}
|
||||
|
||||
key = modifiers + key;
|
||||
|
||||
|
@ -2004,7 +1951,7 @@ function onCut ( event ) {
|
|||
this.saveUndoState( range );
|
||||
|
||||
// Edge only seems to support setting plain text as of 2016-03-11.
|
||||
if ( !isEdge && event.clipboardData ) {
|
||||
if ( event.clipboardData ) {
|
||||
// Clipboard content should include all parents within block, or all
|
||||
// parents up to root if selection across blocks
|
||||
startBlock = getStartBlockOfRange( range, root );
|
||||
|
@ -2041,7 +1988,7 @@ function onCut ( event ) {
|
|||
|
||||
function onCopy ( event ) {
|
||||
// Edge only seems to support setting plain text as of 2016-03-11.
|
||||
if ( !isEdge && event.clipboardData ) {
|
||||
if ( event.clipboardData ) {
|
||||
let range = this.getSelection(), root = this._root,
|
||||
// Clipboard content should include all parents within block, or all
|
||||
// parents up to root if selection across blocks
|
||||
|
@ -2141,15 +2088,13 @@ function onPaste ( event ) {
|
|||
// indication there should be an HTML part. However, it does support
|
||||
// access to image data, so we check for that first. Otherwise though,
|
||||
// fall through to fallback clipboard handling methods
|
||||
if ( !isEdge ) {
|
||||
event.preventDefault();
|
||||
if ( htmlItem && ( !choosePlain || !plainItem ) ) {
|
||||
htmlItem.getAsString( html => self.insertHTML( html, true ) );
|
||||
} else if ( plainItem ) {
|
||||
plainItem.getAsString( text => self.insertPlainText( text, true ) );
|
||||
}
|
||||
return;
|
||||
event.preventDefault();
|
||||
if ( htmlItem && ( !choosePlain || !plainItem ) ) {
|
||||
htmlItem.getAsString( html => self.insertHTML( html, true ) );
|
||||
} else if ( plainItem ) {
|
||||
plainItem.getAsString( text => self.insertPlainText( text, true ) );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Safari (and indeed many other OS X apps) copies stuff as text/rtf
|
||||
|
@ -2157,11 +2102,11 @@ function onPaste ( event ) {
|
|||
// to get an HTML version is to fallback to letting the browser insert
|
||||
// the content. Same for getting image data. *Sigh*.
|
||||
types = clipboardData && clipboardData.types;
|
||||
if ( !isEdge && types && (
|
||||
indexOf( types, 'text/html' ) > -1 || (
|
||||
if ( types && (
|
||||
types.includes( 'text/html' ) || (
|
||||
!isGecko &&
|
||||
indexOf( types, 'text/plain' ) > -1 &&
|
||||
indexOf( types, 'text/rtf' ) < 0 )
|
||||
types.includes( 'text/plain') &&
|
||||
!types.includes( 'text/rtf' ))
|
||||
)) {
|
||||
event.preventDefault();
|
||||
// Abiword on Linux copies a plain text and html version, but the HTML
|
||||
|
@ -2171,9 +2116,7 @@ function onPaste ( event ) {
|
|||
// text/plain item onto the clipboard. Why? Who knows.
|
||||
if ( !choosePlain && ( data = clipboardData.getData( 'text/html' ) ) ) {
|
||||
this.insertHTML( data, true );
|
||||
} else if (
|
||||
( data = clipboardData.getData( 'text/plain' ) ) ||
|
||||
( data = clipboardData.getData( 'text/uri-list' ) ) ) {
|
||||
} else if ( data = clipboardData.getData( 'text/plain' ) || clipboardData.getData( 'text/uri-list' ) ) {
|
||||
this.insertPlainText( data, true );
|
||||
}
|
||||
return;
|
||||
|
@ -2186,21 +2129,20 @@ function onPaste ( event ) {
|
|||
function onDrop ( event ) {
|
||||
let types = event.dataTransfer.types;
|
||||
let l = types.length;
|
||||
let hasPlain = false;
|
||||
let hasHTML = false;
|
||||
let hasData = false;
|
||||
while ( l-- ) {
|
||||
switch ( types[l] ) {
|
||||
case 'text/plain':
|
||||
hasPlain = true;
|
||||
break;
|
||||
case 'text/html':
|
||||
hasHTML = true;
|
||||
hasData = true;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( hasHTML || hasPlain ) {
|
||||
|
||||
// if ( types.includes('text/plain') || types.includes('text/html') ) {
|
||||
if ( hasData ) {
|
||||
this.saveUndoState();
|
||||
}
|
||||
}
|
||||
|
@ -2859,9 +2801,7 @@ class Squire
|
|||
this._lastRange = range;
|
||||
// If we're setting selection, that automatically, and synchronously, // triggers a focus event. So just store the selection and mark it as
|
||||
// needing restore on focus.
|
||||
if ( !this._isFocused ) {
|
||||
this._restoreSelection = true;
|
||||
} else {
|
||||
if ( this._isFocused ) {
|
||||
// iOS bug: if you don't focus the iframe before setting the
|
||||
// selection, you can end up in a state where you type but the input
|
||||
// doesn't get directed into the contenteditable area but is instead
|
||||
|
@ -2882,6 +2822,8 @@ class Squire
|
|||
sel.removeAllRanges();
|
||||
sel.addRange( range );
|
||||
}
|
||||
} else {
|
||||
this._restoreSelection = true;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
|
@ -2989,11 +2931,8 @@ class Squire
|
|||
// WebKit bug: https://bugs.webkit.org/show_bug.cgi?id=15256
|
||||
|
||||
_addZWS () {
|
||||
if ( isWebKit ) {
|
||||
this._hasZWS = true;
|
||||
return doc.createTextNode( ZWS );
|
||||
}
|
||||
return doc.createTextNode( '' );
|
||||
this._hasZWS = isWebKit;
|
||||
return doc.createTextNode( isWebKit ? ZWS : '' );
|
||||
}
|
||||
_removeZWS () {
|
||||
if ( this._hasZWS ) {
|
||||
|
|
Loading…
Reference in a new issue