Add VisualEditor support

Most functionality is provided by MWExtensionNode & MWLiveExtensionInspector.

Bug: T45126
Bug: T60388
Bug: T69515
Change-Id: If502a8bd2199b6ceb824e02fdfd13c81c39b53b4
This commit is contained in:
Ed Sanders 2015-05-27 17:15:00 +01:00
parent 0e43b2dc8b
commit b403b58682
12 changed files with 414 additions and 7 deletions

13
.csslintrc Normal file
View file

@ -0,0 +1,13 @@
{
"adjoining-classes": false,
"box-model": false,
"box-sizing": false,
"fallback-colors": false,
"important": false,
"outline-none": false,
"qualified-headings": false,
"unique-headings": false,
"universal-selector": false,
"unqualified-attributes": false,
"gradients": false
}

View file

@ -7,6 +7,7 @@
/*jshint node:true */
module.exports = function ( grunt ) {
grunt.loadNpmTasks( 'grunt-banana-checker' );
grunt.loadNpmTasks( 'grunt-contrib-csslint' );
grunt.loadNpmTasks( 'grunt-contrib-jshint' );
grunt.loadNpmTasks( 'grunt-contrib-watch' );
grunt.loadNpmTasks( 'grunt-jscs' );
@ -32,12 +33,18 @@ module.exports = function ( grunt ) {
jscs: {
src: '<%= jshint.all %>'
},
csslint: {
options: {
csslintrc: '.csslintrc'
},
all: 'modules/**/*.css'
},
banana: {
all: 'i18n/'
},
watch: {
files: [
'.{jscsrc,jshintignore,jshintrc}',
'.{csslintrc,jscsrc,jshintignore,jshintrc}',
'<%= jshint.all %>',
'<%= csslint.all %>'
],
@ -45,6 +52,6 @@ module.exports = function ( grunt ) {
}
} );
grunt.registerTask( 'test', [ 'jshint', 'jsonlint', 'jscs', 'banana' ] );
grunt.registerTask( 'test', [ 'jshint', 'jsonlint', 'jscs', 'csslint', 'banana' ] );
grunt.registerTask( 'default', 'test' );
};

View file

@ -0,0 +1,61 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*/
class ResourceLoaderGeSHiDataModule extends ResourceLoaderModule {
protected $targets = array( 'desktop', 'mobile' );
/**
* @param ResourceLoaderContext $context
* @return string JavaScript code
*/
public function getScript( ResourceLoaderContext $context ) {
return Xml::encodeJsCall(
'mw.config.set',
array(
'wgGeSHiSupportedLanguages',
$this->getLanguages()
),
ResourceLoader::inDebugMode()
);
}
/**
* Get the definition summary for this module.
*
* @param ResourceLoaderContext $context
* @return array
*/
public function getDefinitionSummary( ResourceLoaderContext $context ) {
return array_merge( parent::getDefinitionSummary( $context ), $this->getLanguages() );
}
/**
* Get a full list of available langauges
* @return array
*/
private function getLanguages() {
$lexers = require __DIR__ . '/SyntaxHighlight_GeSHi.lexers.php';
return array_merge( $lexers, array_keys( GeSHi::$compatibleLexers ) );
}
public function enableModuleContentVersion() {
return true;
}
}

View file

