1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
/*!
* VisualEditor ContentEditable MWSignatureNode class.
*
* @copyright See AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* ContentEditable MediaWiki signature node. This defines the behavior of the signature node
* inserted into the ContentEditable document.
*
* @class
* @extends ve.ce.LeafNode
*
* @constructor
* @param {ve.dm.MWSignatureNode} model Model to observe
* @param {Object} [config] Configuration options
*/
ve.ce.MWSignatureNode = function VeCeMWSignatureNode() {
// Parent constructor
ve.ce.MWSignatureNode.super.apply( this, arguments );
// Mixin constructors
ve.ce.GeneratedContentNode.call( this );
ve.ce.FocusableNode.call( this );
// DOM changes
this.$element.addClass( 've-ce-mwSignatureNode' );
if ( this.isGenerating() ) {
// Use an initial rendering of '~~~~' as a placeholder to avoid
// the width changing when using the Sequence.
this.$element.text( '~~~~' );
}
};
/* Inheritance */
OO.inheritClass( ve.ce.MWSignatureNode, ve.ce.LeafNode );
OO.mixinClass( ve.ce.MWSignatureNode, ve.ce.GeneratedContentNode );
OO.mixinClass( ve.ce.MWSignatureNode, ve.ce.FocusableNode );
/* Static Properties */
ve.ce.MWSignatureNode.static.name = 'mwSignature';
ve.ce.MWSignatureNode.static.tagName = 'span';
ve.ce.MWSignatureNode.static.primaryCommandName = 'mwSignature';
ve.ce.MWSignatureNode.static.liveSignatures = [];
// Set a description for focusable node tooltip
ve.ce.MWSignatureNode.static.getDescription = function () {
return ve.msg( 'visualeditor-mwsignature-tool' );
};
// Update the timestamp on inserted signatures every minute.
setInterval( () => {
const liveSignatures = ve.ce.MWSignatureNode.static.liveSignatures;
const updatedSignatures = [];
for ( let i = 0; i < liveSignatures.length; i++ ) {
const sig = liveSignatures[ i ];
try {
sig.forceUpdate();
updatedSignatures.push( sig );
} catch ( er ) {
// Do nothing
}
}
// Stop updating signatures that failed once
ve.ce.MWSignatureNode.static.liveSignatures = updatedSignatures;
}, 60 * 1000 );
/* Methods */
/**
* @inheritdoc
*/
ve.ce.MWSignatureNode.prototype.onSetup = function () {
// Parent method
ve.ce.MWSignatureNode.super.prototype.onSetup.call( this );
// Keep track for regular updating of timestamp
this.constructor.static.liveSignatures.push( this );
};
/**
* @inheritdoc
*/
ve.ce.MWSignatureNode.prototype.onTeardown = function () {
const liveSignatures = this.constructor.static.liveSignatures;
// Parent method
ve.ce.MWSignatureNode.super.prototype.onTeardown.call( this );
// Stop tracking
const index = liveSignatures.indexOf( this );
if ( index !== -1 ) {
liveSignatures.splice( index, 1 );
}
};
/**
* @inheritdoc ve.ce.GeneratedContentNode
*/
ve.ce.MWSignatureNode.prototype.generateContents = function () {
const doc = this.getModel().getDocument();
let abortable, aborted;
const abortedPromise = ve.createDeferred().reject( 'http',
{ textStatus: 'abort', exception: 'abort' } ).promise();
function abort() {
aborted = true;
if ( abortable && abortable.abort ) {
abortable.abort();
}
}
// Acquire a temporary user username before previewing, so that signatures
// display the temp user instead of IP user. (T331397)
return mw.user.acquireTempUserName()
.then( () => {
if ( aborted ) {
return abortedPromise;
}
// We must have only one top-level node, this is the easiest way.
const wikitext = '<span>~~~~</span>';
// Parsoid doesn't support pre-save transforms. PHP parser doesn't support Parsoid's
// meta attributes (that may or may not be required).
// We could try hacking up one (or even both) of these, but just calling the two parsers
// in order seems slightly saner.
return ( abortable = ve.init.target.getContentApi( doc ).post( {
action: 'parse',
text: wikitext,
contentmodel: 'wikitext',
prop: 'text',
onlypst: true
} ) );
} )
.then( ( pstResponse ) => {
if ( aborted ) {
return abortedPromise;
}
const wikitext = ve.getProp( pstResponse, 'parse', 'text' );
if ( !wikitext ) {
return ve.createDeferred().reject();
}
return ( abortable = ve.init.target.parseWikitextFragment( wikitext, true, doc ) );
} )
.then( ( parseResponse ) => {
if ( aborted ) {
return abortedPromise;
}
if ( ve.getProp( parseResponse, 'visualeditor', 'result' ) !== 'success' ) {
return ve.createDeferred().reject();
}
// Simplified case of template rendering, don't need to worry about filtering etc
return $( parseResponse.visualeditor.content ).contents().toArray();
} )
.promise( { abort: abort } );
};
/* Registration */
ve.ce.nodeFactory.register( ve.ce.MWSignatureNode );
|