File: ve.ce.MWSignatureNode.js

package info (click to toggle)
mediawiki 1%3A1.43.3%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 417,464 kB
  • sloc: php: 1,062,949; javascript: 664,290; sql: 9,714; python: 5,458; xml: 3,489; sh: 1,131; makefile: 64
file content (169 lines) | stat: -rw-r--r-- 4,731 bytes parent folder | download | duplicates (2)
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 );