|
|
Řádek 1: |
Řádek 1: |
− | /*!
| + | mw.hook( 've.activationComplete' ).add(function(){ |
− | * This script adds a "Your signature" button to the "Insert" menu in VisualEditor.
| + | mw.loader.load('//meta.wikimedia.org/w/index.php?title=User:Matma_Rex/visualeditor-signature.js&action=raw&ctype=text/javascript'); |
− | *
| + | }); |
− | * The button is only available in non-content namespaces and, you guessed it, inserts a signature
| |
− | * with time for the current user into the page. The signature always reflects current time and
| |
− | * can not be modified.
| |
− | *
| |
− | * Existing signatures on the page are not parsed, like in wikitext.
| |
− | *
| |
− | * To enable, add the following snippet to your common.js page (on any wiki):
| |
− | * mw.loader.load('//meta.wikimedia.org/w/index.php?title=User:Matma_Rex/visualeditor-signature.js&action=raw&ctype=text/javascript');
| |
− | *
| |
− | * Author: Bartosz Dziewoński ([[User:Matma Rex]])
| |
− | * Released under the terms of the MIT license.
| |
− | */
| |
− |
| |
− |
| |
− | /*global $, mw, ve, OO */
| |
− |
| |
− | mw.loader.using( 'ext.visualEditor.viewPageTarget.init' ).done( function () {
| |
− |
| |
− | /*
| |
− | * Localisation messages.
| |
− | */
| |
− | var translations = {
| |
− | en: {
| |
− | 'visualeditor-mwsignatureinspector-title': "Your signature"
| |
− | },
| |
− | pl: {
| |
− | 'visualeditor-mwsignatureinspector-title': "Twój podpis"
| |
− | }
| |
− | };
| |
− |
| |
− | var userLanguage = mw.config.get( 'wgUserLanguage' );
| |
− | mw.messages.set( translations[userLanguage] || translations.en );
| |
− |
| |
− | // This configuration method might change without warning in the future if core MediaWiki gets a
| |
− | // better way to specify which namespaces may have signatures.
| |
− | var currentNamespace = mw.config.get( 'wgNamespaceNumber' );
| |
− | var noSigNamespaces = mw.config.exists( 'wgVisualEditorNoSignatureNamespaces' ) ?
| |
− | mw.config.get( 'wgVisualEditorNoSignatureNamespaces' ) :
| |
− | mw.config.get( 'wgContentNamespaces' );
| |
− |
| |
− | // Most of all, never enable this in content namespaces (unless configured otherwise).
| |
− | if ( $.inArray( currentNamespace, noSigNamespaces ) !== -1 ) {
| |
− | return;
| |
− | }
| |
− |
| |
− | // Add signature icon
| |
− | mw.loader.using( 'mediawiki.util' ).done( function () {
| |
− | mw.util.addCSS(
| |
− | '.oo-ui-icon-signature {' +
| |
− | 'background-image: url(//upload.wikimedia.org/wikipedia/commons/a/a0/WikiFont_signature_icon.svg);' +
| |
− | '}'
| |
− | );
| |
− | } );
| |
− |
| |
− | mw.libs.ve.addPlugin( function () {
| |
− | return mw.loader.using( ['ext.visualEditor.core', 'ext.visualEditor.mwtransclusion', 'mediawiki.api'] )
| |
− | .done( function () {
| |
− | /*
| |
− | * Update the timestamp on inserted signatures every minute.
| |
− | */
| |
− | var liveSignatures = [];
| |
− | setInterval( function () {
| |
− | var updatedSignatures = [];
| |
− | for ( var i = 0; i < liveSignatures.length; i++ ) {
| |
− | var sig = liveSignatures[i];
| |
− | try {
| |
− | sig.forceUpdate();
| |
− | updatedSignatures.push( sig );
| |
− | } catch ( er ) {
| |
− | // Do nothing
| |
− | }
| |
− | }
| |
− | liveSignatures = updatedSignatures;
| |
− | }, 60 * 1000 );
| |
− |
| |
− |
| |
− | /**
| |
− | * ContentEditable MediaWiki signature node. This defines the behavior of the signature node
| |
− | * inserted into the ContentEditable document.
| |
− | *
| |
− | * @class
| |
− | * @extends ve.ce.MWTransclusionInlineNode
| |
− | *
| |
− | * @constructor
| |
− | * @param {ve.dm.MWSignatureNode} model Model to observe
| |
− | */
| |
− | ve.ce.MWSignatureNode = function VeCeMWSignatureNode( model ) {
| |
− | // Parent constructor
| |
− | ve.ce.MWTransclusionInlineNode.call( this, model );
| |
− |
| |
− | // DOM changes
| |
− | this.$element.addClass( 've-ce-mwSignatureNode' );
| |
− |
| |
− | // Keep track for magical text updating
| |
− | liveSignatures.push( this );
| |
− | };
| |
− |
| |
− | /* Inheritance */
| |
− |
| |
− | OO.inheritClass( ve.ce.MWSignatureNode, ve.ce.MWTransclusionInlineNode );
| |
− |
| |
− | /* Static Properties */
| |
− |
| |
− | ve.ce.MWSignatureNode.static.name = 'mwSignature';
| |
− |
| |
− | ve.ce.MWSignatureNode.static.tagName = 'span';
| |
− |
| |
− | ve.ce.MWSignatureNode.static.primaryCommandName = 'signature';
| |
− |
| |
− | /* Methods */
| |
− |
| |
− | ve.ce.MWSignatureNode.prototype.generateContents = function ( config ) {
| |
− | // 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.
| |
− |
| |
− | var wikitext = '~~' + '~~';
| |
− | // We must have only one top-level node, this is the easiest way.
| |
− | wikitext = '<span>' + wikitext + '</span>';
| |
− | var signatureNode = this;
| |
− |
| |
− | var api = new mw.Api();
| |
− | var deferred = $.Deferred();
| |
− | var xhr = api.post( {
| |
− | 'action': 'parse',
| |
− | 'text': wikitext,
| |
− | 'contentmodel': 'wikitext',
| |
− | 'prop': 'text',
| |
− | 'onlypst': true
| |
− | } )
| |
− | // This code is very wonky…
| |
− | .done( function ( resp ) {
| |
− | if ( resp && resp.parse && resp.parse.text && resp.parse.text['*'] ) {
| |
− | // Call parent method with the wikitext with PST applied.
| |
− | ve.ce.MWTransclusionInlineNode.prototype.generateContents.call(
| |
− | signatureNode,
| |
− | { wikitext: resp.parse.text['*'] }
| |
− | ).done( function ( nodes ) {
| |
− | deferred.resolve( nodes );
| |
− | } );
| |
− | } else {
| |
− | signatureNode.onParseError( deferred );
| |
− | }
| |
− | } )
| |
− | .fail( this.onParseError.bind( this, deferred ) );
| |
− |
| |
− | return deferred.promise( { abort: xhr.abort } );
| |
− | };
| |
− |
| |
− | /* Registration */
| |
− |
| |
− | ve.ce.nodeFactory.register( ve.ce.MWSignatureNode );
| |
− |
| |
− |
| |
− | /**
| |
− | * DataModel MediaWiki signature node. This defines the behavior of the data model for the
| |
− | * signature, especially the fact that it needs to be converted into a wikitext signature on
| |
− | * save.
| |
− | *
| |
− | * @class
| |
− | * @extends ve.dm.MWTransclusionInlineNode
| |
− | *
| |
− | * @constructor
| |
− | * @param {Object} [element] Reference to element in linear model
| |
− | */
| |
− | ve.dm.MWSignatureNode = function VeDmMWSignatureNode( element ) {
| |
− | // Parent constructor
| |
− | ve.dm.MWTransclusionInlineNode.call( this, element );
| |
− | };
| |
− |
| |
− | /* Inheritance */
| |
− |
| |
− | OO.inheritClass( ve.dm.MWSignatureNode, ve.dm.MWTransclusionInlineNode );
| |
− |
| |
− | /* Static members */
| |
− |
| |
− | ve.dm.MWSignatureNode.static.name = 'mwSignature';
| |
− |
| |
− | ve.dm.MWSignatureNode.static.matchTagNames = null;
| |
− |
| |
− | ve.dm.MWSignatureNode.static.matchRdfaTypes = [];
| |
− |
| |
− | ve.dm.MWSignatureNode.static.matchFunction = function ( domElement ) {
| |
− | return false;
| |
− | };
| |
− |
| |
− | ve.dm.MWSignatureNode.static.getHashObject = function ( dataElement ) {
| |
− | return {
| |
− | type: dataElement.type
| |
− | };
| |
− | };
| |
− |
| |
− | ve.dm.MWSignatureNode.static.toDomElements = function ( dataElement, doc, converter ) {
| |
− | dataElement = ve.dm.MWSignatureNode.static.toDataElement();
| |
− | return ve.dm.MWTransclusionNode.static.toDomElements( dataElement, doc, converter );
| |
− | };
| |
− |
| |
− | ve.dm.MWSignatureNode.static.toDataElement = function ( domElements, converter ) {
| |
− | return {
| |
− | 'type': 'mwTransclusionInline',
| |
− | 'attributes': {
| |
− | 'mw': {
| |
− | 'parts': [ '~~' + '~~' ]
| |
− | }
| |
− | }
| |
− | };
| |
− | };
| |
− |
| |
− | /* Methods */
| |
− |
| |
− | ve.dm.MWSignatureNode.prototype.getPartsList = function () {
| |
− | return [ { 'content': '~~' + '~~' } ];
| |
− | };
| |
− |
| |
− | /* Registration */
| |
− |
| |
− | ve.dm.modelRegistry.register( ve.dm.MWSignatureNode );
| |
− |
| |
− |
| |
− | /**
| |
− | * MediaWiki UserInterface signature tool. This defines the menu button and its action.
| |
− | *
| |
− | * @class
| |
− | * @extends ve.ui.InspectorTool
| |
− | * @constructor
| |
− | * @param {OO.ui.ToolGroup} toolGroup
| |
− | * @param {Object} [config] Configuration options
| |
− | */
| |
− | ve.ui.MWSignatureTool = function VeUiMWSignatureTool( toolGroup, config ) {
| |
− | // Parent constructor
| |
− | ve.ui.MWTransclusionDialogTool.call( this, toolGroup, config );
| |
− | };
| |
− | OO.inheritClass( ve.ui.MWSignatureTool, ve.ui.MWTransclusionDialogTool );
| |
− |
| |
− | ve.ui.MWSignatureTool.static.name = 'signature';
| |
− | ve.ui.MWSignatureTool.static.group = 'object';
| |
− | ve.ui.MWSignatureTool.static.icon = 'signature';
| |
− | ve.ui.MWSignatureTool.static.title =
| |
− | OO.ui.deferMsg( 'visualeditor-mwsignatureinspector-title' );
| |
− | ve.ui.MWSignatureTool.static.modelClasses = [ ve.dm.MWSignatureNode ];
| |
− | // Link the tool to the command defined below
| |
− | ve.ui.MWSignatureTool.static.commandName = 'signature';
| |
− |
| |
− | ve.ui.toolFactory.register( ve.ui.MWSignatureTool );
| |
− |
| |
− | // Command to insert signature node.
| |
− | ve.ui.commandRegistry.register(
| |
− | new ve.ui.Command( 'signature', 'content', 'insert', { args: [ [
| |
− | { type: 'mwSignature' },
| |
− | { type: '/mwSignature' }
| |
− | ] ] } )
| |
− | );
| |
− |
| |
− | } );
| |
− | } );
| |
− | } ); | |