mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-15 18:39:52 +00:00
62ec9d92e2
Update: Switched to generic ve.ui.Toolbar and ve.ui.Tool for consistency with VE in general. Changed searchbox to floating and hidden by default. Call for search using shortcut key. Overriden ESC key. Node handler for <syntaxhighlight>'s Rdfa type. Embedded in VisualEditor.php with its own ResourceLoader module. Use $wgVisualEditorEnableExperimentalCode in LocalSettings.php to load the module. Supported languages (for testing): text, javascript Features: (1) Internal mechanisms: (1.1) Tokenizer. Tokenize input code string. (1.2) Highlighter. Highlight based on predefined rules. (1.3) Syntax checker. Validate based on predefined rules. (2) SimpleSurface: (2.1) Auto indenter. Works with (){}[] blocks. (2.2) Reformatter. Reformats spaces and remove trailing whitespaces. (2.3) Language selection dropdown. (2.4) Basic editing. Insert, deletion, selection. (2.5) Clipboard & edit history support. (2.5.1) Undo (Ctrl + Z), redo (Ctrl + Y) (2.5.2) Copy (Ctrl + C), cut (Ctrl + X), paste (Ctrl + V) (2.6) Search & replace. Ctrl + F to quickly move focus to search box. Bug 47742 Change-Id: I4adede9e05fd2236cee50ce03f597e8ff6b1914d
308 lines
7.8 KiB
JavaScript
308 lines
7.8 KiB
JavaScript
/*!
|
|
* VisualEditor MediaWiki UserInterface raster icon styles.
|
|
*
|
|
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
*/
|
|
|
|
/**
|
|
* MWSyntaxHighlight validator
|
|
*
|
|
* @constructor
|
|
*/
|
|
ve.ce.MWSyntaxHighlightValidator = function VeCeMWSyntaxHighlightValidator() {
|
|
// Properties
|
|
// 'methodname' : method reference
|
|
this.roster = {
|
|
'error' : this.err,
|
|
'balanceAA' : this.balanceAA,
|
|
'balanceAB' : this.balanceAB,
|
|
'matchAB' : this.matchAB
|
|
};
|
|
// Data to perform validation on
|
|
this.data = '';
|
|
// Indices of included areas
|
|
this.matchableArea = [];
|
|
};
|
|
|
|
/* Methods */
|
|
|
|
/**
|
|
* Get the roster of helper methods
|
|
*
|
|
* @method
|
|
* @returns {Object} Method name, method reference
|
|
*/
|
|
ve.ce.MWSyntaxHighlightValidator.prototype.getRoster = function(){
|
|
return this.roster;
|
|
};
|
|
|
|
/**
|
|
* Set the data for validation
|
|
*
|
|
* @method
|
|
* @param {string} dataString
|
|
*/
|
|
ve.ce.MWSyntaxHighlightValidator.prototype.setData = function( dataString ){
|
|
this.data = dataString;
|
|
};
|
|
|
|
/**
|
|
* Get the overlap of two ranges
|
|
*
|
|
* @method
|
|
* @param {Object} x Model index range
|
|
* @param {Object} y Model index range
|
|
* @returns {Object} Model index range
|
|
*/
|
|
ve.ce.MWSyntaxHighlightValidator.prototype.getOverlap = function( x, y ){
|
|
if ( x.b <= y.a || y.b <= x.a ){
|
|
return null;
|
|
} else {
|
|
var sorted = [x.a,x.b,y.a,y.b].sort(function(a,b){return a-b;});
|
|
return {'a':sorted[1], 'b':sorted[2]};
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get the subtraction of two overlapped ranges
|
|
*
|
|
* @method
|
|
* @param {Object} x Model index range
|
|
* @param {Object} y Model index range
|
|
* @returns {Array} Model index ranges
|
|
*/
|
|
ve.ce.MWSyntaxHighlightValidator.prototype.subtractRange = function( x, y ){
|
|
var range1 = {'a':x.a, 'b':y.a},
|
|
range2 = {'a':y.b, 'b':x.b},
|
|
r = [];
|
|
if (range1.a !== range1.b){ r.push(range1); }
|
|
if (range2.a !== range2.b){ r.push(range2); }
|
|
return r;
|
|
};
|
|
|
|
/**
|
|
* Check whether a model index falls within matchable area(s)
|
|
*
|
|
* @method
|
|
* @param {int} index Model index
|
|
* @returns {boolean}
|
|
*/
|
|
ve.ce.MWSyntaxHighlightValidator.prototype.canMatch = function( index ){
|
|
for ( var i = 0; i < this.matchableArea.length; i++ ){
|
|
if (this.matchableArea[i].a <= index && this.matchableArea[i].b >= index){
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Initialize matchable areas
|
|
*
|
|
* @method
|
|
* @param {Array} parsedExcept Regex to determine exceptions
|
|
* @param {Array} parsedWithin Regex to determine included areas
|
|
* @param {Array} parsedOutside Regex to determine excluded areas
|
|
*/
|
|
ve.ce.MWSyntaxHighlightValidator.prototype.initRange = function( parsedExcept, parsedWithin, parsedOutside ){
|
|
var exceptions = [],
|
|
inclusions = [],
|
|
exclusions = [],
|
|
match,
|
|
i, j, o, n;
|
|
for ( i = 0; i < parsedExcept.length; i++ ){
|
|
while ( (match = parsedExcept[i].exec( this.data ) ) !== null ){
|
|
match.index += match[0].indexOf(match[1]);
|
|
exceptions.push({'a': match.index, 'b': match.index + match[1].length});
|
|
}
|
|
}
|
|
for ( i = 0; i < parsedOutside.length; i++ ){
|
|
while ( (match = parsedOutside[i].exec( this.data ) ) !== null ){
|
|
match.index += match[0].indexOf(match[1]);
|
|
exclusions.push({'a': match.index, 'b': match.index + match[1].length});
|
|
}
|
|
}
|
|
if (parsedWithin.length === 0){
|
|
inclusions.push({'a': 0, 'b': this.data.length});
|
|
} else {
|
|
for ( i = 0; i < parsedWithin.length; i++ ){
|
|
while ( (match = parsedWithin[i].exec( this.data ) ) !== null ){
|
|
match.index += match[0].indexOf(match[1]);
|
|
inclusions.push({'a': match.index, 'b': match.index + match[1].length});
|
|
}
|
|
}
|
|
}
|
|
exceptions.sort(function(a, b){ return a.a - b.a; });
|
|
inclusions.sort(function(a, b){ return a.a - b.a; });
|
|
exclusions.sort(function(a, b){ return a.a - b.a; });
|
|
for ( i = 0; i < inclusions.length; i++ ){
|
|
for ( j = 0; j < exclusions.length; j++ ){
|
|
o = this.getOverlap( inclusions[i] , exclusions[j] );
|
|
if ( o !== null ){
|
|
n = this.subtractRange( inclusions[i], o );
|
|
if (n.length === 1){ inclusions.splice(i, 1, n[0]); }
|
|
else if (n.length === 2){ inclusions.splice(i, 1, n[0], n[1]); }
|
|
else if (n.length === 0){ inclusions.splice(i, 1); }
|
|
}
|
|
}
|
|
for ( j = 0; j < exceptions.length; j++ ){
|
|
o = this.getOverlap( inclusions[i] , exceptions[j] );
|
|
if ( o !== null ){
|
|
n = this.subtractRange( inclusions[i], o );
|
|
if (n.length === 1){ inclusions.splice(i, 1, n[0]); }
|
|
else if (n.length === 2){ inclusions.splice(i, 1, n[0], n[1]); }
|
|
else if (n.length === 0){ inclusions.splice(i, 1); }
|
|
}
|
|
}
|
|
}
|
|
this.matchableArea = inclusions;
|
|
};
|
|
|
|
/* Helper methods on roster */
|
|
|
|
/**
|
|
* Return error for matches
|
|
*
|
|
* @method
|
|
* @param {Array} parsedRegexGroup RegExp objects
|
|
* @param {Object} context Context
|
|
* @returns {Object} If need error highlights; regex matches to be highlighted
|
|
*/
|
|
ve.ce.MWSyntaxHighlightValidator.prototype.err = function( parsedRegexGroup, context ){
|
|
var matching = [],
|
|
match,
|
|
i;
|
|
for ( i = 0; i < parsedRegexGroup.length; i++ ){
|
|
while ( (match = parsedRegexGroup[i].exec( context.data ) ) !== null ){
|
|
match.index = match.index + match[0].indexOf(match[1]);
|
|
if (context.canMatch(match.index)){
|
|
matching.push(match);
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
'needMarks' : true,
|
|
'matches' : matching
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Balance the existence of the same token; 1-1 relationship
|
|
*
|
|
* @method
|
|
* @param {Array} parsedRegexGroup RegExp objects
|
|
* @param {Object} context Context
|
|
* @returns {Object} If need error highlights; regex matches to be highlighted
|
|
*/
|
|
ve.ce.MWSyntaxHighlightValidator.prototype.balanceAA = function( parsedRegexGroup, context ){
|
|
var a = [],
|
|
match,
|
|
pushing = true,
|
|
regexA = parsedRegexGroup[0];
|
|
while ( (match = regexA.exec( context.data ) ) !== null ){
|
|
match.index = match.index + match[0].indexOf(match[1]);
|
|
if (context.canMatch(match.index)){
|
|
if (pushing){
|
|
a.push(match);
|
|
pushing = false;
|
|
} else {
|
|
a.pop();
|
|
pushing = true;
|
|
}
|
|
}
|
|
}
|
|
if (a.length === 0){
|
|
return {
|
|
'needMarks' : false,
|
|
'matches' : []
|
|
};
|
|
} else {
|
|
return {
|
|
'needMarks' : true,
|
|
'matches' : a
|
|
};
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Balance the existence of two different tokens; 1-1 relationship
|
|
*
|
|
* @method
|
|
* @param {Array} parsedRegexGroup RegExp objects
|
|
* @param {Object} context Context
|
|
* @returns {Object} If need error highlights; regex matches to be highlighted
|
|
*/
|
|
ve.ce.MWSyntaxHighlightValidator.prototype.balanceAB = function( parsedRegexGroup, context ){
|
|
var a = [],
|
|
b = [],
|
|
match,
|
|
regexA = parsedRegexGroup[0],
|
|
regexB = parsedRegexGroup[1],
|
|
regex = new RegExp('('+regexA.source + '|' + regexB.source+')', 'g');
|
|
while ( (match = regex.exec( context.data ) ) !== null ){
|
|
match.index = match.index + match[0].indexOf(match[1]);
|
|
if (context.canMatch(match.index)){
|
|
if (regexA.test(match[1])){
|
|
a.push(match);
|
|
} else {
|
|
if (a.length !== 0){
|
|
a.pop();
|
|
} else {
|
|
b.push(match);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (a.length === 0 && b.length === 0){
|
|
return {
|
|
'needMarks' : false,
|
|
'matches' : []
|
|
};
|
|
} else {
|
|
return {
|
|
'needMarks' : true,
|
|
'matches' : a.concat(b)
|
|
};
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Check co-existence of two different tokens; n-1 relationship
|
|
*
|
|
* @method
|
|
* @param {Array} parsedRegexGroup RegExp objects
|
|
* @param {Object} context Context
|
|
* @returns {Object} If need error highlights; regex matches to be highlighted
|
|
*/
|
|
ve.ce.MWSyntaxHighlightValidator.prototype.matchAB = function( parsedRegexGroup, context ){
|
|
var match,
|
|
regexA = parsedRegexGroup[0],
|
|
regexB = parsedRegexGroup[1],
|
|
a = [],
|
|
matchOpen = true,
|
|
regex = new RegExp('('+regexA.source + '|' + regexB.source+')', 'g');
|
|
while ( (match = regex.exec( context.data ) ) !== null ){
|
|
match.index = match.index + match[0].indexOf(match[1]);
|
|
if (context.canMatch(match.index)){
|
|
if (regexA.test(match[1])){
|
|
matchOpen = true;
|
|
a.push(match);
|
|
} else {
|
|
matchOpen = false;
|
|
a = [];
|
|
}
|
|
}
|
|
}
|
|
if (!matchOpen){
|
|
return {
|
|
'needMarks' : false,
|
|
'matches' : []
|
|
};
|
|
} else {
|
|
return {
|
|
'needMarks' : true,
|
|
'matches' : a
|
|
};
|
|
}
|
|
}; |