@ -6,7 +6,8 @@
"Tim Starling",
"Rob Church",
"Niklas Laxström",
"Ori Livneh"
"Ori Livneh",
"Ed Sanders"
],
"url": "https://www.mediawiki.org/wiki/Extension:SyntaxHighlight_GeSHi",
"descriptionmsg": "syntaxhighlight-desc",
@ -19,7 +20,8 @@
},
"AutoloadClasses": {
"SyntaxHighlight_GeSHi": "SyntaxHighlight_GeSHi.class.php",
"GeSHi": "SyntaxHighlight_GeSHi.compat.php"
"GeSHi": "SyntaxHighlight_GeSHi.compat.php",
"ResourceLoaderGeSHiDataModule": "ResourceLoaderGeSHiDataModule.php"
},
"ExtensionFunctions": [
"SyntaxHighlight_GeSHi::onSetup"
@ -39,6 +41,30 @@
"pygments.wrapper.css",
"pygments.generated.css"
]
},
"ext.geshi.visualEditor": {
"scripts": [
"VisualEditor/ve.dm.MWSyntaxHighlightNode.js",
"VisualEditor/ve.ce.MWSyntaxHighlightNode.js",
"VisualEditor/ve.ui.MWSyntaxHighlightInspector.js",
"VisualEditor/ve.ui.MWSyntaxHighlightInspectorTool.js"
],
"styles": [
"VisualEditor/ve.ui.MWSyntaxHighlightInspector.css"
],
"dependencies": [
"ext.visualEditor.mwcore",
"ext.geshi.data"
],
"messages": [
"syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-code",
"syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-language",
"syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-title"
],
"targets": [ "desktop", "mobile" ]
},
"ext.geshi.data": {
"class": "ResourceLoaderGeSHiDataModule"
}
},
"Hooks": {
@ -59,6 +85,9 @@
"css": "css",
"javascript": "javascript"
},
"VisualEditorPluginModules": [
"ext.geshi.visualEditor"
],
"config": {
"PygmentizePath": false
},

View file

@ -1,8 +1,12 @@
{
"@metadata": {
"authors": [
"Brion Vibber"
"Brion Vibber",
"Ed Sanders"
]
},
"syntaxhighlight-desc": "Provides syntax highlighting <code>&lt;syntaxhighlight&gt;</code> using [http://pygments.org/ GeSHi - Generic Syntax Highlighter]"
"syntaxhighlight-desc": "Provides syntax highlighting <code>&lt;syntaxhighlight&gt;</code> using [http://pygments.org/ GeSHi - Generic Syntax Highlighter]",
"syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-code": "Code",
"syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-language": "Language",
"syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-title": "Code block"
}

View file

@ -8,5 +8,8 @@
"Umherirrender"
]
},
"syntaxhighlight-desc": "{{desc|name=Syntax Highlight GeSHi|url=https://www.mediawiki.org/wiki/Extension:SyntaxHighlight_GeSHi}}"
"syntaxhighlight-desc": "{{desc|name=Syntax Highlight GeSHi|url=https://www.mediawiki.org/wiki/Extension:SyntaxHighlight_GeSHi}}",
"syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-code": "Label for the code input field",
"syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-language": "Label for the language field",
"syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-title": "Title for the VisualEditor syntax highlighter inspector, above the text area for the code"
}

View file

@ -0,0 +1,55 @@
/*!
* VisualEditor ContentEditable MWSyntaxHighlightNode class.
*
* @copyright 2011-2015 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* ContentEditable MediaWiki syntax highlight node.
*
* @class
* @extends ve.ce.MWBlockExtensionNode
*
* @constructor
* @param {ve.dm.MWSyntaxHighlightNode} model Model to observe
* @param {Object} [config] Configuration options
*/
ve.ce.MWSyntaxHighlightNode = function VeCeMWSyntaxHighlightNode() {
// Parent constructor
ve.ce.MWSyntaxHighlightNode.super.apply( this, arguments );
};
/* Inheritance */
OO.inheritClass( ve.ce.MWSyntaxHighlightNode, ve.ce.MWBlockExtensionNode );
/* Static Properties */
ve.ce.MWSyntaxHighlightNode.static.name = 'mwSyntaxHighlight';
ve.ce.MWSyntaxHighlightNode.static.primaryCommandName = 'syntaxhighlight';
/* Methods */
/** */
ve.ce.MWSyntaxHighlightNode.prototype.generateContents = function () {
if ( !this.getModel().isLanguageSupported() ) {
return $.Deferred().reject().promise();
}
// Parent method
return ve.ce.MWSyntaxHighlightNode.super.prototype.generateContents.apply( this, arguments );
};
/** */
ve.ce.MWSyntaxHighlightNode.prototype.onSetup = function () {
// Parent method
ve.ce.MWSyntaxHighlightNode.super.prototype.onSetup.call( this );
// DOM changes
this.$element.addClass( 've-ce-mwSyntaxHighlightNode' );
};
/* Registration */
ve.ce.nodeFactory.register( ve.ce.MWSyntaxHighlightNode );

