Jump to content
MediaWiki

User:Iniquity/translateEditor.js

From mediawiki.org

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
 // WikiEditor Tranlate Integration
 // Integrates WikiEditor and CodeMirror into [[mw:Help:Extension:Translate]]
 // Author: [[User:Iniquity]]

 (function(){
 'use strict';

 // Wait for page load
 $(document).ready(function(){
 // Fast initialization
 initTranslateWikiEditor();

 // Track DOM changes for new forms
 observeNewForms();
 });

 functioninitTranslateWikiEditor(){
 // Find all translatewiki.net translation fields
 var$translationTextareas=$('textarea.tux-textarea-translation');

 if(!$translationTextareas.length){
 // Fast retry
 setTimeout(initTranslateWikiEditor,100);
 return;
 }

 // Initialize only visible fields that haven't been initialized yet
 $translationTextareas.each(function(){
 var$textarea=$(this);

 // Check that textarea is visible and not initialized
 if($textarea.is(':visible')&&!$textarea.data('wecm-tux-initialized')){
 initializeForm($textarea);
 }
 });
 }



 functioninitializeForm($textarea){
 // Check if this textarea is already initialized
 if($textarea.data('wecm-tux-initialized')){
 return;
 }

 // Set ID for compatibility (if not already set)
 if(!$textarea.attr('id')){
 $textarea.attr('id','wpTextbox1-'+Date.now());
 }

 // Load resources and setup editor
 loadResourcesAndSetup($textarea);

 // Mark textarea as initialized
 $textarea.data('wecm-tux-initialized',true);
 }

 functionloadResourcesAndSetup($textarea){
 // Check MediaWiki API availability
 if(typeofmw==='undefined'||!mw.loader){
 return;
 }

 // Load WikiEditor and CodeMirror
 mw.loader.using([
 'ext.wikiEditor',
 'ext.CodeMirror.v6.WikiEditor',
 'ext.CodeMirror.v6.mode.mediawiki'
 ]).then(function(require){
 if(typeofmw.addWikiEditor==='function'){
 try{
 mw.addWikiEditor($textarea);
 $textarea.wikiEditor({
 toolbar:{
 gadgets:{
 type:'group',
 label:'Гаджеты'
 },
 format:{},
 insert:{},
 advanced:{}
 }
 });

 // Attempt CodeMirror initialization (optional)
 try{
 constCodeMirrorWikiEditor=require('ext.CodeMirror.v6.WikiEditor');
 constmediawikiLang=require('ext.CodeMirror.v6.mode.mediawiki');

 if(typeofCodeMirrorWikiEditor==='function'&&typeofmediawikiLang==='function'){
 // Additional textarea checks before CodeMirror initialization
 if($textarea[0]&&
 $textarea[0].tagName==='TEXTAREA'&&
 $textarea[0].value!==null&&
 $textarea[0].name!==null&&
 $textarea[0].id!==null){

 // Save original properties for compatibility
 varoriginalTextarea=$textarea[0];
 varoriginalReadOnly=originalTextarea.readOnly;
 varoriginalDisabled=originalTextarea.disabled;

 constcmWe=newCodeMirrorWikiEditor($textarea[0],mediawikiLang());

 // Check that initialization was successful
 if(!cmWe||typeofcmWe.initialize!=='function'){
 return;
 }

 // Set mode manually if not set
 if(!cmWe.mode){
 cmWe.mode='mediawiki';
 }

 // Additional checks before initialization
 if(!cmWe.$textarea){
 cmWe.$textarea=$textarea;
 }

 if(!cmWe.context){
 // Create minimal context
 cmWe.context={
 $ui:$textarea.closest('.wikiEditor-ui'),
 modules:{
 toolbar:{
 $toolbar:$textarea.closest('.wikiEditor-ui').find('.toolbar')
 }
 }
 };
 }

 try{
 cmWe.initialize();
 }catch(initError){
 return;
 }

 // Check that view is created
 if(!cmWe.view||!cmWe.view.dom){
 return;
 }

 // Restore properties for compatibility
 Object.defineProperty(cmWe.view.dom,'readOnly',{
 get:function(){returnoriginalReadOnly;},
 set:function(value){originalReadOnly=value;}
 });

 Object.defineProperty(cmWe.view.dom,'disabled',{
 get:function(){returnoriginalDisabled;},
 set:function(value){originalDisabled=value;}
 });

 Object.defineProperty(cmWe.view.dom,'tagName',{
 get:function(){return'TEXTAREA';}
 });

 Object.defineProperty(cmWe.view.dom,'value',{
 get:function(){returncmWe.view.state.doc.toString();},
 set:function(value){
 cmWe.view.dispatch({
 changes:{from:0,to:cmWe.view.state.doc.length,insert:value}
 });
 }
 });

 // Save reference to editor
 $textarea.data('codeMirrorEditor',cmWe);

 // Add auto-resize functionality for CodeMirror
 setupAutoResize(cmWe,$textarea);

 // Add Ctrl+Enter (Cmd+Enter) save shortcut on .cm-content with capture:true
 varcmContent=$(cmWe.view.dom).find('.cm-content')[0];
 if(cmContent&&!$textarea.data('wecm-tux-capture-keydown')){
 $textarea.data('wecm-tux-capture-keydown',true);
 cmContent.addEventListener('keydown',function(e){
 constisCmdModifierPressed=$.client.profile().platform==='mac'?e.metaKey:e.ctrlKey;
 if(isCmdModifierPressed&&!e.shiftKey&&!e.altKey&&e.keyCode===13){
 e.preventDefault();
 e.stopPropagation();
 var$btn=$('.tux-editor-save-button').filter(function(){
 var$el=$(this);
 return$el.is(':visible')&&!$el.prop('disabled');
 });
 console.log('[edit-here] All candidate save buttons:',$('.tux-editor-save-button').toArray());
 if($btn.length){
 $btn.click();
 console.log('[edit-here] Save button clicked from cm-content capture');
 }else{
 console.log('[edit-here] Save button not found from cm-content capture');
 }
 returnfalse;
 }
 },true);
 }

 // Register custom keymap for Ctrl+Enter in CodeMirror (works even if event does not bubble)
 mw.loader.using('ext.CodeMirror.v6.lib').then(function(lib){
 if(cmWe&&cmWe.view&&typeofcmWe.view.applyExtension==='function'&&!$textarea.data('wecm-tux-keymap-registered')){
 $textarea.data('wecm-tux-keymap-registered',true);
 varsaveKeymap=lib.keymap.of([
 {
 key:"Ctrl-Enter",
 run:function(){
 var$btn=$('button[value="save"], input[value="save"], .save-button');
 if($btn.length){
 $btn.click();
 console.log('[edit-here] Save button clicked from keymap');
 }else{
 console.log('[edit-here] Save button not found from keymap');
 }
 returntrue;
 }
 }
 ]);
 cmWe.view.applyExtension(saveKeymap);
 }
 });

 // -- Fallback: MutationObserver for CodeMirror content changes --
 if(cmWe&&cmWe.view&&cmWe.view.dom&&!$textarea.data('wecm-tux-mutation-observer')){
 $textarea.data('wecm-tux-mutation-observer',true);
 varcmContent=$(cmWe.view.dom).find('.cm-content')[0];
 varcmEditor=$(cmWe.view.dom).closest('.cm-editor')[0]||$(cmWe.view.dom).find('.cm-editor')[0];
 if(cmContent){
 varlastText=cmWe.view.state.doc.toString();
 varsyncTimeout=null;
 varobserver=newMutationObserver(function(mutationsList,observer){
 clearTimeout(syncTimeout);
 syncTimeout=setTimeout(function(){
 varnewText=cmWe.view.state.doc.toString();
 if(newText!==lastText){
 lastText=newText;
 $textarea.css('display','block').css('visibility','visible');
 // Set direction and language from .cm-editor
 if(cmEditor){
 vardir=cmEditor.getAttribute('dir')||'ltr';
 varlang=cmEditor.getAttribute('lang')||'en';
 $textarea.attr('dir',dir);
 $textarea.attr('lang',lang);
 $(cmEditor).find('.cm-content').attr('dir',dir).attr('lang',lang);
 }
 varta=$textarea[0];
 ta.dispatchEvent(newEvent('input',{bubbles:true}));
 ta.dispatchEvent(newEvent('change',{bubbles:true}));
 // Do not change value, focus or blur!
 }
 },50);
 });
 observer.observe(cmContent,{characterData:true,subtree:true,childList:true});
 $textarea.data('wecm-tux-mutation-observer-instance',observer);
 }
 }

 }else{
 // Textarea not ready for CodeMirror
 }
 }
 }catch(cmError){
 // CodeMirror unavailable, continue without it
 }

 // Load wikificator and add to toolbar
 loadWikificatorAndSetup($textarea);

 // Add keyboard shortcuts
 addKeyboardShortcuts($textarea);

 // Add syntax button handler
 addSyntaxButtonHandler($textarea);

 // Remove margin from all group-format on page after initialization
 setTimeout(function(){
 $('.wikiEditor-ui .group-format').each(function(){
 this.style.setProperty('margin-left','0','important');
 });
 },100);

 }catch(e){
 // Error initializing WikiEditor
 }
 }

 }).catch(function(error){
 // Failed to load WikiEditor, CodeMirror
 });
 }

 functionsetupAutoResize(cmWe,$textarea){
 // Function to automatically resize CodeMirror editor based on content
 functionautoResize(){
 if(!cmWe||!cmWe.view||!cmWe.view.dom){
 return;
 }

 // Get the CodeMirror editor element
 var$cmEditor=$(cmWe.view.dom).closest('.cm-editor');
 if(!$cmEditor.length){
 return;
 }

 // Get current content and calculate lines
 varcontent=cmWe.view.state.doc.toString();
 varlines=content.split('\n');
 varlineCount=lines.length;

 // Calculate minimum and maximum heights
 varminHeight=150;// Minimum height in pixels
 varmaxHeight=600;// Maximum height in pixels
 varlineHeight=20;// Approximate line height in pixels
 varpadding=40;// Padding for editor

 // Calculate additional height for long lines that wrap
 vareditorWidth=$cmEditor.width()||600;// Get current editor width
 varcharWidth=8;// Approximate character width in pixels
 varwidthPadding=20;// Account for padding and scrollbars
 varcharsPerLine=Math.floor((editorWidth-widthPadding)/charWidth);

 vartotalWrappedLines=0;
 for(vari=0;i<lines.length;i++){
 varline=lines[i];

 // Calculate actual line width considering different character types
 varlineWidth=0;
 for(varj=0;j<line.length;j++){
 varchar=line[j];
 // Adjust width for different character types
 if(char.charCodeAt(0)>127){
 // Non-ASCII characters (like Cyrillic) are wider
 lineWidth+=charWidth*1.2;
 }elseif(char===' '||char==='\t'){
 // Spaces and tabs
 lineWidth+=charWidth*0.5;
 }else{
 // Regular ASCII characters
 lineWidth+=charWidth;
 }
 }

 // Check if line needs wrapping
 if(lineWidth>editorWidth-widthPadding){
 // Calculate how many lines this long line will wrap to
 varwrappedLines=Math.ceil(lineWidth/(editorWidth-widthPadding));
 totalWrappedLines+=(wrappedLines-1);// Subtract 1 because we already count the original line
 }
 }

 // Add extra height for wrapped lines
 varwrappedHeight=totalWrappedLines*lineHeight;
 varfinalHeight=Math.max(minHeight,Math.min(maxHeight,lineCount*lineHeight+wrappedHeight+padding));

 // Apply new height to CodeMirror editor
 $cmEditor.css({
 'height':finalHeight+'px',
 'min-height':minHeight+'px',
 'max-height':maxHeight+'px',
 'overflow-y':finalHeight>=maxHeight?'auto':'hidden'
 });

 // Also update the textarea height for compatibility
 $textarea.css({
 'height':finalHeight+'px',
 'min-height':minHeight+'px',
 'max-height':maxHeight+'px'
 });

 // Force CodeMirror to update its layout
 if(cmWe.view.requestMeasure){
 cmWe.view.requestMeasure();
 }
 }

 // Initial resize
 setTimeout(autoResize,100);

 // Listen for content changes using CodeMirror's update event
 if(cmWe.view&&cmWe.view.state){
 try{
 // Try to use CodeMirror's built-in update listener
 mw.loader.using('@codemirror/view').then(function(viewModule){
 if(cmWe.view&&cmWe.view.state){
 // Add update listener
 varupdateListener=viewModule.EditorView.updateListener.of(function(update){
 if(update.docChanged){
 setTimeout(autoResize,10);
 }
 });

 // Apply the listener
 cmWe.view.dispatch({
 effects:updateListener
 });
 }
 }).catch(function(){
 // Fallback to MutationObserver if CodeMirror view module not available
 setupMutationObserver();
 });
 }catch(e){
 // Fallback to MutationObserver
 setupMutationObserver();
 }
 }else{
 // Fallback to MutationObserver
 setupMutationObserver();
 }

 functionsetupMutationObserver(){
 var$cmContent=$(cmWe.view.dom).find('.cm-content');
 if($cmContent.length){
 varobserver=newMutationObserver(function(){
 setTimeout(autoResize,10);
 });
 observer.observe($cmContent[0],{
 childList:true,
 subtree:true,
 characterData:true
 });
 $textarea.data('wecm-tux-resize-observer',observer);
 }
 }

 // Also listen for window resize events
 $(window).off('resize.wecm-tux-resize').on('resize.wecm-tux-resize',function(){
 setTimeout(autoResize,100);
 });

 // Listen for input events on the CodeMirror editor for faster response
 var$cmEditor=$(cmWe.view.dom).closest('.cm-editor');
 if($cmEditor.length){
 $cmEditor.off('input.wecm-tux-resize keyup.wecm-tux-resize').on('input.wecm-tux-resize keyup.wecm-tux-resize',function(){
 setTimeout(autoResize,50);
 });
 }
 }

 functionloadWikificatorAndSetup($textarea){
 // Load wikificator directly from ru.wikipedia.org
 loadWikificatorFallback($textarea);
 }

 functionloadWikificatorFallback($textarea){
 // Fallback: load via $.getScript
 $.getScript('//ru.wikipedia.org/w/index.php?title=MediaWiki:Gadget-wikificator.js&action=raw&ctype=text/javascript')
 .done(function(){
 addWikificatorToToolbar($textarea);
 })
 .fail(function(jqXHR,textStatus,errorThrown){
 // Wikificator not loaded
 });
 }

 functionaddWikificatorToToolbar($textarea){
 // Wait for toolbar readiness (like in edit-here-config.js)
 var$wikiEditorUI=$textarea.closest('.wikiEditor-ui');
 vartoolbarReady=$wikiEditorUI.length&&$wikiEditorUI.find('.toolbar').length;

 if(toolbarReady){
 try{
 vargadgetsTools={
 wikificator:{
 label:'Викификатор — автоматический обработчик текста (Ctrl+Alt+W)',
 type:'button',
 icon:'https://upload.wikimedia.org/wikipedia/commons/0/06/Wikify-toolbutton.png',
 action:{
 type:'callback',
 execute:function(){
 if(typeofwindow.Wikify==='function'){
 window.Wikify($textarea[0]);
 }else{
 // Wikify not found
 }
 }
 }
 }
 };

 $textarea.wikiEditor('addToToolbar',{
 section:'main',
 groups:{
 gadgets:{
 tools:gadgetsTools
 }
 }
 });

 // Move gadgets group to the beginning of toolbar
 $wikiEditorUI.find('.group-gadgets').insertBefore($wikiEditorUI.find('.section-main .group-format'));

 // Remove temporary margin from group-format
 $wikiEditorUI.find('.group-format').each(function(){
 this.style.setProperty('margin-left','0','important');
 });

 // Remove CSS rule from styles
 $('style').each(function(){
 var$style=$(this);
 varcssText=$style.text();
 if(cssText.includes('.wikiEditor-ui .group-format')&&cssText.includes('margin-left: 34px !important')){
 varnewCssText=cssText.replace(/\.wikiEditor-ui \.group-format\s*\{\s*margin-left:\s*34px\s*!important;\s*\}/g,'');
 $style.text(newCssText);
 }
 });

 // Remove margin from all group-format on page
 $('.wikiEditor-ui .group-format').each(function(){
 this.style.setProperty('margin-left','0','important');
 });

 }catch(e){
 // Error integrating Wikificator
 }
 }else{
 // Retry after 500ms
 setTimeout(function(){
 addWikificatorToToolbar($textarea);
 },500);
 }
 }

 functionaddKeyboardShortcuts($textarea){
 // Remove previous handlers
 $textarea.off('keydown.wecm-tux');
 // Add to textarea (in case focus is there)
 $textarea.on('keydown.wecm-tux',handleShortcut);

 // Add to CodeMirror content if present
 varcmWe=$textarea.data('codeMirrorEditor');
 if(cmWe&&cmWe.view&&cmWe.view.dom){
 var$cmContent=$(cmWe.view.dom).find('.cm-content');
 $cmContent.off('keydown.wecm-tux');
 $cmContent.on('keydown.wecm-tux',handleShortcut);
 }

 functionhandleShortcut(e){
 constisCmdModifierPressed=$.client.profile().platform==='mac'?e.metaKey:e.ctrlKey;

 // Ctrl+Enter for save
 if(isCmdModifierPressed&&!e.shiftKey&&!e.altKey&&e.keyCode===13){
 e.preventDefault();
 e.stopPropagation();
 $('button[value="save"], input[value="save"], .save-button').click();
 returnfalse;
 }
 // Ctrl+Alt+W for wikificator
 if(isCmdModifierPressed&&!e.shiftKey&&e.altKey&&e.keyCode===87){
 e.preventDefault();
 e.stopPropagation();
 if(typeofwindow.Wikify==='function'){
 window.Wikify($textarea[0]);
 }
 returnfalse;
 }
 // ALT+SHIFT+D for skip
 if(!isCmdModifierPressed&&e.shiftKey&&e.altKey&&e.keyCode===68){
 e.preventDefault();
 e.stopPropagation();
 $('.tux-editor-skip-button').click();
 returnfalse;
 }
 // ALT+SHIFT+B for edit summary
 if(!isCmdModifierPressed&&e.shiftKey&&e.altKey&&e.keyCode===66){
 e.preventDefault();
 e.stopPropagation();
 $('.tux-input-editsummary').focus();
 returnfalse;
 }
 }
 }



 functionrunWikificator(textareaElement){
 if(typeofwindow.Wikify==='function'){
 window.Wikify(textareaElement);
 showNotification('Wikificator started','success');
 }else{
 showNotification('Wikificator unavailable','error');
 }
 }


 // Add CSS styles for proper WikiEditor and CodeMirror display
 functionaddStyles(){
 varstyles=`
  /* Force show toolbar and WikiEditor tabs even for readonly/inactive CodeMirror */
  .wikiEditor-ui.ext-codemirror-readonly .wikiEditor-section-secondary,
  .wikiEditor-ui:not(.ext-codemirror-mediawiki) .wikiEditor-section-secondary,
  .wikiEditor-ui.ext-codemirror-readonly .tabs,
  .wikiEditor-ui:not(.ext-codemirror-mediawiki) .tabs,
  .wikiEditor-ui.ext-codemirror-readonly .sections,
  .wikiEditor-ui:not(.ext-codemirror-mediawiki) .sections {
  display: block !important;
  }

  .wikiEditor-ui:not(.ext-codemirror-mediawiki) .group:not(.group-codemirror):not(.group-codemirror-format):not(.group-codemirror-preferences):not(.group-codemirror-search),
  .wikiEditor-ui.ext-codemirror-readonly .group:not(.group-codemirror):not(.group-codemirror-format):not(.group-codemirror-preferences):not(.group-codemirror-search) {
  display: flex;
  }

  /* Proper cursor for text fields */
  .wikiEditor-ui textarea,
  .wikiEditor-ui .wikiEditor-ui-text textarea,
  .wikiEditor-ui .wikiEditor-ui-text .wikiEditor-ui-text textarea {
  cursor: text !important;
  }

  /* Cursor for CodeMirror */
  .wikiEditor-ui .cm-editor {
  cursor: text !important;
  transition: height 0.2s ease-in-out;
  }

  /* Auto-resize styles for CodeMirror */
  .wikiEditor-ui .cm-editor.cm-focused {
  outline: none !important;
  }

  .wikiEditor-ui .cm-editor .cm-scroller {
  overflow-x: auto !important;
  }

  .wikiEditor-ui .cm-editor .cm-content {
  white-space: pre-wrap !important;
  word-wrap: break-word !important;
  word-break: break-word !important;
  overflow-wrap: break-word !important;
  }

  /* Cursor for buttons and controls */
  .wikiEditor-ui .tool,
  .wikiEditor-ui .toolbar .tool,
  .wikiEditor-ui button,
  .wikiEditor-ui input[type="button"],
  .wikiEditor-ui input[type="submit"] {
  cursor: pointer !important;
  }

  /* Reserve space for wikificator button */
  .wikiEditor-ui .group-gadgets {
  min-width: 34px !important;
  }

  /* Temporarily move group-format */
  .wikiEditor-ui .group-format {
  margin-left: 34px !important;
  }
  `;

 var$style=$('<style>').text(styles);
 $('head').append($style);
 }

 functioncleanupPreviousEditors(){
 // Clean up previous CodeMirror editors and observers
 $('textarea.tux-textarea-translation').each(function(){
 var$textarea=$(this);

 // Clean up resize observer
 varresizeObserver=$textarea.data('wecm-tux-resize-observer');
 if(resizeObserver&&typeofresizeObserver.disconnect==='function'){
 resizeObserver.disconnect();
 $textarea.removeData('wecm-tux-resize-observer');
 }

 // Clean up mutation observer
 varmutationObserver=$textarea.data('wecm-tux-mutation-observer-instance');
 if(mutationObserver&&typeofmutationObserver.disconnect==='function'){
 mutationObserver.disconnect();
 $textarea.removeData('wecm-tux-mutation-observer-instance');
 }

 // Remove event handlers
 $textarea.off('keydown.wecm-tux');
 $(window).off('resize.wecm-tux-resize');

 // Remove CodeMirror editor event handlers
 varcmWe=$textarea.data('codeMirrorEditor');
 if(cmWe&&cmWe.view&&cmWe.view.dom){
 var$cmEditor=$(cmWe.view.dom).closest('.cm-editor');
 if($cmEditor.length){
 $cmEditor.off('input.wecm-tux-resize keyup.wecm-tux-resize');
 }
 }

 // Reset textarea styles
 $textarea.css({
 'height':'',
 'min-height':'',
 'max-height':''
 });

 // Remove CodeMirror editor reference
 $textarea.removeData('codeMirrorEditor');
 $textarea.removeData('wecm-tux-initialized');
 });
 }

 functionobserveNewForms(){
 // Fast tracking of clicks on links for new form initialization
 $(document).on('click','a[href*="action=translate"], .tux-editor-link',function(){
 // Clean up previous editors before switching
 cleanupPreviousEditors();

 // Add margin when switching forms
 addMarginToGroupFormat();

 setTimeout(function(){
 initTranslateWikiEditor();
 },100);
 });

 // More frequent check for new forms (every 500ms)
 setInterval(function(){
 initTranslateWikiEditor();
 },500);
 }

 functionaddMarginToGroupFormat(){
 // Add margin to all group-format on page
 $('.wikiEditor-ui .group-format').each(function(){
 this.style.setProperty('margin-left','34px','important');
 });

 // Add CSS rule back
 var$existingStyle=$('style').filter(function(){
 return$(this).text().includes('wecm-tux-wikieditor');
 }).first();

 if($existingStyle.length){
 varcssText=$existingStyle.text();
 if(!cssText.includes('.wikiEditor-ui .group-format')||!cssText.includes('margin-left: 34px !important')){
 varnewRule='\n /* Temporarily move group-format */\n .wikiEditor-ui .group-format {\n margin-left: 34px !important;\n }';
 $existingStyle.text(cssText+newRule);
 }
 }
 }



 // Add styles on initialization
 addStyles();

 // Global Ctrl+Enter handler for save (works even if CodeMirror eats the event)
 $(document).off('keydown.wecm-tux-global').on('keydown.wecm-tux-global',function(e){
 constisCmdModifierPressed=$.client.profile().platform==='mac'?e.metaKey:e.ctrlKey;
 if(isCmdModifierPressed&&!e.shiftKey&&!e.altKey&&e.keyCode===13){
 console.log('[edit-here] Global Ctrl+Enter handler fired');
 e.preventDefault();
 e.stopPropagation();
 var$btn=$('button[value="save"], input[value="save"], .save-button');
 console.log('[edit-here] Save button:',$btn[0],'disabled:',$btn.prop('disabled'),'opacity:',$btn.css('opacity'),'class:',$btn.attr('class'));
 if($btn.length){
 $btn.click();
 console.log('[edit-here] Save button clicked');
 }else{
 console.log('[edit-here] Save button not found');
 }
 returnfalse;
 }
 });

 })();

AltStyle によって変換されたページ (->オリジナル) /