')
- .append($label)
- .append($save)
- .append($cancel);
- var $xcontainer = $('
')
- .append($controls)
- .append($container);
- $xcontainer.width($main.width())
- .height($main.height() * 1.1 + 64 + 32);
+ } );
+ var $controls = $( '
' )
+ .append( $label )
+ .append( $save )
+ .append( $cancel );
+ var $xcontainer = $( '
' )
+ .append( $controls )
+ .append( $container );
+ $xcontainer.width( $main.width() )
+ .height( $main.height() * 1.1 + 64 + 32 );
- $div.css('display', 'none');
- $xcontainer.insertAfter($div);
+ $div.css( 'display', 'none' );
+ $xcontainer.insertAfter( $div );
- var codeEditor = ace.edit($container[0]);
+ var codeEditor = ace.edit( $container[0] );
- var setLanguage = function(lang) {
+ var setLanguage = function ( lang ) {
geshiLang = lang;
var aceLang = map[geshiLang];
- codeEditor.getSession().setMode(new (require("ace/mode/" + aceLang).Mode));
+ codeEditor.getSession().setMode( new (require( "ace/mode/" + aceLang ).Mode) );
};
- setLanguage(geshiLang);
+ setLanguage( geshiLang );
- var closeEditor = function() {
+ var closeEditor = function () {
$xcontainer.remove();
- $div.css('display', 'block');
+ $div.css( 'display', 'block' );
};
- });
+ } );
};
- $sources.each(function(i, div) {
- var $div = $(div);
- setupEditor($div);
- });
+ $sources.each( function ( i, div ) {
+ var $div = $( div );
+ setupEditor( $div );
+ } );
}
-});
+} );
diff --git a/modules/ext.codeEditor.js b/modules/ext.codeEditor.js
index 352b16b1..7c9feb2f 100644
--- a/modules/ext.codeEditor.js
+++ b/modules/ext.codeEditor.js
@@ -37,7 +37,7 @@
* JavaScript for WikiEditor Table of Contents
*/
-$( document ).ready( function() {
+$( document ).ready( function () {
var $wpTextbox1 = $( '#wpTextbox1' );
// Code is supposed to be always LTR. See bug 39364.
@@ -46,8 +46,8 @@ $( document ).ready( function() {
// Add code editor module
$wpTextbox1.wikiEditor( 'addModule', 'codeEditor' );
} );
-$( window ).load( function() {
+$( window ).load( function () {
// If there is a fragment giving a line number, scroll to the relevant location
// Wait for the load event since it doesn't scroll properly on ready
- $( '#wpTextbox1' ).data('wikiEditor-context').fn.codeEditorMonitorFragment();
+ $( '#wpTextbox1' ).data( 'wikiEditor-context' ).fn.codeEditorMonitorFragment();
} );
diff --git a/modules/jquery.codeEditor.js b/modules/jquery.codeEditor.js
index a4678332..3c8fce99 100644
--- a/modules/jquery.codeEditor.js
+++ b/modules/jquery.codeEditor.js
@@ -1,429 +1,427 @@
/* Ace syntax-highlighting code editor extension for wikiEditor */
-( function( $ ) {
+(function ( $ ) {
+ $.wikiEditor.modules.codeEditor = {
+ /**
+ * Core Requirements
+ */
+ 'req': [ 'codeEditor' ],
+ /**
+ * Configuration
+ */
+ cfg: {
+ //
+ },
+ /**
+ * API accessible functions
+ */
+ api: {
+ //
+ },
+ /**
+ * Event handlers
+ */
+ evt: {
+ //
+ },
+ /**
+ * Internally used functions
+ */
+ fn: {
+ }
-$.wikiEditor.modules.codeEditor = {
+ };
-/**
- * Core Requirements
- */
-'req': [ 'codeEditor' ],
-/**
- * Configuration
- */
-cfg: {
- //
-},
-/**
- * API accessible functions
- */
-api: {
- //
-},
-/**
- * Event handlers
- */
-evt: {
- //
-},
-/**
- * Internally used functions
- */
-fn: {
-}
-
-};
-
-$.wikiEditor.extensions.codeEditor = function( context ) {
-
-/*
- * Event Handlers
- *
- * These act as filters returning false if the event should be ignored or returning true if it should be passed
- * on to all modules. This is also where we can attach some extra information to the events.
- */
-context.evt = $.extend( context.evt, {
- /**
- * Filters change events, which occur when the user interacts with the contents of the iframe. The goal of this
- * function is to both classify the scope of changes as 'division' or 'character' and to prevent further
- * processing of events which did not actually change the content of the iframe.
- */
- 'keydown': function( event ) {
- },
- 'change': function( event ) {
- },
- 'delayedChange': function( event ) {
- },
- 'cut': function( event ) {
- },
- 'paste': function( event ) {
- },
- 'ready': function( event ) {
- },
- 'codeEditorSubmit': function( event ) {
- context.$textarea.val( context.$textarea.textSelection( 'getContents' ) );
- }
-} );
-
-var cookieEnabled = $.cookie('wikiEditor-' + context.instance + '-codeEditor-enabled');
-context.codeEditorActive = (cookieEnabled !== '0');
-
-/**
- * Internally used functions
- */
-context.fn = $.extend( context.fn, {
- 'codeEditorToolbarIcon': function() {
- // When loaded as a gadget, one may need to override the wiki's own assets path.
- var iconPath = mw.config.get('wgCodeEditorAssetsPath', mw.config.get('wgExtensionAssetsPath')) + '/CodeEditor/images/';
- return iconPath + (context.codeEditorActive ? 'code-selected.png' : 'code.png');
- },
- 'setupCodeEditorToolbar': function() {
- // Drop out some formatting that isn't relevant on these pages...
- context.api.removeFromToolbar(context, {
- 'section': 'main',
- 'group': 'format',
- 'tool': 'bold'
- });
- context.api.removeFromToolbar(context, {
- 'section': 'main',
- 'group': 'format',
- 'tool': 'italic'
- });
- var callback = function( context ) {
- context.codeEditorActive = !context.codeEditorActive;
- $.cookie(
- 'wikiEditor-' + context.instance + '-codeEditor-enabled',
- context.codeEditorActive ? 1 : 0,
- { expires: 30, path: '/' }
- );
- context.fn.toggleCodeEditorToolbar();
-
- if (context.codeEditorActive) {
- // set it back up!
- context.fn.setupCodeEditor();
- } else {
- context.fn.disableCodeEditor();
- }
- };
- context.api.addToToolbar( context, {
- 'section': 'main',
- 'group': 'format',
- 'tools': {
- 'codeEditor': {
- 'labelMsg': 'codeeditor-toolbar-toggle',
- 'type': 'button',
- 'icon': context.fn.codeEditorToolbarIcon(),
- 'action': {
- 'type': 'callback',
- 'execute': callback
- }
- }
+ $.wikiEditor.extensions.codeEditor = function ( context ) {
+ /*
+ * Event Handlers
+ *
+ * These act as filters returning false if the event should be ignored or returning true if it should be passed
+ * on to all modules. This is also where we can attach some extra information to the events.
+ */
+ context.evt = $.extend( context.evt, {
+ /**
+ * Filters change events, which occur when the user interacts with the contents of the iframe. The goal of this
+ * function is to both classify the scope of changes as 'division' or 'character' and to prevent further
+ * processing of events which did not actually change the content of the iframe.
+ */
+ 'keydown': function ( event ) {
+ },
+ 'change': function ( event ) {
+ },
+ 'delayedChange': function ( event ) {
+ },
+ 'cut': function ( event ) {
+ },
+ 'paste': function ( event ) {
+ },
+ 'ready': function ( event ) {
+ },
+ 'codeEditorSubmit': function ( event ) {
+ context.$textarea.val( context.$textarea.textSelection( 'getContents' ) );
}
} );
- },
- 'toggleCodeEditorToolbar': function() {
- var target = 'img.tool[rel=codeEditor]';
- var $img = context.modules.toolbar.$toolbar.find( target );
- $img.attr('src', context.fn.codeEditorToolbarIcon());
- },
- /**
- * Sets up the iframe in place of the textarea to allow more advanced operations
- */
- 'setupCodeEditor': function() {
- var box = context.$textarea;
- var lang = mw.config.get("wgCodeEditorCurrentLanguage");
- if (lang) {
- // Ace doesn't like replacing a textarea directly.
- // We'll stub this out to sit on top of it...
- // line-height is needed to compensate for oddity in WikiEditor extension, which zeroes the line-height on a parent container
- var container = context.$codeEditorContainer = $('
').insertAfter(box);
- var editdiv = container.find('.editor');
+ var cookieEnabled = $.cookie( 'wikiEditor-' + context.instance + '-codeEditor-enabled' );
+ context.codeEditorActive = (cookieEnabled !== '0');
- box.css('display', 'none');
- container.width(box.width())
- .height(box.height());
+ /**
+ * Internally used functions
+ */
+ context.fn = $.extend( context.fn, {
+ 'codeEditorToolbarIcon': function () {
+ // When loaded as a gadget, one may need to override the wiki's own assets path.
+ var iconPath = mw.config.get( 'wgCodeEditorAssetsPath', mw.config.get( 'wgExtensionAssetsPath' ) ) + '/CodeEditor/images/';
+ return iconPath + (context.codeEditorActive ? 'code-selected.png' : 'code.png');
+ },
+ 'setupCodeEditorToolbar': function () {
+ // Drop out some formatting that isn't relevant on these pages...
+ context.api.removeFromToolbar( context, {
+ 'section': 'main',
+ 'group': 'format',
+ 'tool': 'bold'
+ } );
+ context.api.removeFromToolbar( context, {
+ 'section': 'main',
+ 'group': 'format',
+ 'tool': 'italic'
+ } );
+ var callback = function ( context ) {
+ context.codeEditorActive = !context.codeEditorActive;
+ $.cookie(
+ 'wikiEditor-' + context.instance + '-codeEditor-enabled',
+ context.codeEditorActive ? 1 : 0,
+ { expires: 30, path: '/' }
+ );
+ context.fn.toggleCodeEditorToolbar();
- editdiv.text(box.val());
- context.codeEditor = ace.edit(editdiv[0]);
-
- // Disable some annoying commands
- context.codeEditor.commands.removeCommand('replace'); // ctrl+R
- context.codeEditor.commands.removeCommand('transposeletters'); // ctrl+T
- context.codeEditor.commands.removeCommand('gotoline'); // ctrl+L
-
- // fakeout for bug 29328
- context.$iframe = [
- {
- contentWindow: {
- focus: function() {
- context.codeEditor.focus();
+ if ( context.codeEditorActive ) {
+ // set it back up!
+ context.fn.setupCodeEditor();
+ } else {
+ context.fn.disableCodeEditor();
+ }
+ };
+ context.api.addToToolbar( context, {
+ 'section': 'main',
+ 'group': 'format',
+ 'tools': {
+ 'codeEditor': {
+ 'labelMsg': 'codeeditor-toolbar-toggle',
+ 'type': 'button',
+ 'icon': context.fn.codeEditorToolbarIcon(),
+ 'action': {
+ 'type': 'callback',
+ 'execute': callback
+ }
}
}
+ } );
+ },
+ 'toggleCodeEditorToolbar': function () {
+ var target = 'img.tool[rel=codeEditor]';
+ var $img = context.modules.toolbar.$toolbar.find( target );
+ $img.attr( 'src', context.fn.codeEditorToolbarIcon() );
+ },
+ /**
+ * Sets up the iframe in place of the textarea to allow more advanced operations
+ */
+ 'setupCodeEditor': function () {
+ var box = context.$textarea;
+
+ var lang = mw.config.get( "wgCodeEditorCurrentLanguage" );
+ if ( lang ) {
+ // Ace doesn't like replacing a textarea directly.
+ // We'll stub this out to sit on top of it...
+ // line-height is needed to compensate for oddity in WikiEditor extension, which zeroes the line-height on a parent container
+ var container = context.$codeEditorContainer = $( '
' ).insertAfter( box );
+ var editdiv = container.find( '.editor' );
+
+ box.css( 'display', 'none' );
+ container.width( box.width() )
+ .height( box.height() );
+
+ editdiv.text( box.val() );
+ context.codeEditor = ace.edit( editdiv[0] );
+
+ // Disable some annoying commands
+ context.codeEditor.commands.removeCommand( 'replace' ); // ctrl+R
+ context.codeEditor.commands.removeCommand( 'transposeletters' ); // ctrl+T
+ context.codeEditor.commands.removeCommand( 'gotoline' ); // ctrl+L
+
+ // fakeout for bug 29328
+ context.$iframe = [
+ {
+ contentWindow: {
+ focus: function () {
+ context.codeEditor.focus();
+ }
+ }
+ }
+ ];
+ box.closest( 'form' ).submit( context.evt.codeEditorSubmit );
+ var session = context.codeEditor.getSession();
+
+ // Use proper tabs
+ session.setUseSoftTabs( false );
+
+ // Bug 47235: Update text field for LivePreview
+ if ( mw.hook ) {
+ // New style hook
+ mw.hook( 'LivePreviewPrepare' ).add( context.evt.codeEditorSubmit );
+
+ mw.hook( 'codeEditor.configure' ).fire( session );
+ }
+ // Old, deprecated style for backwards compat
+ // Do this even if mw.hook exists, because the caller wasn't
+ // updated right away to actually use the new style.
+ $( mw ).bind( 'LivePreviewPrepare', context.evt.codeEditorSubmit );
+
+ // Disable code-linting in the background using JavaScript WebWorkers.
+ // Currently broken due to require() / ResourceLoader mismatch.
+ session.setUseWorker( false );
+ session.setMode( new (require( "ace/mode/" + lang ).Mode) );
+
+ // Force the box to resize horizontally to match in future :D
+ var resize = function () {
+ container.width( box.width() );
+ };
+ $( window ).resize( resize );
+ // Use jquery.ui.resizable so user can make the box taller too
+ container.resizable( {
+ handles: 's',
+ minHeight: box.height(),
+ resize: function () {
+ context.codeEditor.resize();
+ }
+ } );
+
+ var summary = $( '#wpSummary' );
+ // Let modules know we're ready to start working with the content
+ context.fn.trigger( 'ready' );
}
- ];
- box.closest('form').submit( context.evt.codeEditorSubmit );
- var session = context.codeEditor.getSession();
+ },
- // Use proper tabs
- session.setUseSoftTabs( false );
+ /**
+ * Turn off the code editor view and return to the plain textarea.
+ * May be needed by some folks with funky browsers, or just to compare.
+ */
+ 'disableCodeEditor': function () {
+ // Kills it!
+ context.$textarea.closest( 'form' ).unbind( 'submit', context.evt.codeEditorSubmit );
+ if ( mw.hook ) {
+ mw.hook( 'LivePreviewPrepare' ).remove( context.evt.codeEditorSubmit );
+ }
+ $( mw ).unbind( 'LivePreviewPrepare', context.evt.codeEditorSubmit ); // deprecated
- // Bug 47235: Update text field for LivePreview
- if ( mw.hook ) {
- // New style hook
- mw.hook( 'LivePreviewPrepare' ).add( context.evt.codeEditorSubmit );
+ // Save contents
+ context.$textarea.val( context.fn.getContents() );
- mw.hook( 'codeEditor.configure' ).fire( session );
+ // @todo fetch cursor, scroll position
+
+ // Drop the fancy editor widget...
+ context.$codeEditorContainer.remove();
+ context.$codeEditorContainer = undefined;
+ context.$iframe = undefined;
+ context.codeEditor = undefined;
+
+ // Restore textarea
+ context.$textarea.show();
+
+ // @todo restore cursor, scroll position
+ },
+
+ /**
+ * Start monitoring the fragment of the current window for hash change
+ * events. If the hash is already set, handle it as a new event.
+ */
+ 'codeEditorMonitorFragment': function () {
+ function onHashChange() {
+ var regexp = /#mw-ce-l(\d+)/;
+ var result = regexp.exec( window.location.hash );
+ if ( result === null ) {
+ return;
+ }
+
+ // Line numbers in CodeEditor are zero-based
+ var line = parseInt( result[1] );
+ context.codeEditor.navigateTo( line - 1, 0 );
+ // Scroll up a bit to give some context
+ context.codeEditor.scrollToRow( line - 4 );
+ }
+
+ onHashChange();
+ $( window ).bind( 'hashchange', onHashChange );
}
- // Old, deprecated style for backwards compat
- // Do this even if mw.hook exists, because the caller wasn't
- // updated right away to actually use the new style.
- $( mw ).bind( 'LivePreviewPrepare', context.evt.codeEditorSubmit );
- // Disable code-linting in the background using JavaScript WebWorkers.
- // Currently broken due to require() / ResourceLoader mismatch.
- session.setUseWorker(false);
- session.setMode(new (require("ace/mode/" + lang).Mode));
+ } );
- // Force the box to resize horizontally to match in future :D
- var resize = function() {
- container.width(box.width());
+ /**
+ * Override the base functions in a way that lets
+ * us fall back to the originals when we turn off.
+ */
+ var saveAndExtend = function ( base, extended ) {
+ var saved = {};
+ // $.map doesn't handle objects in jQuery < 1.6; need this for compat with MW 1.17
+ var map = function ( obj, callback ) {
+ for ( var key in extended ) {
+ if ( obj.hasOwnProperty( key ) ) {
+ callback( obj[key], key );
+ }
+ }
};
- $(window).resize(resize);
- // Use jquery.ui.resizable so user can make the box taller too
- container.resizable({
- handles: 's',
- minHeight: box.height(),
- resize: function() {
- context.codeEditor.resize();
- }
- });
-
- var summary = $('#wpSummary');
- // Let modules know we're ready to start working with the content
- context.fn.trigger( 'ready' );
- }
- },
-
- /**
- * Turn off the code editor view and return to the plain textarea.
- * May be needed by some folks with funky browsers, or just to compare.
- */
- 'disableCodeEditor': function() {
- // Kills it!
- context.$textarea.closest('form').unbind('submit', context.evt.codeEditorSubmit );
- if ( mw.hook ) {
- mw.hook( 'LivePreviewPrepare' ).remove( context.evt.codeEditorSubmit );
- }
- $( mw ).unbind( 'LivePreviewPrepare', context.evt.codeEditorSubmit ); // deprecated
-
- // Save contents
- context.$textarea.val(context.fn.getContents());
-
- // @todo fetch cursor, scroll position
-
- // Drop the fancy editor widget...
- context.$codeEditorContainer.remove();
- context.$codeEditorContainer = undefined;
- context.$iframe = undefined;
- context.codeEditor = undefined;
-
- // Restore textarea
- context.$textarea.show();
-
- // @todo restore cursor, scroll position
- },
-
- /**
- * Start monitoring the fragment of the current window for hash change
- * events. If the hash is already set, handle it as a new event.
- */
- 'codeEditorMonitorFragment': function() {
- function onHashChange() {
- var regexp = /#mw-ce-l(\d+)/;
- var result = regexp.exec( window.location.hash );
- if ( result === null ) {
- return;
- }
-
- // Line numbers in CodeEditor are zero-based
- var line = parseInt( result[1] );
- context.codeEditor.navigateTo( line - 1, 0 );
- // Scroll up a bit to give some context
- context.codeEditor.scrollToRow( line - 4 );
- }
- onHashChange();
- $( window ).bind( 'hashchange', onHashChange );
- }
-
-});
-
-/**
- * Override the base functions in a way that lets
- * us fall back to the originals when we turn off.
- */
-var saveAndExtend = function( base, extended ) {
- var saved = {};
- // $.map doesn't handle objects in jQuery < 1.6; need this for compat with MW 1.17
- var map = function( obj, callback ) {
- for (var key in extended ) {
- if ( obj.hasOwnProperty( key ) ) {
- callback( obj[key], key );
- }
- }
- };
- map( extended, function( func, name ) {
- if ( name in base ) {
- var orig = base[name];
- base[name] = function() {
- if (context.codeEditorActive) {
- return func.apply(this, arguments);
- } else if (orig) {
- return orig.apply(this, arguments);
+ map( extended, function ( func, name ) {
+ if ( name in base ) {
+ var orig = base[name];
+ base[name] = function () {
+ if ( context.codeEditorActive ) {
+ return func.apply( this, arguments );
+ } else if ( orig ) {
+ return orig.apply( this, arguments );
+ } else {
+ throw new Error( 'CodeEditor: no original function to call for ' + name );
+ }
+ };
} else {
- throw new Error('CodeEditor: no original function to call for ' + name);
+ base[name] = func;
}
- };
- } else {
- base[name] = func;
- }
- });
-};
-
-saveAndExtend( context.fn, {
- 'saveCursorAndScrollTop': function() {
- // Stub out textarea behavior
- return;
- },
- 'restoreCursorAndScrollTop': function() {
- // Stub out textarea behavior
- return;
- },
- 'saveSelection': function() {
- mw.log('codeEditor stub function saveSelection called');
- },
- 'restoreSelection': function() {
- mw.log('codeEditor stub function restoreSelection called');
- },
-
- /* Needed for search/replace */
- 'getContents': function() {
- return context.codeEditor.getSession().getValue();
- },
-
- /**
- * Compatibility with the $.textSelection jQuery plug-in. When the iframe is in use, these functions provide
- * equivilant functionality to the otherwise textarea-based functionality.
- */
-
- 'getElementAtCursor': function() {
- mw.log('codeEditor stub function getElementAtCursor called');
- },
-
- /**
- * Gets the currently selected text in the content
- * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
- */
- 'getSelection': function() {
- return context.codeEditor.getCopyText();
- },
- /**
- * Inserts text at the begining and end of a text selection, optionally inserting text at the caret when
- * selection is empty.
- * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
- */
- 'encapsulateSelection': function( options ) {
- // Does not yet handle 'ownline', 'splitlines' option
- var sel = context.codeEditor.getSelection();
- var range = sel.getRange();
- var selText = context.fn.getSelection();
- var isSample = false;
- if ( !selText ) {
- selText = options.peri;
- isSample = true;
- } else if ( options.replace ) {
- selText = options.peri;
- }
- var text = options.pre;
- text += selText;
- text += options.post;
- context.codeEditor.insert( text );
- if ( isSample && options.selectPeri && !options.splitlines ) {
- // May esplode if anything has newlines, be warned. :)
- range.setStart( range.start.row, range.start.column + options.pre.length );
- range.setEnd( range.start.row, range.start.column + selText.length );
- sel.setSelectionRange(range);
- }
- return context.$textarea;
- },
- /**
- * Gets the position (in resolution of bytes not nessecarily characters) in a textarea
- * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
- */
- 'getCaretPosition': function( options ) {
- mw.log('codeEditor stub function getCaretPosition called');
- },
- /**
- * Sets the selection of the content
- * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
- *
- * @param start Character offset of selection start
- * @param end Character offset of selection end
- * @param startContainer Element in iframe to start selection in. If not set, start is a character offset
- * @param endContainer Element in iframe to end selection in. If not set, end is a character offset
- */
- 'setSelection': function( options ) {
- // Ace stores positions for ranges as row/column pairs.
- // To convert from character offsets, we'll need to iterate through the document
- var doc = context.codeEditor.getSession().getDocument();
- var lines = doc.getAllLines();
-
- var offsetToPos = function( offset ) {
- var row = 0, col = 0;
- var pos = 0;
- while ( row < lines.length && pos + lines[row].length < offset) {
- pos += lines[row].length;
- pos++; // for the newline
- row++;
- }
- col = offset - pos;
- return {row: row, column: col};
+ } );
};
- var start = offsetToPos( options.start ),
- end = offsetToPos( options.end );
- var sel = context.codeEditor.getSelection();
- var range = sel.getRange();
- range.setStart( start.row, start.column );
- range.setEnd( end.row, end.column );
- sel.setSelectionRange( range );
- return context.$textarea;
- },
- /**
- * Scroll a textarea to the current cursor position. You can set the cursor position with setSelection()
- * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
- */
- 'scrollToCaretPosition': function( options ) {
- mw.log('codeEditor stub function scrollToCaretPosition called');
- return context.$textarea;
- },
- /**
- * Scroll an element to the top of the iframe
- * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
- *
- * @param $element jQuery object containing an element in the iframe
- * @param force If true, scroll the element even if it's already visible
- */
- 'scrollToTop': function( $element, force ) {
- mw.log('codeEditor stub function scrollToTop called');
- }
-} );
+ saveAndExtend( context.fn, {
+ 'saveCursorAndScrollTop': function () {
+ // Stub out textarea behavior
+ return;
+ },
+ 'restoreCursorAndScrollTop': function () {
+ // Stub out textarea behavior
+ return;
+ },
+ 'saveSelection': function () {
+ mw.log( 'codeEditor stub function saveSelection called' );
+ },
+ 'restoreSelection': function () {
+ mw.log( 'codeEditor stub function restoreSelection called' );
+ },
-/* Setup the editor */
-context.fn.setupCodeEditorToolbar();
-if (context.codeEditorActive) {
- context.fn.setupCodeEditor();
-}
+ /* Needed for search/replace */
+ 'getContents': function () {
+ return context.codeEditor.getSession().getValue();
+ },
-};
-} )( jQuery );
+ /**
+ * Compatibility with the $.textSelection jQuery plug-in. When the iframe is in use, these functions provide
+ * equivilant functionality to the otherwise textarea-based functionality.
+ */
+
+ 'getElementAtCursor': function () {
+ mw.log( 'codeEditor stub function getElementAtCursor called' );
+ },
+
+ /**
+ * Gets the currently selected text in the content
+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
+ */
+ 'getSelection': function () {
+ return context.codeEditor.getCopyText();
+ },
+ /**
+ * Inserts text at the begining and end of a text selection, optionally inserting text at the caret when
+ * selection is empty.
+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
+ */
+ 'encapsulateSelection': function ( options ) {
+ // Does not yet handle 'ownline', 'splitlines' option
+ var sel = context.codeEditor.getSelection();
+ var range = sel.getRange();
+ var selText = context.fn.getSelection();
+ var isSample = false;
+ if ( !selText ) {
+ selText = options.peri;
+ isSample = true;
+ } else if ( options.replace ) {
+ selText = options.peri;
+ }
+ var text = options.pre;
+ text += selText;
+ text += options.post;
+ context.codeEditor.insert( text );
+ if ( isSample && options.selectPeri && !options.splitlines ) {
+ // May esplode if anything has newlines, be warned. :)
+ range.setStart( range.start.row, range.start.column + options.pre.length );
+ range.setEnd( range.start.row, range.start.column + selText.length );
+ sel.setSelectionRange( range );
+ }
+ return context.$textarea;
+ },
+ /**
+ * Gets the position (in resolution of bytes not nessecarily characters) in a textarea
+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
+ */
+ 'getCaretPosition': function ( options ) {
+ mw.log( 'codeEditor stub function getCaretPosition called' );
+ },
+ /**
+ * Sets the selection of the content
+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
+ *
+ * @param start Character offset of selection start
+ * @param end Character offset of selection end
+ * @param startContainer Element in iframe to start selection in. If not set, start is a character offset
+ * @param endContainer Element in iframe to end selection in. If not set, end is a character offset
+ */
+ 'setSelection': function ( options ) {
+ // Ace stores positions for ranges as row/column pairs.
+ // To convert from character offsets, we'll need to iterate through the document
+ var doc = context.codeEditor.getSession().getDocument();
+ var lines = doc.getAllLines();
+
+ var offsetToPos = function ( offset ) {
+ var row = 0, col = 0;
+ var pos = 0;
+ while ( row < lines.length && pos + lines[row].length < offset ) {
+ pos += lines[row].length;
+ pos++; // for the newline
+ row++;
+ }
+ col = offset - pos;
+ return {row: row, column: col};
+ };
+ var start = offsetToPos( options.start ),
+ end = offsetToPos( options.end );
+
+ var sel = context.codeEditor.getSelection();
+ var range = sel.getRange();
+ range.setStart( start.row, start.column );
+ range.setEnd( end.row, end.column );
+ sel.setSelectionRange( range );
+ return context.$textarea;
+ },
+ /**
+ * Scroll a textarea to the current cursor position. You can set the cursor position with setSelection()
+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
+ */
+ 'scrollToCaretPosition': function ( options ) {
+ mw.log( 'codeEditor stub function scrollToCaretPosition called' );
+ return context.$textarea;
+ },
+ /**
+ * Scroll an element to the top of the iframe
+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
+ *
+ * @param $element jQuery object containing an element in the iframe
+ * @param force If true, scroll the element even if it's already visible
+ */
+ 'scrollToTop': function ( $element, force ) {
+ mw.log( 'codeEditor stub function scrollToTop called' );
+ }
+ } );
+
+ /* Setup the editor */
+ context.fn.setupCodeEditorToolbar();
+ if ( context.codeEditorActive ) {
+ context.fn.setupCodeEditor();
+ }
+
+ };
+})( jQuery );