( function ( mw, $ ) { var useCodeMirror, codeMirror, api, originHooksTextarea, cmTextSelection, enableContentEditable = true; if ( mw.config.get( 'wgCodeEditorCurrentLanguage' ) ) { // If the CodeEditor is used then just exit; return; } // Exit if WikiEditor is disabled // usebetatoolbar can be the string "0" if the user disabled the preference - Bug T54542#555387 if ( !( mw.loader.getState( 'ext.wikiEditor' ) && mw.user.options.get( 'usebetatoolbar' ) > 0 ) ) { return; } useCodeMirror = mw.user.options.get( 'usecodemirror' ) > 0; api = new mw.Api(); originHooksTextarea = $.valHooks.textarea; // define jQuery hook for searching and replacing text using JS if CodeMirror is enabled, see Bug: T108711 $.valHooks.textarea = { get: function ( elem ) { if ( elem.id === 'wpTextbox1' && codeMirror ) { return codeMirror.doc.getValue(); } else if ( originHooksTextarea ) { return originHooksTextarea.get( elem ); } return elem.value; }, set: function ( elem, value ) { if ( elem.id === 'wpTextbox1' && codeMirror ) { return codeMirror.doc.setValue( value ); } else if ( originHooksTextarea ) { return originHooksTextarea.set( elem, value ); } elem.value = value; } }; // Disable spellchecking for Firefox users on non-Mac systems (Bug T95104) if ( navigator.userAgent.indexOf( 'Firefox' ) > -1 && navigator.userAgent.indexOf( 'Mac' ) === -1 ) { enableContentEditable = false; } // T174055: Do not redefine the browser history navigation keys (T175378: for PC only) CodeMirror.keyMap.pcDefault[ 'Alt-Left' ] = false; CodeMirror.keyMap.pcDefault[ 'Alt-Right' ] = false; // jQuery.textSelection overrides for CodeMirror. // See jQuery.textSelection.js for method documentation cmTextSelection = { getContents: function () { return codeMirror.doc.getValue(); }, setContents: function ( content ) { codeMirror.doc.setValue( content ); return this; }, getSelection: function () { return codeMirror.doc.getSelection(); }, setSelection: function ( options ) { codeMirror.focus(); codeMirror.doc.setSelection( codeMirror.doc.posFromIndex( options.start ), codeMirror.doc.posFromIndex( options.end ) ); return this; }, replaceSelection: function ( value ) { codeMirror.doc.replaceSelection( value ); return this; }, getCaretPosition: function ( options ) { var caretPos = codeMirror.doc.indexFromPos( codeMirror.doc.getCursor( true ) ), endPos = codeMirror.doc.indexFromPos( codeMirror.doc.getCursor( false ) ); if ( options.startAndEnd ) { return [ caretPos, endPos ]; } return caretPos; }, scrollToCaretPosition: function () { codeMirror.scrollIntoView( null ); return this; } }; /** * Save CodeMirror enabled pref. * * @param {boolean} prefValue True, if CodeMirror should be enabled by default, otherwise false. */ function setCodeEditorPreference( prefValue ) { useCodeMirror = prefValue; // Save state for function updateToolbarButton() if ( mw.user.isAnon() ) { // Skip it for anon users return; } api.saveOption( 'usecodemirror', prefValue ? 1 : 0 ); mw.user.options.set( 'usecodemirror', prefValue ? 1 : 0 ); } /** * Replaces the default textarea with CodeMirror */ function enableCodeMirror() { var config = mw.config.get( 'extCodeMirrorConfig' ); mw.loader.using( config.pluginModules, function () { var $codeMirror, $textbox1 = $( '#wpTextbox1' ), selectionStart = $textbox1.prop( 'selectionStart' ), selectionEnd = $textbox1.prop( 'selectionEnd' ), scrollTop = $textbox1.scrollTop(); // If CodeMirror is already loaded or wikEd gadget is enabled, abort. See T178348. // FIXME: Would be good to replace the wikEd check with something more generic. if ( codeMirror || mw.user.options.get( 'gadget-wikEd' ) > 0 ) { return; } codeMirror = CodeMirror.fromTextArea( $textbox1[ 0 ], { mwConfig: config, // styleActiveLine: true, // disabled since Bug: T162204, maybe should be optional lineWrapping: true, readOnly: $textbox1[ 0 ].readOnly, // select mediawiki as text input mode mode: 'text/mediawiki', extraKeys: { Tab: false, 'Shift-Tab': false, // T174514: Move the cursor at the beginning/end of the current wrapped line Home: 'goLineLeft', End: 'goLineRight' }, inputStyle: enableContentEditable ? 'contenteditable' : 'textarea', spellcheck: enableContentEditable, viewportMargin: Infinity } ); $codeMirror = $( codeMirror.getWrapperElement() ); // Allow textSelection() functions to work with CodeMirror editing field. $codeMirror.textSelection( 'register', cmTextSelection ); // Also override textSelection() functions for the "real" hidden textarea to route to // CodeMirror. We unregister this when switching to normal textarea mode. $textbox1.textSelection( 'register', cmTextSelection ); $codeMirror.resizable( { handles: 'se', resize: function ( event, ui ) { ui.size.width = ui.originalSize.width; } } ); codeMirror.doc.setSelection( codeMirror.doc.posFromIndex( selectionEnd ), codeMirror.doc.posFromIndex( selectionStart ) ); codeMirror.scrollTo( null, scrollTop ); // HACK: