User:Polygnotus/Scripts/ReplyButtonsv4.js
(function() {
if (mw.config.get('wgDiscussionToolsFeaturesEnabled')) {
mw.loader.using(['ext.discussionTools.ReplyWidget', 'mediawiki.api'], () => {
// Improved configuration loading with caching
async function loadToolsConfig() {
const username = mw.config.get('wgUserName');
if (!username) return [];
const api = new mw.Api();
try {
const result = await api.get({
action: 'query',
prop: 'revisions',
titles: `User:${username}/ReplyButtonsJSON`,
rvslots: '*',
rvprop: 'content',
formatversion: '2',
uselang: 'content', // Enable caching
smaxage: '86400', // Cache for 1 day
maxage: '86400' // Cache for 1 day
});
if (result.query.pages[0].missing) {
return [];
}
const content = result.query.pages[0].revisions[0].slots.main.content;
return JSON.parse(content);
} catch (error) {
console.error('Error loading reply buttons configuration:', error);
return [];
}
}
// Helper function to create tools from config
function createToolFromConfig(config) {
// Add error handling for icon URLs
if (config.icon?.startsWith('http')) {
try {
addCustomIconCSS(config.name, config.icon);
} catch (error) {
console.warn(`Failed to add custom icon for ${config.name}:`, error);
config.icon = 'help'; // Fallback icon
}
}
// Create command with improved error handling
const CommandClass = function() {
ve.ui.Command.call(this, config.name);
};
OO.inheritClass(CommandClass, ve.ui.Command);
CommandClass.prototype.execute = function() {
try {
const target = ve.init.target;
if (!target) {
console.warn('Visual editor target not found');
return false;
}
const surface = target.getSurface();
const surfaceModel = surface.getModel();
const content = typeof config.insertContent === 'function'
? config.insertContent()
: config.insertText.replace(/'\s*\+\s*'/g, '');
surfaceModel.getFragment()
.collapseToEnd()
.insertContent(content)
.collapseToEnd()
.select();
return true;
} catch (error) {
console.error(`Error executing command ${config.name}:`, error);
return false;
}
};
ve.ui.commandRegistry.register(new CommandClass());
// Create tool with improved validation
const ToolClass = function() {
ve.ui.Tool.apply(this, arguments);
};
OO.inheritClass(ToolClass, ve.ui.Tool);
ToolClass.static.name = config.name;
ToolClass.static.title = config.title || config.name;
ToolClass.static.commandName = config.name;
ToolClass.static.icon = config.icon?.startsWith('http')
? 'custom-' + config.name
: (config.icon || 'help');
ve.ui.toolFactory.register(ToolClass);
// Add to toolbar groups with improved module detection
const replyWidget = mw.loader.moduleRegistry['ext.discussionTools.ReplyWidget']
?.packageExports?.['dt-ve/CommentTarget.js'];
const newTopicWidget = mw.loader.moduleRegistry['ext.discussionTools.NewTopic']
?.packageExports?.['dt-ve/NewTopicTarget.js'];
if (replyWidget?.static?.toolbarGroups) {
const toolbarGroup = replyWidget.static.toolbarGroups[3];
if (toolbarGroup && Array.isArray(toolbarGroup.include)) {
toolbarGroup.include.push(config.name);
}
}
if (newTopicWidget?.static?.toolbarGroups) {
let customGroup = newTopicWidget.static.toolbarGroups.find(g => g.name === 'custom');
if (!customGroup) {
customGroup = {
name: 'custom',
include: [],
demote: ['custom']
};
newTopicWidget.static.toolbarGroups.push(customGroup);
}
if (Array.isArray(customGroup.include)) {
customGroup.include.push(config.name);
}
}
}
// Function to add custom CSS with error handling
function addCustomIconCSS(name, iconUrl) {
const styleId = `custom-icon-${name}`;
if (!document.getElementById(styleId)) {
const style = document.createElement('style');
style.id = styleId;
style.textContent = `
.oo-ui-icon-custom-${name} {
background-image: url(${iconUrl}) !important;
background-size: contain !important;
background-position: center !important;
background-repeat: no-repeat !important;
}
`;
document.head.appendChild(style);
}
}
// Initialize tools with improved error handling
loadToolsConfig().then(tools => {
if (Array.isArray(tools) && tools.length > 0) {
tools.forEach(config => {
try {
createToolFromConfig(config);
} catch (error) {
console.error(`Failed to create tool from config:`, config, error);
}
});
}
}).catch(error => {
console.error('Failed to load tools configuration:', error);
});
});
}
})();