View file

@ -0,0 +1,67 @@
/*!
* VisualEditor DataModel MWSyntaxHighlightNode class.
*
* @copyright 2011-2015 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* DataModel MediaWiki syntax highlight node.
*
* @class
* @extends ve.dm.MWBlockExtensionNode
*
* @constructor
* @param {Object} [element]
*/
ve.dm.MWSyntaxHighlightNode = function VeDmMWSyntaxHighlightNode() {
// Parent constructor
ve.dm.MWSyntaxHighlightNode.super.apply( this, arguments );
};
/* Inheritance */
OO.inheritClass( ve.dm.MWSyntaxHighlightNode, ve.dm.MWBlockExtensionNode );
/* Static members */
ve.dm.MWSyntaxHighlightNode.static.name = 'mwSyntaxHighlight';
ve.dm.MWSyntaxHighlightNode.static.tagName = 'div';
ve.dm.MWSyntaxHighlightNode.static.extensionName = 'syntaxhighlight';
ve.dm.MWSyntaxHighlightNode.static.getMatchRdfaTypes = function () {
return [ 'mw:Extension/syntaxhighlight', 'mw:Extension/source' ];
};
/* Static methods */
/**
* Check if a language is supported
*
* @param {string} langauge Language name
* @return {boolean} The language is supported
*/
ve.dm.MWSyntaxHighlightNode.static.isLanguageSupported = function ( language ) {
return mw.config.get( 'wgGeSHiSupportedLanguages' ).indexOf( language ) !== -1;
};
/* Methods */
/**
* Check if the node's current language is supported
*
* @return {boolean} The language is supported
*/
ve.dm.MWSyntaxHighlightNode.prototype.isLanguageSupported = function () {
return this.constructor.static.isLanguageSupported( this.getLanguage() );
};
ve.dm.MWSyntaxHighlightNode.prototype.getLanguage = function () {
return this.getAttribute( 'mw' ).attrs.lang;
};
/* Registration */
ve.dm.modelRegistry.register( ve.dm.MWSyntaxHighlightNode );

View file

@ -0,0 +1,10 @@
/*!
* VisualEditor UserInterface MWSyntaxHighlightInspector styles.
*
* @copyright 2011-2015 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
.ve-ui-mwSyntaxHighlightInspector-content .ve-ui-mwExtensionInspector-input textarea {
font-family: monospace, Courier;
}

View file

@ -0,0 +1,120 @@
/*!
* VisualEditor UserInterface MWSyntaxHighlightInspector class.
*
* @copyright 2011-2015 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* MediaWiki syntax highlight inspector.
*
* @class
* @extends ve.ui.MWLiveExtensionInspector
*
* @constructor
* @param {Object} [config] Configuration options
*/
ve.ui.MWSyntaxHighlightInspector = function VeUiMWSyntaxHighlightInspector() {
// Parent constructor
ve.ui.MWSyntaxHighlightInspector.super.apply( this, arguments );
};
/* Inheritance */
OO.inheritClass( ve.ui.MWSyntaxHighlightInspector, ve.ui.MWLiveExtensionInspector );
/* Static properties */
ve.ui.MWSyntaxHighlightInspector.static.name = 'syntaxhighlight';
ve.ui.MWSyntaxHighlightInspector.static.icon = 'alienextension';
ve.ui.MWSyntaxHighlightInspector.static.size = 'large';
ve.ui.MWSyntaxHighlightInspector.static.title = OO.ui.deferMsg( 'syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-title' );
ve.ui.MWSyntaxHighlightInspector.static.modelClasses = [ ve.dm.MWSyntaxHighlightNode ];
ve.ui.MWSyntaxHighlightInspector.static.dir = 'ltr';
/* Methods */
/**
* @inheritdoc
*/
ve.ui.MWSyntaxHighlightInspector.prototype.initialize = function () {
// Parent method
ve.ui.MWSyntaxHighlightInspector.super.prototype.initialize.call( this );
this.language = new OO.ui.TextInputWidget( {
validate: function ( value ) {
return ve.dm.MWSyntaxHighlightNode.static.isLanguageSupported( value );
}
} );
var languageField = new OO.ui.FieldLayout( this.language, {
align: 'top',
label: ve.msg( 'syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-language' )
} ),
codeField = new OO.ui.FieldLayout( this.input, {
align: 'top',
label: ve.msg( 'syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-code' )
} );
// Initialization
this.$content.addClass( 've-ui-mwSyntaxHighlightInspector-content' );
this.form.$element.prepend( languageField.$element, codeField.$element );
};
/**
* @inheritdoc
*/
ve.ui.MWSyntaxHighlightInspector.prototype.getReadyProcess = function ( data ) {
return ve.ui.MWSyntaxHighlightInspector.super.prototype.getReadyProcess.call( this, data )
.next( function () {
if ( this.language.getValue() ) {
this.input.focus();
} else {
this.language.focus();
}
}, this );
};
/**
* @inheritdoc
*/
ve.ui.MWSyntaxHighlightInspector.prototype.getSetupProcess = function ( data ) {
return ve.ui.MWSyntaxHighlightInspector.super.prototype.getSetupProcess.call( this, data )
.next( function () {
var language = this.selectedNode.getAttribute( 'mw' ).attrs.lang || '';
this.language.setValue( language );
if ( !language ) {
this.language.setValidityFlag( true );
}
this.language.on( 'change', this.onChangeHandler );
}, this );
};
/**
* @inheritdoc
*/
ve.ui.MWSyntaxHighlightInspector.prototype.getTeardownProcess = function ( data ) {
return ve.ui.MWSyntaxHighlightInspector.super.prototype.getTeardownProcess.call( this, data )
.first( function () {
this.language.off( 'change', this.onChangeHandler );
}, this );
};
/**
* @inheritdoc
*/
ve.ui.MWSyntaxHighlightInspector.prototype.updateMwData = function ( mwData ) {
// Parent method
ve.ui.MWSyntaxHighlightInspector.super.prototype.updateMwData.call( this, mwData );
mwData.attrs.lang = this.language.getValue();
};
/* Registration */
ve.ui.windowFactory.register( ve.ui.MWSyntaxHighlightInspector );

