diff options
Diffstat (limited to 'www/wiki/extensions/AbuseFilter/modules')
-rw-r--r-- | www/wiki/extensions/AbuseFilter/modules/ext.abuseFilter.css | 120 | ||||
-rw-r--r-- | www/wiki/extensions/AbuseFilter/modules/ext.abuseFilter.edit.js | 428 | ||||
-rw-r--r-- | www/wiki/extensions/AbuseFilter/modules/ext.abuseFilter.examine.js | 101 | ||||
-rw-r--r-- | www/wiki/extensions/AbuseFilter/modules/ext.abuseFilter.tools.js | 109 | ||||
-rw-r--r-- | www/wiki/extensions/AbuseFilter/modules/mode-abusefilter.js | 117 | ||||
-rw-r--r-- | www/wiki/extensions/AbuseFilter/modules/red_x.png | bin | 0 -> 356 bytes | |||
-rw-r--r-- | www/wiki/extensions/AbuseFilter/modules/yes_check.png | bin | 0 -> 339 bytes |
7 files changed, 875 insertions, 0 deletions
diff --git a/www/wiki/extensions/AbuseFilter/modules/ext.abuseFilter.css b/www/wiki/extensions/AbuseFilter/modules/ext.abuseFilter.css new file mode 100644 index 00000000..8ae9bc1b --- /dev/null +++ b/www/wiki/extensions/AbuseFilter/modules/ext.abuseFilter.css @@ -0,0 +1,120 @@ +/* This code was stolen shamelessly from enwikipedia's Common.css */ +table.mw-abuselog-details { + margin: 1em 1em 1em 0; + background: #f9f9f9; + border: 1px #aaa solid; + border-collapse: collapse; + width: 100%; + table-layout: fixed; +} + +table.mw-abuselog-details th, +table.mw-abuselog-details td { + border: 1px #aaa solid; + padding: 0.2em; +} + +table.mw-abuselog-details th { + background: #f2f2f2; + text-align: center; +} + +table.mw-abuselog-details caption { + font-weight: bold; +} + +.mw-abusefilter-history-changed { + background: #ffe0e0; + font-weight: bold; +} + +.mw-abuselog-var-value { + white-space: pre-wrap; + font-family: monospace; +} + +div.mw-abuselog-var-value { + max-height: 25em; + max-width: 100%; + overflow: auto; +} + +td.mw-abuselog-var { + width: 30%; +} + +table.mw-abusefilter-list-scrollable { + display: block; + border: 0; + overflow-x: auto; +} + +tr.mw-abusefilter-list-disabled, +tr.mw-abusefilter-list-disabled td { + color: #666; +} + +tr.mw-abusefilter-list-deleted, +tr.mw-abusefilter-list-deleted td { + color: #aaa; +} + +.mw-abusefilter-examine-match, +.mw-abusefilter-syntaxresult-ok, +li.mw-abusefilter-changeslist-match { + /* @embed */ + background-image: url( yes_check.png ); +} + +.mw-abusefilter-examine-nomatch, +.mw-abusefilter-syntaxresult-error, +li.mw-abusefilter-changeslist-nomatch { + /* @embed */ + background-image: url( red_x.png ); +} + +div.mw-abusefilter-editor { + max-width: 75em; + height: 30em; + line-height: 1.5em; + border: 1px solid #a2a9b1; + display: none; +} + +fieldset.mw-abusefilter-edit-buttons { + margin-top: 1em; +} + +.mw-abusefilter-load-filter-id { + width: 25%; + min-width: 250px; +} + +#mw-abusefilter-syntaxresult, +ul li.mw-abusefilter-changeslist-nomatch, +ul li.mw-abusefilter-changeslist-match { + /* Do not consolidate these into a "background:" rule; it will override the background-image: settings in above rulesets */ + background-repeat: no-repeat; + padding-left: 25px; + background-position: left center; +} + +/* Name is in site content language */ +/* @noflip */ +.sitedir-ltr .TablePager_col_af_public_comments { + direction: ltr; +} + +/* @noflip */ +.sitedir-rtl .TablePager_col_af_public_comments { + direction: rtl; +} + +#mw-abusefilter-filter-tools { + display: inline; +} + +.client-nojs #mw-abusefilter-export-link, +.client-js #mw-abusefilter-export { + display: none; +} diff --git a/www/wiki/extensions/AbuseFilter/modules/ext.abuseFilter.edit.js b/www/wiki/extensions/AbuseFilter/modules/ext.abuseFilter.edit.js new file mode 100644 index 00000000..176a45e5 --- /dev/null +++ b/www/wiki/extensions/AbuseFilter/modules/ext.abuseFilter.edit.js @@ -0,0 +1,428 @@ +/** + * AbuseFilter editing JavaScript + * + * @author John Du Hart + * @author Marius Hoch <hoo@online.de> + */ +/* global ace */ + +( function ( mw, $ ) { + 'use strict'; + + // Filter editor for JS and jQuery handling + // @var {jQuery} + var $filterBox, + // Filter editor for Ace specific functions + filterEditor, + // Hidden textarea for submitting form + // @var {jQuery} + $plainTextBox, + // Bool to determine what editor to use + useAce = false; + + /** + * Returns the currently selected warning message + * + * @return {string} current warning message + */ + function getCurrentWarningMessage() { + var message = $( '#mw-abusefilter-warn-message-existing' ).val(); + + if ( message === 'other' ) { + message = $( '#mw-abusefilter-warn-message-other' ).val(); + } + + return message; + } + + /** + * Things always needed after syntax checks + * + * @param {string} resultText + * @param {string} className Class to add + * @param {bool} syntaxOk Is the syntax ok? + */ + function processSyntaxResultAlways( resultText, className, syntaxOk ) { + $.removeSpinner( 'abusefilter-syntaxcheck' ); + $( '#mw-abusefilter-syntaxcheck' ).prop( 'disabled', false ); + + $( '#mw-abusefilter-syntaxresult' ) + .show() + .attr( 'class', className ) + .text( resultText ) + .data( 'syntaxOk', syntaxOk ); + } + + /** + * Converts index (used in textareas) in position {row, column} for ace + * + * @author danyaPostfactum (https://github.com/ajaxorg/ace/issues/1162) + * + * @param {string} index Part of data returned from the AJAX request + * @return {Object} row and column + */ + function indexToPosition( index ) { + var lines = filterEditor.session.getDocument().$lines, + newLineChar = filterEditor.session.doc.getNewLineCharacter(), + currentIndex = 0, + row, length; + for ( row = 0; row < lines.length; row++ ) { + length = filterEditor.session.getLine( row ).length; + if ( currentIndex + length >= index ) { + return { + row: row, + column: index - currentIndex + }; + } + currentIndex += length + newLineChar.length; + } + } + + /** + * Switch between Ace Editor and classic textarea + */ + function switchEditor() { + if ( useAce ) { + useAce = false; + $filterBox.hide(); + $plainTextBox.show(); + } else { + useAce = true; + filterEditor.session.setValue( $plainTextBox.val() ); + $filterBox.show(); + $plainTextBox.hide(); + } + } + + /** + * Takes the data retrieved in doSyntaxCheck and processes it + * + * @param {Object} data Data returned from the AJAX request + */ + function processSyntaxResult( data ) { + var position; + data = data.abusefilterchecksyntax; + + if ( data.status === 'ok' ) { + // Successful + processSyntaxResultAlways( + mw.msg( 'abusefilter-edit-syntaxok' ), + 'mw-abusefilter-syntaxresult-ok', + true + ); + } else { + // Set a custom error message as we're aware of the actual problem + processSyntaxResultAlways( + mw.message( 'abusefilter-edit-syntaxerr', data.message ).toString(), + 'mw-abusefilter-syntaxresult-error', + false + ); + + if ( useAce ) { + filterEditor.focus(); + position = indexToPosition( data.character ); + filterEditor.navigateTo( position.row, position.column ); + filterEditor.scrollToRow( position.row ); + } else { + $plainTextBox + .focus() + .textSelection( 'setSelection', { start: data.character } ); + } + } + } + + /** + * Acts on errors after doSyntaxCheck + * + * @param {string} error Error code returned from the AJAX request + * @param {Object} details Details about the error + */ + function processSyntaxResultFailure( error, details ) { + var msg = error === 'http' ? 'abusefilter-http-error' : 'unknown-error'; + processSyntaxResultAlways( + mw.msg( msg, details && details.exception ), + 'mw-abusefilter-syntaxresult-error', + false + ); + } + + /** + * Sends the current filter text to be checked for syntax issues. + * + * @context HTMLElement + * @param {jQuery.Event} e + */ + function doSyntaxCheck() { + var filter = $plainTextBox.val(), + api = new mw.Api(); + + $( this ) + .prop( 'disabled', true ) + .injectSpinner( { id: 'abusefilter-syntaxcheck', size: 'large' } ); + + api.post( { + action: 'abusefilterchecksyntax', + filter: filter + } ) + .done( processSyntaxResult ) + .fail( processSyntaxResultFailure ); + } + + /** + * Adds text to the filter textarea + * Fired by a change event from the #wpFilterBuilder dropdown + */ + function addText() { + var $filterBuilder = $( '#wpFilterBuilder' ); + + if ( $filterBuilder.prop( 'selectedIndex' ) === 0 ) { + return; + } + + if ( useAce ) { + filterEditor.insert( $filterBuilder.val() + ' ' ); + $plainTextBox.val( filterEditor.getSession().getValue() ); + } else { + $plainTextBox.textSelection( + 'encapsulateSelection', { pre: $filterBuilder.val() + ' ' } + ); + } + $filterBuilder.prop( 'selectedIndex', 0 ); + } + + /** + * Fetches a filter from the API and inserts it into the filter box. + * + * @context HTMLElement + * @param {jQuery.Event} e + */ + function fetchFilter() { + var filterId = $.trim( $( '#mw-abusefilter-load-filter input' ).val() ), + api; + + if ( filterId === '' ) { + return; + } + + $( this ).injectSpinner( { id: 'fetch-spinner', size: 'large' } ); + + // We just ignore errors or unexisting filters over here + api = new mw.Api(); + api.get( { + action: 'query', + list: 'abusefilters', + abfprop: 'pattern', + abfstartid: filterId, + abfendid: filterId, + abflimit: 1 + } ) + .always( function () { + $.removeSpinner( 'fetch-spinner' ); + } ) + .done( function ( data ) { + if ( data.query.abusefilters[ 0 ] !== undefined ) { + if ( useAce ) { + filterEditor.setValue( data.query.abusefilters[ 0 ].pattern ); + } + $plainTextBox.val( data.query.abusefilters[ 0 ].pattern ); + } + } ); + } + + /** + * Cycles through all action checkboxes and hides parameter divs + * that don't have checked boxes + */ + function hideDeselectedActions() { + $( 'input.mw-abusefilter-action-checkbox' ).each( function () { + // mw-abusefilter-action-checkbox-{$action} + var action = this.id.substr( 31 ), + $params = $( '#mw-abusefilter-' + action + '-parameters' ); + + if ( $params.length ) { + if ( this.checked ) { + $params.show(); + } else { + $params.hide(); + } + } + } ); + } + + /** + * Fetches the selected warning message for previewing + */ + function previewWarnMessage() { + var api = new mw.Api(), + args = [ + '<nowiki>' + $( 'input[name=wpFilterDescription]' ).val() + '</nowiki>', + $( '#mw-abusefilter-edit-id' ).children().last().text() + ], + message = getCurrentWarningMessage(); + api.get( { + action: 'query', + meta: 'allmessages', + ammessages: message, + amargs: args.join( '|' ) + } ) + .done( function ( data ) { + api.parse( data.query.allmessages[ 0 ][ '*' ], { + disablelimitreport: '', + preview: '', + prop: 'text', + title: 'MediaWiki:' + message + } ) + .done( function ( html ) { + $( '#mw-abusefilter-warn-preview' ).html( html ); + } ); + } ); + } + + /** + * Redirects the browser to the warning message for editing + */ + function editWarnMessage() { + var message = getCurrentWarningMessage(); + + window.location = mw.config.get( 'wgScript' ) + + '?title=MediaWiki:' + mw.util.wikiUrlencode( message ) + + '&action=edit&preload=MediaWiki:abusefilter-warning'; + } + + /** + * Called if the filter group (#mw-abusefilter-edit-group-input) is changed. + * + * @context HTMLELement + * @param {jQuery.Event} e + */ + function onFilterGroupChange() { + var $afWarnMessageExisting, $afWarnMessageOther, newVal; + + if ( !$( '#mw-abusefilter-action-warn-checkbox' ).is( ':checked' ) ) { + $afWarnMessageExisting = $( '#mw-abusefilter-warn-message-existing' ); + $afWarnMessageOther = $( '#mw-abusefilter-warn-message-other' ); + newVal = mw.config.get( 'wgAbuseFilterDefaultWarningMessage' )[ $( this ).val() ]; + + if ( $afWarnMessageExisting.find( 'option[value=\'' + newVal + '\']' ).length ) { + $afWarnMessageExisting.val( newVal ); + $afWarnMessageOther.val( '' ); + } else { + $afWarnMessageExisting.val( 'other' ); + $afWarnMessageOther.val( newVal ); + } + } + } + + /** + * Remove the options for warning messages if the filter is set to global + */ + function toggleCustomMessages() { + // Use the table over here as hideDeselectedActions might alter the visibility of the div + var $warnOptions = $( '#mw-abusefilter-warn-parameters > table' ); + + if ( $( '#wpFilterGlobal' ).is( ':checked' ) ) { + // It's a global filter, so use the default message and hide the option from the user + $( '#mw-abusefilter-warn-message-existing option[value="abusefilter-warning"]' ) + .prop( 'selected', true ); + + $warnOptions.hide(); + } else { + $warnOptions.show(); + } + } + + /** + * Called if the user presses a key in the load filter field + * + * @context HTMLELement + * @param {jQuery.Event} e + */ + function onFilterKeypress( e ) { + if ( e.type === 'keypress' && e.which === 13 ) { + e.preventDefault(); + $( '#mw-abusefilter-load' ).click(); + } + } + + // On ready initialization + $( document ).ready( function () { + var basePath, readOnly, + $exportBox = $( '#mw-abusefilter-export' ); + + $plainTextBox = $( '#' + mw.config.get( 'abuseFilterBoxName' ) ); + + if ( $( '#wpAceFilterEditor' ).length ) { + // CodeEditor is installed. + mw.loader.using( [ 'ext.abuseFilter.ace' ] ).then( function () { + $filterBox = $( '#wpAceFilterEditor' ); + + filterEditor = ace.edit( 'wpAceFilterEditor' ); + filterEditor.session.setMode( 'ace/mode/abusefilter' ); + + // Ace setup from codeEditor extension + basePath = mw.config.get( 'wgExtensionAssetsPath', '' ); + if ( basePath.slice( 0, 2 ) === '//' ) { + // ACE uses web workers, which have importScripts, which don't like relative links. + // This is a problem only when the assets are on another server, so this rewrite should suffice + // Protocol relative + basePath = window.location.protocol + basePath; + } + ace.config.set( 'basePath', basePath + '/CodeEditor/modules/ace' ); + + // Settings for Ace editor box + readOnly = mw.config.get( 'aceConfig' ).aceReadOnly; + + filterEditor.setTheme( 'ace/theme/textmate' ); + filterEditor.session.setOption( 'useWorker', false ); + filterEditor.setReadOnly( readOnly ); + filterEditor.$blockScrolling = Infinity; + + // Display Ace editor + switchEditor(); + + // Hide the syntax ok message when the text changes and sync dummy box + $filterBox.keyup( function () { + var $el = $( '#mw-abusefilter-syntaxresult' ); + + if ( $el.data( 'syntaxOk' ) ) { + $el.hide(); + } + + $plainTextBox.val( filterEditor.getSession().getValue() ); + } ); + + $( '#mw-abusefilter-switcheditor' ).click( switchEditor ); + } ); + } + + // Hide the syntax ok message when the text changes + $plainTextBox.keyup( function () { + var $el = $( '#mw-abusefilter-syntaxresult' ); + + if ( $el.data( 'syntaxOk' ) ) { + $el.hide(); + } + } ); + + $( '#mw-abusefilter-load' ).click( fetchFilter ); + $( '#mw-abusefilter-load-filter' ).keypress( onFilterKeypress ); + $( '#mw-abusefilter-warn-preview-button' ).click( previewWarnMessage ); + $( '#mw-abusefilter-warn-edit-button' ).click( editWarnMessage ); + $( 'input.mw-abusefilter-action-checkbox' ).click( hideDeselectedActions ); + hideDeselectedActions(); + + $( '#wpFilterGlobal' ).change( toggleCustomMessages ); + toggleCustomMessages(); + + $( '#mw-abusefilter-syntaxcheck' ).click( doSyntaxCheck ); + $( '#wpFilterBuilder' ).change( addText ); + $( '#mw-abusefilter-edit-group-input' ).change( onFilterGroupChange ); + + $( '#mw-abusefilter-export-link' ).click( + function ( e ) { + e.preventDefault(); + $exportBox.toggle(); + } + ); + } ); +}( mediaWiki, jQuery ) ); diff --git a/www/wiki/extensions/AbuseFilter/modules/ext.abuseFilter.examine.js b/www/wiki/extensions/AbuseFilter/modules/ext.abuseFilter.examine.js new file mode 100644 index 00000000..e51becf6 --- /dev/null +++ b/www/wiki/extensions/AbuseFilter/modules/ext.abuseFilter.examine.js @@ -0,0 +1,101 @@ +/** + * Check a filter against a change + * + * @author John Du Hart + * @author Marius Hoch <hoo@online.de> + */ + +( function ( mw, $ ) { + 'use strict'; + + // Syntax result div + // @type {jQuery} + var $syntaxResult; + + /** + * Processes the results of the filter test + * + * @param {Object} data + */ + function examinerTestProcess( data ) { + var msg, exClass; + $.removeSpinner( 'filter-check' ); + + if ( data.abusefiltercheckmatch.result ) { + exClass = 'mw-abusefilter-examine-match'; + msg = 'abusefilter-examine-match'; + } else { + exClass = 'mw-abusefilter-examine-nomatch'; + msg = 'abusefilter-examine-nomatch'; + } + $syntaxResult + .attr( 'class', exClass ) + .text( mw.msg( msg ) ) + .show(); + } + + /** + * Processes the results of the filter test in case of an error + * + * @param {string} error Error code returned from the AJAX request + * @param {Object} details Details about the error + */ + function examinerTestProcessFailure( error, details ) { + var msg; + $.removeSpinner( 'filter-check' ); + + if ( error === 'badsyntax' ) { + $syntaxResult.attr( + 'class', 'mw-abusefilter-syntaxresult-error' + ); + msg = 'abusefilter-examine-syntaxerror'; + } else if ( error === 'nosuchrcid' || error === 'nosuchlogid' ) { + msg = 'abusefilter-examine-notfound'; + } else if ( error === 'permissiondenied' ) { + // The 'abusefilter-modify' right is needed to use this API + msg = 'abusefilter-mustbeeditor'; + } else if ( error === 'http' ) { + msg = 'abusefilter-http-error'; + } else { + msg = 'unknown-error'; + } + + $syntaxResult + .text( mw.msg( msg, details && details.exception ) ) + .show(); + } + + /** + * Tests the filter against an rc event or abuse log entry. + * + * @context HTMLElement + * @param {jQuery.Event} e + */ + function examinerTestFilter() { + var filter = $( '#wpTestFilter' ).val(), + examine = mw.config.get( 'abuseFilterExamine' ), + params = { + action: 'abusefiltercheckmatch', + filter: filter + }, + api = new mw.Api(); + + $( this ).injectSpinner( { id: 'filter-check', size: 'large' } ); + + if ( examine.type === 'rc' ) { + params.rcid = examine.id; + } else { + params.logid = examine.id; + } + + // Use post due to the rather large amount of data + api.post( params ) + .done( examinerTestProcess ) + .fail( examinerTestProcessFailure ); + } + + $( document ).ready( function () { + $syntaxResult = $( '#mw-abusefilter-syntaxresult' ); + $( '#mw-abusefilter-examine-test' ).click( examinerTestFilter ); + } ); +}( mediaWiki, jQuery ) ); diff --git a/www/wiki/extensions/AbuseFilter/modules/ext.abuseFilter.tools.js b/www/wiki/extensions/AbuseFilter/modules/ext.abuseFilter.tools.js new file mode 100644 index 00000000..aa212771 --- /dev/null +++ b/www/wiki/extensions/AbuseFilter/modules/ext.abuseFilter.tools.js @@ -0,0 +1,109 @@ +/** + * JavaScript for AbuseFilter tools + * + * @author John Du Hart + * @author Marius Hoch <hoo@online.de> + */ + +( function ( mw, $ ) { + 'use strict'; + + /** + * Submits the expression to be evaluated. + * @context HTMLElement + * @param {jQuery.Event} e + */ + function doExprSubmit() { + var expr = $( '#wpTestExpr' ).val(), + api = new mw.Api(); + $( this ).injectSpinner( 'abusefilter-expr' ); + + api.post( { + action: 'abusefilterevalexpression', + expression: expr + } ) + .fail( function ( error, details ) { + var msg = error === 'http' ? 'abusefilter-http-error' : 'unknown-error'; + $.removeSpinner( 'abusefilter-expr' ); + $( '#mw-abusefilter-expr-result' ) + .text( mw.msg( msg, details.exception ) ); + } ) + .done( function ( data ) { + $.removeSpinner( 'abusefilter-expr' ); + + $( '#mw-abusefilter-expr-result' ) + .text( data.abusefilterevalexpression.result ); + } ); + } + + /** + * Processes the result of the unblocking autopromotions for a user + * + * @param {Object} data + */ + function processReautoconfirm( data ) { + mw.notify( + mw.message( 'abusefilter-reautoconfirm-done', data.abusefilterunblockautopromote.user ).toString() + ); + + $.removeSpinner( 'abusefilter-reautoconfirm' ); + } + + /** + * Processes the result of the unblocking autopromotions for a user in case of an error + * + * @param {string} errorCode + * @param {Object} data + */ + function processReautoconfirmFailure( errorCode, data ) { + var msg; + + switch ( errorCode ) { + case 'permissiondenied': + msg = mw.msg( 'abusefilter-reautoconfirm-notallowed' ); + break; + case 'http': + msg = mw.msg( 'abusefilter-http-error', data && data.exception ); + break; + case 'notsuspended': + msg = data.error.info; + break; + default: + msg = mw.msg( 'unknown-error' ); + break; + } + mw.notify( msg ); + + $.removeSpinner( 'abusefilter-reautoconfirm' ); + } + + /** + * Submits a call to reautoconfirm a user. + * @context HTMLElement + * @param {jQuery.Event} e + */ + function doReautoSubmit() { + var name = $( '#reautoconfirm-user' ).val(), + api; + + if ( name === '' ) { + return; + } + + $( this ).injectSpinner( 'abusefilter-reautoconfirm' ); + + api = new mw.Api(); + api.post( { + action: 'abusefilterunblockautopromote', + user: name, + token: mw.user.tokens.get( 'editToken' ) + } ) + .done( processReautoconfirm ) + .fail( processReautoconfirmFailure ); + } + + $( document ).ready( function () { + $( '#mw-abusefilter-submitexpr' ).click( doExprSubmit ); + $( '#mw-abusefilter-reautoconfirmsubmit' ).click( doReautoSubmit ); + } ); +}( mediaWiki, jQuery ) ); diff --git a/www/wiki/extensions/AbuseFilter/modules/mode-abusefilter.js b/www/wiki/extensions/AbuseFilter/modules/mode-abusefilter.js new file mode 100644 index 00000000..f705913b --- /dev/null +++ b/www/wiki/extensions/AbuseFilter/modules/mode-abusefilter.js @@ -0,0 +1,117 @@ +/* global ace, mw */ +ace.define( 'ace/mode/abusefilter_highlight_rules', [ 'require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/text_highlight_rules' ], function ( require, exports, module ) { + 'use strict'; + + var oop = require( 'ace/lib/oop' ), + TextHighlightRules = require( './text_highlight_rules' ).TextHighlightRules, + AFHighlightRules = function () { + + var keywords = ( mw.config.get( 'aceConfig' ).keywords ), + constants = ( 'true|false|null' ), + functions = ( mw.config.get( 'aceConfig' ).functions ), + variables = ( mw.config.get( 'aceConfig' ).variables ), + deprecated = ( '' ), // Template for deprecated vars, already registered within ace settings. + keywordMapper = this.createKeywordMapper( + { + 'keyword': keywords, + 'support.function': functions, + 'constant.language': constants, + 'variable.language': variables, + 'keyword.deprecated': deprecated + }, + 'identifier' + ), + decimalInteger = '(?:(?:[1-9]\\d*)|(?:0))', + hexInteger = '(?:0[xX][\\dA-Fa-f]+)', + integer = '(?:' + decimalInteger + '|' + hexInteger + ')', + fraction = '(?:\\.\\d+)', + intPart = '(?:\\d+)', + pointFloat = '(?:(?:' + intPart + '?' + fraction + ')|(?:' + intPart + '\\.))', + floatNumber = '(?:' + pointFloat + ')'; + + this.$rules = { + 'start': [ { + token: 'comment', + regex: '\\/\\*', + next: 'comment' + }, { + token: 'string', // " string + regex: '"(?:[^\\\\]|\\\\.)*?"' + }, { + token: 'string', // ' string + regex: "'(?:[^\\\\]|\\\\.)*?'" + }, { + token: 'constant.numeric', // float + regex: floatNumber + }, { + token: 'constant.numeric', // integer + regex: integer + '\\b' + }, { + token: keywordMapper, + regex: '[a-zA-Z_$][a-zA-Z0-9_$]*\\b' + }, { + token: 'keyword.operator', + regex: '\\+|\\-|\\*\\*|\\*|\\/|%|\\^|&|\\||<|>|<=|=>|==|!=|===|!==|:=|=|!' + }, { + token: 'paren.lparen', + regex: '[\\[\\(]' + }, { + token: 'paren.rparen', + regex: '[\\]\\)]' + }, { + token: 'text', + regex: '\\s+|\\w+' + } ], + 'comment': [ { + token: 'comment', + regex: '\\*\\/', + next: 'start' + }, { + defaultToken: 'comment' + } ] + }; + + this.normalizeRules(); + }; + + oop.inherits( AFHighlightRules, TextHighlightRules ); + + exports.AFHighlightRules = AFHighlightRules; +} ); + +ace.define( 'ace/mode/abusefilter', [ 'require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/text', 'ace/mode/abusefilter_highlight_rules' ], function ( require, exports, module ) { + 'use strict'; + + var oop = require( 'ace/lib/oop' ), + TextMode = require( './text' ).Mode, + AFHighlightRules = require( './abusefilter_highlight_rules' ).AFHighlightRules, + MatchingBraceOutdent = require( './matching_brace_outdent' ).MatchingBraceOutdent, + Mode = function () { + this.HighlightRules = AFHighlightRules; + this.$behaviour = this.$defaultBehaviour; + this.$outdent = new MatchingBraceOutdent(); + }; + oop.inherits( Mode, TextMode ); + + ( function () { + this.blockComment = { + start: '/*', + end: '*/' + }; + this.getNextLineIndent = function ( state, line, tab ) { + var indent = this.$getIndent( line ); + return indent; + }; + this.checkOutdent = function ( state, line, input ) { + return this.$outdent.checkOutdent( line, input ); + }; + this.autoOutdent = function ( state, doc, row ) { + this.$outdent.autoOutdent( doc, row ); + }; + + this.$id = 'ace/mode/abusefilter'; + } ) + .call( Mode.prototype ); + + exports.Mode = Mode; +} ); diff --git a/www/wiki/extensions/AbuseFilter/modules/red_x.png b/www/wiki/extensions/AbuseFilter/modules/red_x.png Binary files differnew file mode 100644 index 00000000..11978f6a --- /dev/null +++ b/www/wiki/extensions/AbuseFilter/modules/red_x.png diff --git a/www/wiki/extensions/AbuseFilter/modules/yes_check.png b/www/wiki/extensions/AbuseFilter/modules/yes_check.png Binary files differnew file mode 100644 index 00000000..3b85985d --- /dev/null +++ b/www/wiki/extensions/AbuseFilter/modules/yes_check.png |