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

Allow defaultBlockTag to be configured

Resolves #23
This commit is contained in:
Neil Jenkins 2014-12-27 15:02:26 +07:00
parent b69a1635de
commit 249ea93c13
4 changed files with 66 additions and 132 deletions

View file

@ -41,6 +41,12 @@ Advanced usage
If you load the library into a top-level document (rather than an iframe), it will not turn the page into an editable document, but will instead add a function named `Squire` to the global scope. Call `new Squire( document )`, with the `document` from an iframe to instantiate multiple rich text areas on the same page efficiently.
### Setting the default block style
By default, the editor will use a `<div>` for blank lines, as most users have been conditioned by Microsoft Word to expect <kbd>Enter</kbd> to act like pressing <kbd>return</kbd> on a typewriter. If you would like to use `<p>` tags (or anything else) for the default block type instead, then after calling `var editor = new Squire( document )` (or getting your reference to the ready-made `editor` instance if using the simple setup), set `editor.defaultBlockTag = 'P';`.
You can also set an object of attributes to apply to each default block node by setting the *defaultBlockProperties* property, e.g. `editor.defaultBlockProperties = { style: 'font-size: 16px;' }`.
License
-------

View file

@ -1131,7 +1131,8 @@ function Squire ( doc ) {
this.addEventListener( 'keyup', this._keyUpDetectChange );
}
this.defaultBlockProperties = undefined;
this.defaultBlockTag = 'DIV';
this.defaultBlockProperties = null;
// IE sometimes fires the beforepaste event twice; make sure it is not run
// again before our after paste function is called.
@ -1191,7 +1192,8 @@ proto.createElement = function ( tag, props, children ) {
proto.createDefaultBlock = function ( children ) {
return fixCursor(
this.createElement( 'DIV', this.defaultBlockProperties, children )
this.createElement(
this.defaultBlockTag, this.defaultBlockProperties, children )
);
};
@ -1902,29 +1904,29 @@ proto.changeFormat = function ( add, remove, range, partial ) {
// --- Block formatting ---
var tagAfterSplit = {
DIV: 'DIV',
PRE: 'DIV',
H1: 'DIV',
H2: 'DIV',
H3: 'DIV',
H4: 'DIV',
H5: 'DIV',
H6: 'DIV',
P: 'DIV',
DT: 'DD',
DD: 'DT',
LI: 'LI'
};
var splitBlock = function ( block, node, offset ) {
var splitBlock = function ( self, block, node, offset ) {
var splitTag = tagAfterSplit[ block.nodeName ],
splitProperties = null,
nodeAfterSplit = split( node, offset, block.parentNode );
if ( !splitTag ) {
splitTag = self.defaultBlockTag;
splitProperties = self.defaultBlockProperties;
}
// Make sure the new node is the correct type.
if ( nodeAfterSplit.nodeName !== splitTag ) {
block = createElement( nodeAfterSplit.ownerDocument, splitTag );
block.className = nodeAfterSplit.dir === 'rtl' ? 'dir-rtl' : '';
block.dir = nodeAfterSplit.dir;
if ( !hasTagAttributes( nodeAfterSplit, splitTag, splitProperties ) ) {
block = createElement( nodeAfterSplit.ownerDocument,
splitTag, splitProperties );
if ( nodeAfterSplit.dir ) {
block.className = nodeAfterSplit.dir === 'rtl' ? 'dir-rtl' : '';
block.dir = nodeAfterSplit.dir;
}
replaceWith( nodeAfterSplit, block );
block.appendChild( empty( nodeAfterSplit ) );
nodeAfterSplit = block;
@ -2533,8 +2535,8 @@ var cleanupBRs = function ( root ) {
proto._ensureBottomLine = function () {
var body = this._body,
div = body.lastChild;
if ( !div || div.nodeName !== 'DIV' || !isBlock( div ) ) {
last = body.lastChild;
if ( !last || last.nodeName !== this.defaultBlockTag || !isBlock( last ) ) {
body.appendChild( this.createDefaultBlock() );
}
};
@ -2764,7 +2766,7 @@ var afterDelete = function ( self, range ) {
var keyHandlers = {
enter: function ( self, event, range ) {
var block, parent, tag, splitTag, nodeAfterSplit;
var block, parent, nodeAfterSplit;
// We handle this ourselves
event.preventDefault();
@ -2784,15 +2786,10 @@ var keyHandlers = {
}
block = getStartBlockOfRange( range );
if ( block && ( parent = getNearest( block, 'LI' ) ) ) {
block = parent;
}
tag = block ? block.nodeName : 'DIV';
splitTag = tagAfterSplit[ tag ];
// If this is a malformed bit of document, just play it safe
// and insert a <br>.
if ( !block ) {
// If this is a malformed bit of document or in a table;
// just play it safe and insert a <br>.
if ( !block || /^T[HD]$/.test( block.nodeName ) ) {
insertNodeInRange( range, self.createElement( 'BR' ) );
range.collapse( false );
self.setSelection( range );
@ -2800,43 +2797,9 @@ var keyHandlers = {
return;
}
// We need to wrap the contents in divs.
var splitNode = range.startContainer,
splitOffset = range.startOffset,
replacement;
if ( !splitTag ) {
// If the selection point is inside the block, we're going to
// rewrite it so our saved reference points won't be valid.
// Pick a node at a deeper point in the tree to avoid this.
if ( splitNode === block ) {
splitNode = splitOffset ?
splitNode.childNodes[ splitOffset - 1 ] : null;
splitOffset = 0;
if ( splitNode ) {
if ( splitNode.nodeName === 'BR' ) {
splitNode = splitNode.nextSibling;
} else {
splitOffset = getLength( splitNode );
}
if ( !splitNode || splitNode.nodeName === 'BR' ) {
replacement = fixCursor( self.createElement( 'DIV' ) );
if ( splitNode ) {
block.replaceChild( replacement, splitNode );
} else {
block.appendChild( replacement );
}
splitNode = replacement;
}
}
}
fixContainer( block );
splitTag = 'DIV';
if ( !splitNode ) {
splitNode = block.firstChild;
}
range.setStart( splitNode, splitOffset );
range.setEnd( splitNode, splitOffset );
block = getStartBlockOfRange( range );
// If in a list, we'll split the LI instead.
if ( parent = getNearest( block, 'LI' ) ) {
block = parent;
}
if ( !block.textContent ) {
@ -2851,7 +2814,8 @@ var keyHandlers = {
}
// Otherwise, split at cursor point.
nodeAfterSplit = splitBlock( block, splitNode, splitOffset );
nodeAfterSplit = splitBlock( self, block,
range.startContainer, range.startOffset );
// Clean up any empty inlines if we hit enter at the beginning of the
// block

File diff suppressed because one or more lines are too long

View file

@ -53,7 +53,8 @@ function Squire ( doc ) {
this.addEventListener( 'keyup', this._keyUpDetectChange );
}
this.defaultBlockProperties = undefined;
this.defaultBlockTag = 'DIV';
this.defaultBlockProperties = null;
// IE sometimes fires the beforepaste event twice; make sure it is not run
// again before our after paste function is called.
@ -113,7 +114,8 @@ proto.createElement = function ( tag, props, children ) {
proto.createDefaultBlock = function ( children ) {
return fixCursor(
this.createElement( 'DIV', this.defaultBlockProperties, children )
this.createElement(
this.defaultBlockTag, this.defaultBlockProperties, children )
);
};
@ -824,29 +826,29 @@ proto.changeFormat = function ( add, remove, range, partial ) {
// --- Block formatting ---
var tagAfterSplit = {
DIV: 'DIV',
PRE: 'DIV',
H1: 'DIV',
H2: 'DIV',
H3: 'DIV',
H4: 'DIV',
H5: 'DIV',
H6: 'DIV',
P: 'DIV',
DT: 'DD',
DD: 'DT',
LI: 'LI'
};
var splitBlock = function ( block, node, offset ) {
var splitBlock = function ( self, block, node, offset ) {
var splitTag = tagAfterSplit[ block.nodeName ],
splitProperties = null,
nodeAfterSplit = split( node, offset, block.parentNode );
if ( !splitTag ) {
splitTag = self.defaultBlockTag;
splitProperties = self.defaultBlockProperties;
}
// Make sure the new node is the correct type.
if ( nodeAfterSplit.nodeName !== splitTag ) {
block = createElement( nodeAfterSplit.ownerDocument, splitTag );
block.className = nodeAfterSplit.dir === 'rtl' ? 'dir-rtl' : '';
block.dir = nodeAfterSplit.dir;
if ( !hasTagAttributes( nodeAfterSplit, splitTag, splitProperties ) ) {
block = createElement( nodeAfterSplit.ownerDocument,
splitTag, splitProperties );
if ( nodeAfterSplit.dir ) {
block.className = nodeAfterSplit.dir === 'rtl' ? 'dir-rtl' : '';
block.dir = nodeAfterSplit.dir;
}
replaceWith( nodeAfterSplit, block );
block.appendChild( empty( nodeAfterSplit ) );
nodeAfterSplit = block;
@ -1455,8 +1457,8 @@ var cleanupBRs = function ( root ) {
proto._ensureBottomLine = function () {
var body = this._body,
div = body.lastChild;
if ( !div || div.nodeName !== 'DIV' || !isBlock( div ) ) {
last = body.lastChild;
if ( !last || last.nodeName !== this.defaultBlockTag || !isBlock( last ) ) {
body.appendChild( this.createDefaultBlock() );
}
};
@ -1686,7 +1688,7 @@ var afterDelete = function ( self, range ) {
var keyHandlers = {
enter: function ( self, event, range ) {
var block, parent, tag, splitTag, nodeAfterSplit;
var block, parent, nodeAfterSplit;
// We handle this ourselves
event.preventDefault();
@ -1706,15 +1708,10 @@ var keyHandlers = {
}
block = getStartBlockOfRange( range );
if ( block && ( parent = getNearest( block, 'LI' ) ) ) {
block = parent;
}
tag = block ? block.nodeName : 'DIV';
splitTag = tagAfterSplit[ tag ];
// If this is a malformed bit of document, just play it safe
// and insert a <br>.
if ( !block ) {
// If this is a malformed bit of document or in a table;
// just play it safe and insert a <br>.
if ( !block || /^T[HD]$/.test( block.nodeName ) ) {
insertNodeInRange( range, self.createElement( 'BR' ) );
range.collapse( false );
self.setSelection( range );
@ -1722,43 +1719,9 @@ var keyHandlers = {
return;
}
// We need to wrap the contents in divs.
var splitNode = range.startContainer,
splitOffset = range.startOffset,
replacement;
if ( !splitTag ) {
// If the selection point is inside the block, we're going to
// rewrite it so our saved reference points won't be valid.
// Pick a node at a deeper point in the tree to avoid this.
if ( splitNode === block ) {
splitNode = splitOffset ?
splitNode.childNodes[ splitOffset - 1 ] : null;
splitOffset = 0;
if ( splitNode ) {
if ( splitNode.nodeName === 'BR' ) {
splitNode = splitNode.nextSibling;
} else {
splitOffset = getLength( splitNode );
}
if ( !splitNode || splitNode.nodeName === 'BR' ) {
replacement = fixCursor( self.createElement( 'DIV' ) );
if ( splitNode ) {
block.replaceChild( replacement, splitNode );
} else {
block.appendChild( replacement );
}
splitNode = replacement;
}
}
}
fixContainer( block );
splitTag = 'DIV';
if ( !splitNode ) {
splitNode = block.firstChild;
}
range.setStart( splitNode, splitOffset );
range.setEnd( splitNode, splitOffset );
block = getStartBlockOfRange( range );
// If in a list, we'll split the LI instead.
if ( parent = getNearest( block, 'LI' ) ) {
block = parent;
}
if ( !block.textContent ) {
@ -1773,7 +1736,8 @@ var keyHandlers = {
}
// Otherwise, split at cursor point.
nodeAfterSplit = splitBlock( block, splitNode, splitOffset );
nodeAfterSplit = splitBlock( self, block,
range.startContainer, range.startOffset );
// Clean up any empty inlines if we hit enter at the beginning of the
// block