View file

@ -0,0 +1,37 @@
/*!
* VisualEditor UserInterface MWSyntaxHighlightInspectorTool class.
*
* @copyright 2011-2015 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/*global ve, OO */
/**
* MediaWiki UserInterface syntax highlight tool.
*
* @class
* @extends ve.ui.InspectorTool
* @constructor
* @param {OO.ui.ToolGroup} toolGroup
* @param {Object} [config] Configuration options
*/
ve.ui.MWSyntaxHighlightInspectorTool = function VeUiMWSyntaxHighlightInspectorTool( toolGroup, config ) {
ve.ui.InspectorTool.call( this, toolGroup, config );
};
OO.inheritClass( ve.ui.MWSyntaxHighlightInspectorTool, ve.ui.InspectorTool );
ve.ui.MWSyntaxHighlightInspectorTool.static.name = 'syntaxhighlight';
ve.ui.MWSyntaxHighlightInspectorTool.static.group = 'object';
ve.ui.MWSyntaxHighlightInspectorTool.static.icon = 'alienextension';
ve.ui.MWSyntaxHighlightInspectorTool.static.title = OO.ui.deferMsg(
'syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-title' );
ve.ui.MWSyntaxHighlightInspectorTool.static.modelClasses = [ ve.dm.MWSyntaxHighlightNode ];
ve.ui.MWSyntaxHighlightInspectorTool.static.commandName = 'syntaxhighlight';
ve.ui.toolFactory.register( ve.ui.MWSyntaxHighlightInspectorTool );
ve.ui.commandRegistry.register(
new ve.ui.Command(
'syntaxhighlight', 'window', 'open',
{ args: [ 'syntaxhighlight' ], supportedSelections: [ 'linear' ] }
)
);

View file

@ -10,6 +10,7 @@
"grunt": "0.4.5",
"grunt-cli": "0.1.13",
"grunt-banana-checker": "0.2.2",
"grunt-contrib-csslint": "0.4.0",
"grunt-contrib-jshint": "0.11.2",
"grunt-contrib-watch": "0.6.1",
"grunt-jscs": "1.8.0",