Справица:DiagramGenerator.js — разлика између измена

Извор: SI Wiki
Пређи на навигацију Пређи на претрагу
(Spravica u testiranju; nekome možda bude korisna)
 
м (Nova podrazumevana vrednost visine stranice)
 
(12 међуизмена истог корисника није приказано)
Ред 1: Ред 1:
/* eslint {"max-statements": ["error", 100]} */
(function() {
(function() {
     'use strict';
     'use strict';
    var lib = mw.libs.diagramGenerator;
     var fileContents = {};
     var fileContents = {};
     var signals = [
 
        'MOST1_2',
     function msg(message) {
        'MOST1_3',
        var prefixedMessage = 'gadget-diagram-generator-' + message;
        'MOST2_1',
         var args = Array.prototype.slice.call(arguments, 1);
        'MOST3_2',
         var mwMsg = mw.message.apply(mw.message, [prefixedMessage].concat(args));
        'rdCPU',
        return mwMsg.plain();
        'wrCPU',
     }
        'ldMDR',
        'mxMDR',
        'MDRout1',
        'eMDR',
        'ldMAR',
        'incMAR',
        'eMAR',
        'ldDWL',
        'ldDWH',
        'DWout2',
        'wrGPR',
        'GPRout1',
        'ldGPRAR',
        'incGPRAR',
        'ldSP',
        'SPout2',
        'incSP',
        'DECSP',
        'ldCW',
        'CWout3',
        'ADDout2',
        'ldPC',
        'incPC',
        'PCHout3',
        'PCLout3',
        'PCout1',
        'ldIR0',
        'ldIR1',
        'ldIR2',
        'ldIR3',
         'IRPOMout3',
        'IRJAout2',
        'IRDAout3',
        'IRBRout3',
        'add',
        'sub',
        'inc',
        'dec',
        'and',
        'or',
        'xor',
        'not',
        'ALUout1',
        'ldAB',
        'ABout3',
        'shr',
        'shl',
        'ldBB',
        'BBout2',
        'ldAW',
        'AWout3',
        'AWHout3',
        'ldBW',
        'BWout2',
        'ldPSWH',
        'ldPSWL',
        'ldN',
        'ldZ',
        'ldC',
        'ldV',
        'ldL',
        'stPSWI',
        'clPSWI',
        'stPSWT',
        'clPSWT',
        'PSWHout3',
        'PSWLout3',
        'clSTART',
        'ldIMR',
        'IMRout2',
        'ldBR',
        'IVTDSPout3',
        'ldIVTP',
        'IVTPout1',
        'UINTout3',
        'UEXTout3',
        'stPRCOD',
        'stPRADR',
        'stPRINS',
        'clPRCOD',
        'clPRADR',
        'clPRINS',
        'clPRINM',
        'clINTR',
        'stPSWR',
        'clPSWR'
    ];
    var BranchType = {
        NONE: 'none',
        CONDITIONAL: 'conditional',
        UNCONDITIONAL: 'unconditional',
         BRADR: 'bradr',
        BROPR: 'bropr'
    };
    var CONDITIONAL_BRANCH = /^br\s*\(\s*if\s+(\S+)\s+then\s+(\S+)\s*\)$/;
    var UNCONDITIONAL_BRANCH = /^br\s+(\S+)$/;
     var MAX_LINE_LENGTH = 22;
    var FIELDS = [
        ['Ime i prezime', 'Indeks', 'Potpis'],
        ['Naziv', 'Datum', 'Strana']
    ];


     function updateSubmitEnabled() {
     function updateSubmitEnabled() {
         if (fileContents.microcode && fileContents.configuration) {
         if (fileContents.microcode && fileContents.configuration) {
             $('#diagram-generator-submit').removeAttr('disabled');
             $('.diagram-generator-action')
                .removeAttr('disabled')
                .parent()
                .removeClass('oo-ui-widget-disabled')
                .addClass('oo-ui-widget-enabled');
         } else {
         } else {
             $('#diagram-generator-submit').attr('disabled', '');
             $('.diagram-generator-action')
                .attr('disabled', '')
                .parent()
                .removeClass('oo-ui-widget-enabled')
                .addClass('oo-ui-widget-disabled');
         }
         }
     }
     }
Ред 138: Ред 45:
     }
     }


     function parseConfig(config) {
     function downloadSVG(svg) {
         try {
         var contents = [
             return config.split('\n').map(function(line) {
             '<?xml version="1.0" encoding="utf-8"?>',
                return line.replace(/\/\/.*/g, '').trim();
            '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">',
             }).filter(Boolean).map(function(line) {
            svg.outerHTML
                return JSON.parse('[' + line + ']');
        ].join('\n');
            });
        var blob = new Blob([contents], {
         } catch (error) {
             type: 'image/svg+xml'
            console.error(error);
        });
            return null;
        var blobUrl = URL.createObjectURL(blob);
        }
        var downloadLink = document.createElement('a');
        downloadLink.href = blobUrl;
        downloadLink.download = 'Diagram.svg';
        document.body.appendChild(downloadLink);
         downloadLink.click();
        URL.revokeObjectURL(blobUrl);
        downloadLink.remove();
     }
     }


     function findConfig(config, prefix) {
     function printPages(width, height, pages) {
         return config.filter(function(line) {
         var imageWindow = window.open('');
             return !line.slice(0, prefix.length).some(function(value, index) {
        pages.forEach(function(page) {
                return value !== prefix[index];
             var svg = lib.createSVG(width, height);
             });
            svg.appendChild(page);
        }).map(function(line) {
            svg.style.breakAfter = 'page';
            return line.slice(prefix.length);
             imageWindow.document.write(svg.outerHTML);
         });
         });
        imageWindow.document.close();
        imageWindow.focus();
        imageWindow.print();
     }
     }


     function prepareLine(line) {
     function postGeneration(parameters, actionId, pages) {
         return line
         switch (actionId) {
            .toLowerCase()
             case 'diagram-generator-this-page':
             .replace(/\(/g, ' ( ')
                var svg = lib.createSVGWithPages(parameters.width, parameters.height, pages);
            .replace(/\)/g, ' ) ')
                $('#diagram-generator-result').html(svg.outerHTML);
            .replace(/\s+/g, ' ')
                break;
            .replace(/if !/g, 'if #')
             case 'diagram-generator-new-page':
            .replace(/(.*?)!.*/g, '$1')
                printPages(parameters.width, parameters.height, pages);
            .replace(/(.*?);.*/, '$1')
                break;
             .replace(/\t/g, ' ')
            case 'diagram-generator-download-svg':
            .replace(/\s+:/g, ': ')
                downloadSVG(lib.createSVGWithPages(parameters.width, parameters.height, pages));
            .replace(/\s+/g, ' ')
                break;
            .trim();
             default:
    }
                // ???
 
    function parseAddress(line) {
        if (line === '') {
            return -1;
        }
        var addressLine = line.split(' ')[0];
        if (addressLine.endsWith(':') || !addressLine.startsWith('madr')) {
            return -1;
        }
        var address = parseInt(addressLine.substring(4), 16);
        if (isNaN(address)) {
            return -1;
        }
        return address;
    }
 
    function parseLabels(line) {
        var labels = [];
        var splitLine = line.split(' ');
        for (var i = 0, l = splitLine.length; i < l; ++i) {
             if (!splitLine[i].endsWith(':')) {
                 break;
                 break;
            }
            var label = splitLine[i].slice(0, -1);
            if (!labels.includes(label)) {
                labels.push(label);
            }
         }
         }
        return labels;
     }
     }


     function parseConditions(config) {
     function getParameters() {
         return findConfig(config, ['CONTRODC']).filter(function(option) {
         return {
             return option[1];
            arrowHeight: Number($('#diagram-generator-arrow-height').val()),
        }).map(function(option) {
            columnHeaderLeft: $('#diagram-generator-left-column-header').val(),
             var segments = option[1].split('.');
            columnHeaderMiddle: $('#diagram-generator-middle-column-header').val(),
             return option[2] + segments[segments.length - 1].toLowerCase();
             columnHeaderRight: $('#diagram-generator-right-column-header').val(),
        });
             headerHeight: Number($('#diagram-generator-header-height').val()),
    }
             height: Number($('#diagram-generator-page-height').val()),
 
            ignoreErrors: false,
    function parseLine(comment, line, config) {
            indexNum: $('#diagram-generator-student-index').val(),
        var preparedLine = prepareLine(line);
            footerRowHeight: Number($('#diagram-generator-footer-row-height').val()),
        var instruction = {
            margin: Number($('#diagram-generator-margin').val()),
             address: parseAddress(preparedLine),
             nameSurname: $('#diagram-generator-student-name').val(),
             comment: comment,
             subjectName: $('#diagram-generator-subject-name').val(),
             line: line
             width: Number($('#diagram-generator-page-width').val())
         };
         };
        var unparsedLine = preparedLine;
        if (instruction.address >= 0) {
            unparsedLine = unparsedLine.split(' ').slice(1).join(' ');
        }
        instruction.labels = parseLabels(unparsedLine);
        instruction.signals = [];
        instruction.branchType = BranchType.NONE;
        unparsedLine = unparsedLine.split(' ').slice(instruction.labels.length).join(' ');
        var hadBranch = false;
        var conditions = parseConditions(config);
        unparsedLine.split(',').map(function(segment) {
            return segment.trim();
        }).forEach(function(arg) {
            var isBropr = arg === 'bropr';
            var isBradr = arg === 'bradr';
            var isAddressBranch = arg.includes('br ');
            var isConditional = arg.includes('if ');
            var isBranch = isBropr || isBradr || isAddressBranch;
            var isSignal = signals.find(function(signal) {
                return signal.toLowerCase() === arg;
            });
            // Check for multiple branches.
            if (isBranch && hadBranch) {
                instruction.error = 'More than one branch';
            } else if (isBranch) {
                hadBranch = true;
            }
            // Set relevant instruction fields.
            var res;
            if (isSignal) {
                if (!instruction.signals.includes(arg)) {
                    instruction.signals.push(arg);
                }
            } else if (isBropr) {
                instruction.branchType = BranchType.BROPR;
            } else if (isBradr) {
                instruction.branchType = BranchType.BRADR;
            } else if (isBranch && isConditional) {
                res = CONDITIONAL_BRANCH.exec(arg);
                if (!res) {
                    instruction.error = 'Invalid conditional branch format';
                    return;
                }
                instruction.branchType = BranchType.CONDITIONAL;
                var splitCondition = res[1].split('.');
                var complement = res[1].startsWith('#') && splitCondition.length > 1 ? '#' : '';
                instruction.condition = complement + splitCondition[splitCondition.length - 1];
                if (!conditions.includes(instruction.condition)) {
                    instruction.error = 'Unknown condition code ' + instruction.condition;
                    return;
                }
                instruction.destination = res[2];
                // TODO: Destination validation
            } else if (isBranch && !isConditional) {
                res = UNCONDITIONAL_BRANCH.exec(arg);
                if (!res) {
                    instruction.error = 'Invalid unconditional branch format';
                    return;
                }
                instruction.branchType = BranchType.UNCONDITIONAL;
                instruction.destination = res[1];
                // TODO: Destination validation
            } else {
                instruction.error = 'Unknown operation ' + arg;
            }
        });
        return instruction;
     }
     }


     function createRect(startX, startY, width, height) {
     function logError(error) {
         var rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
         $('#diagram-generator-error').val($('#diagram-generator-error').val() + error + '\n');
        rect.setAttributeNS('http://www.w3.org/2000/svg', 'x', startX);
        rect.setAttributeNS('http://www.w3.org/2000/svg', 'y', startY);
        rect.setAttributeNS('http://www.w3.org/2000/svg', 'width', width);
        rect.setAttributeNS('http://www.w3.org/2000/svg', 'height', height);
        rect.setAttributeNS('http://www.w3.org/2000/svg', 'stroke-width', '1');
        rect.setAttributeNS('http://www.w3.org/2000/svg', 'fill-opacity', '0');
        rect.style.stroke = 'black';
        return rect;
     }
     }


     function createConditionRect(startX, startY, width, height) {
     function generate(event) {
         var rect = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
         event.preventDefault();
         var points = [
        $('#diagram-generator-error').val('');
            [startX, startY + height / 2],
         var config = lib.parseConfig(fileContents.configuration);
             [startX + width * 1 / 20, startY],
        if (!config) {
             [startX + width * 19 / 20, startY],
             logError(msg('error-parse-config'));
            [startX + width, startY + height / 2],
             return;
            [startX + width * 19 / 20, startY + height],
        }
            [startX + width * 1 / 20, startY + height]
        var instructionsResult = lib.getInstructions(config, fileContents.microcode);
        ];
         if (instructionsResult.errors.length > 0) {
         rect.setAttributeNS('http://www.w3.org/2000/svg', 'points', points.map(function(point) {
             instructionsResult.errors.forEach(logError);
             return point.join(',');
            return;
         }).join(' '));
         }
         rect.style.fill = 'transparent';
        var instructions = instructionsResult.result;
         rect.style.stroke = 'black';
        var parameters = getParameters();
         return rect;
         var pagesResult = lib.getPages(parameters, config, instructions);
         pagesResult.errors.forEach(logError);
         postGeneration(parameters, event.currentTarget.id, pagesResult.result);
     }
     }


     function createTextLine(x, y, text) {
     function createFieldset(text, contents) {
         var line = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
         var $icon = $('<span>').addClass([
         line.textContent = text;
            'oo-ui-widget',
        line.setAttributeNS('http://www.w3.org/2000/svg', 'x', x);
            'oo-ui-widget-enabled',
         line.setAttributeNS('http://www.w3.org/2000/svg', 'y', y);
            'oo-ui-iconElement-icon',
         return line;
            'oo-ui-iconElement',
            'oo-ui-labelElement-invisible',
            'oo-ui-iconWidget'
        ]);
         return $('<fieldset>', {
            html: [
                $('<legend>', {
                    html: [
                        $('<span>', {
                            'class': 'oo-ui-labelElement-label',
                            'text': text
                        }),
                        $icon.clone().addClass('oo-ui-icon-expand'),
                        $icon.clone().addClass('oo-ui-icon-collapse')
                    ],
                    role: 'button',
                    tabindex: 0
                }).addClass([
                    'oo-ui-fieldsetLayout-header',
                    'mw-collapsible-toggle',
                    'mw-collapsible-toggle-expanded'
                ]),
                $('<div>', {
                    'class': 'oo-ui-fieldsetLayout-group mw-collapsible-content',
                    'html': $('<div>', {
                        'class': 'oo-ui-widget oo-ui-widget-enabled',
                        'html': contents
                    })
                })
            ]
         }).addClass([
            'oo-ui-layout',
            'oo-ui-labelElement',
            'mw-collapsibleFieldsetLayout',
            'oo-ui-fieldsetLayout',
            'mw-collapsible',
            'mw-made-collapsible'
         ]);
     }
     }


     function wrapText(textSegments, maxLineLength) {
     function createFormRow(text, options) {
         var lines = [];
         var inputOptions = $.extend({}, options, {
        var currentLine = '';
             'class': 'oo-ui-inputWidget-input',
        textSegments.forEach(function(segment, index) {
             'name': options.id
             var segmentWithComma = segment.trim();
            if (index !== textSegments.length - 1) {
                segmentWithComma += ', ';
            }
             if (segmentWithComma.length + currentLine.length > maxLineLength && currentLine.length > 0) {
                lines.push(currentLine);
                currentLine = '';
            }
            currentLine += segmentWithComma;
         });
         });
         lines.push(currentLine);
         return $('<div>', {
         return lines;
            html: $('<div>', {
                'class': 'oo-ui-fieldLayout-body',
                'html': [
                    $('<span>', {
                        'class': 'oo-ui-fieldLayout-header',
                        'html': $('<label>', {
                            'class': 'oo-ui-labelElement-label',
                            'for': options.id,
                            'text': text + ':'
                        })
                    }),
                    $('<div>', {
                        'class': 'oo-ui-fieldLayout-field',
                        'html': $('<div>', {
                            html: $('<input>', inputOptions)
                        }).addClass([
                            'oo-ui-widget',
                            'oo-ui-widget-enabled',
                            'oo-ui-inputWidget',
                            'oo-ui-textInputWidget',
                            'oo-ui-textInputWidget-type-text'
                        ])
                    })
                ]
            })
         }).addClass([
            'mw-htmlform-field-HTMLTextField',
            'oo-ui-layout',
            'oo-ui-labelElement',
            'oo-ui-fieldLayout',
            'oo-ui-fieldLayout-align-top'
        ]);
     }
     }


     function createText(startX, startY, line) {
     function createFormButton(text, options) {
         var lines = typeof line === 'string' ? [line] : line;
         return $('<span>', $.extend({
        var text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
            'class': [
        text.setAttributeNS('http://www.w3.org/2000/svg', 'x', startX);
                'oo-ui-buttonElement',
        text.setAttributeNS('http://www.w3.org/2000/svg', 'y', startY);
                'oo-ui-buttonElement-framed',
        text.setAttributeNS('http://www.w3.org/2000/svg', 'alignment-baseline', 'middle');
                'oo-ui-flaggedElement-primary',
         text.setAttributeNS('http://www.w3.org/2000/svg', 'font-family', 'monospace');
                'oo-ui-flaggedElement-progressive',
        text.setAttributeNS('http://www.w3.org/2000/svg', 'text-anchor', 'middle');
                'oo-ui-labelElement',
        lines.forEach(function(currentLine, index) {
                'oo-ui-widget-enabled',
             text.appendChild(createTextLine(startX, startY + index * 15, currentLine));
                'oo-ui-inputWidget'
         });
            ].join(' ')
        return text;
         }, options)).append(
            $('<button>', {
                'class': 'oo-ui-buttonElement-button diagram-generator-action',
                'tabindex': '0'
            }).append(
                $('<span>', {
                    'class': 'oo-ui-labelElement-label label',
                    'text': text
                })
             )
         );
     }
     }


     function createTextNotCentered(startX, startY, text) {
     function collapsibleShim(event) {
         var textNode = createText(startX, startY, text);
         if (event.keyCode && ![13, 32].includes(event.keyCode)) {
        textNode.removeAttribute('text-anchor');
             return;
        return textNode;
    }
 
    function createLine(startX, startY, endX, endY) {
        var line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
        line.setAttributeNS('http://www.w3.org/2000/svg', 'x1', startX);
        line.setAttributeNS('http://www.w3.org/2000/svg', 'y1', startY);
        line.setAttributeNS('http://www.w3.org/2000/svg', 'x2', endX);
        line.setAttributeNS('http://www.w3.org/2000/svg', 'y2', endY);
        line.style.stroke = 'black';
        return line;
    }
 
    function createArrow(startX, startY, endX, endY) {
        var arrow = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
        var points = [[endX, endY], [endX, endY], [endX, endY]];
        if (startX === endX) {
            // Vertical
            points[0][0] -= 2.5;
            points[1][0] += 2.5;
            if (endY > startY) {
                // Down
                points[0][1] -= 2.5;
                points[1][1] -= 2.5;
            } else {
                // Up
                points[0][1] += 2.5;
                points[1][1] += 2.5;
            }
        } else {
            // Horizontal
            points[0][1] -= 2.5;
            points[1][1] += 2.5;
            if (endX > startX) {
                // Right
                points[0][0] -= 2.5;
                points[1][0] -= 2.5;
             } else {
                // Left
                points[0][0] += 2.5;
                points[1][0] += 2.5;
            }
         }
         }
         arrow.setAttributeNS('http://www.w3.org/2000/svg', 'points', points.map(function(point) {
         $(event.currentTarget)
             return point.join(',');
             .parent()
        }).join(' '));
            .find('.mw-collapsible-content')
        arrow.style.fill = 'black';
            .toggle();
        var group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
        group.appendChild(createLine(startX, startY, endX, endY));
        group.appendChild(arrow);
        return group;
     }
     }


     function hasSelfBranch(instruction) {
     function init() {
         if (instruction.destination.startsWith('madr')) {
         if ($('#diagram-generator').hasClass('initialized')) {
            return parseInt(instruction.destination.substring(4), 16) === instruction.address;
        }
        return instruction.labels.includes(instruction.destination);
    }
 
    function generateInstructionDiagram(parameters, instruction, currentY) {
        var y = currentY;
        var arrowHeight = parameters.arrowHeight;
        var group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
        var columnWidth = (parameters.width - 2 * parameters.margin) / 3;
        var columnOffset = (columnWidth - 200 - 100 - 20) / 2;
        var offL = parameters.margin + columnOffset;
        var offM = offL + columnWidth;
        var offR = offM + columnWidth;
        // Label
        if (instruction.labels.length > 0) {
            // TODO: Support more than one label and also label wrapping
            group.appendChild(createRect(offM + 220, y, 100, 27.5));
            group.appendChild(createRect(offL + 220, y, 100, 27.5));
            group.appendChild(createText(offM + 270, y + 17.5, instruction.labels[0]));
            group.appendChild(createText(offL + 270, y + 17.5, instruction.labels[0]));
            group.appendChild(createArrow(offM + 220, y + 13.75, offM + 200, y + 13.75));
            group.appendChild(createArrow(offL + 220, y + 13.75, offL + 200, y + 13.75));
        }
        // Signals
        var wrappedTextMiddle = wrapText(instruction.signals, MAX_LINE_LENGTH);
        // TODO: Translate signals
        var wrappedTextLeft = wrapText(instruction.signals, MAX_LINE_LENGTH);
        var wrappedTextRight = wrapText(instruction.line.split(','), columnWidth / 10);
        var yDiffLeft = (wrappedTextLeft.length - 1) * 15 + 17.5;
        var yDiffMiddle = (wrappedTextMiddle.length - 1) * 15 + 17.5;
        var yDiff = Math.max(yDiffLeft, yDiffMiddle);
        group.appendChild(createText(offM + 100, y + 17.5, wrappedTextMiddle));
        group.appendChild(createText(offL + 100, y + 17.5, wrappedTextLeft));
        group.appendChild(createTextNotCentered(offR, y + 17.5, wrappedTextRight));
        var rectHeight = yDiff + 10;
        group.appendChild(createRect(offM, y, 200, rectHeight));
        group.appendChild(createRect(offL, y, 200, rectHeight));
        group.appendChild(createArrow(offM + 100, y + rectHeight, offM + 100, y + rectHeight + arrowHeight));
        group.appendChild(createArrow(offL + 100, y + rectHeight, offL + 100, y + rectHeight + arrowHeight));
        y += rectHeight + arrowHeight;
        // Condition
        switch (instruction.branchType) {
            case BranchType.BRADR:
            case BranchType.BROPR:
                group.appendChild(createText(offM + 100, y + 17.5, instruction.branchType));
                group.appendChild(createText(offL + 100, y + 17.5, instruction.branchType));
                group.appendChild(createConditionRect(offM, y, 200, 27.5));
                group.appendChild(createConditionRect(offL, y, 200, 27.5));
                y += 50;
                break;
            case BranchType.CONDITIONAL:
                group.appendChild(createText(offM + 100, y + 17.5, instruction.condition));
                group.appendChild(createText(offL + 100, y + 17.5, instruction.condition));
                group.appendChild(createConditionRect(offM, y, 200, 27.5));
                group.appendChild(createConditionRect(offL, y, 200, 27.5));
                group.appendChild(createArrow(offM + 100, y + 27.5, offM + 100, y + 27.5 + arrowHeight));
                group.appendChild(createArrow(offL + 100, y + 27.5, offL + 100, y + 27.5 + arrowHeight));
                group.appendChild(createText(offM + 90, y + 27.5 + arrowHeight * 3 / 4, '0'));
                group.appendChild(createText(offL + 90, y + 27.5 + arrowHeight * 3 / 4, '0'));
                group.appendChild(createText(offM + 203, y + 10, '1'));
                group.appendChild(createText(offL + 203, y + 10, '1'));
                if (hasSelfBranch(instruction)) {
                    group.appendChild(createLine(offM + 200, y + 13.75, offM + 210, y + 13.75));
                    group.appendChild(createLine(offL + 200, y + 13.75, offL + 210, y + 13.75));
                    group.appendChild(createLine(offM + 210, y + 13.75, offM + 210, y - 20));
                    group.appendChild(createLine(offL + 210, y + 13.75, offL + 210, y - 20));
                    group.appendChild(createLine(offM + 210, y + 13.75, offM + 210, y - 20));
                    group.appendChild(createLine(offL + 210, y + 13.75, offL + 210, y - 20));
                    group.appendChild(createArrow(offM + 210, y - 20, offM + 200, y - 20));
                    group.appendChild(createArrow(offL + 210, y - 20, offL + 200, y - 20));
                } else {
                    group.appendChild(createArrow(offM + 200, y + 13.75, offM + 220, y + 13.75));
                    group.appendChild(createArrow(offL + 200, y + 13.75, offL + 220, y + 13.75));
                    group.appendChild(createRect(offM + 220, y, 100, 27.5));
                    group.appendChild(createRect(offL + 220, y, 100, 27.5));
                    group.appendChild(createText(offM + 270, y + 17.5, instruction.destination));
                    group.appendChild(createText(offL + 270, y + 17.5, instruction.destination));
                }
                y += 27.5 + arrowHeight;
            break;
            case BranchType.UNCONDITIONAL:
                // TODO: Support label wrapping
                group.appendChild(createRect(offM + 50, y, 100, 27.5));
                group.appendChild(createRect(offL + 50, y, 100, 27.5));
                group.appendChild(createText(offM + 100, y + 17.5, instruction.destination));
                group.appendChild(createText(offL + 100, y + 17.5, instruction.destination));
                y += 40;
                break;
            default:
                // Do nothing.
                break;
        }
        var separator = createLine(parameters.margin, y, parameters.width - parameters.margin, y);
        separator.setAttributeNS('http://www.w3.org/2000/svg', 'stroke-dasharray', '10,10');
        group.appendChild(separator);
        return [group, y];
    }
 
    function createPage(parameters, instructions) {
        var group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
        var width = parameters.width;
        var height = parameters.height;
        var margin = parameters.margin;
        var headerHeight = parameters.headerHeight;
        var footerRowHeight = parameters.footerRowHeight;
        var pageWidth = width - 2 * margin;
        var columnWidth = pageWidth / 3;
        var columnHalfWidth = columnWidth / 2;
        group.append(createLine(margin, margin, width - margin, margin));
        group.append(createLine(margin, margin, margin, height - margin));
        group.append(createLine(width - margin, margin, width - margin, height - margin));
        group.append(createLine(margin, height - margin, width - margin, height - margin));
        group.append(createLine(margin, margin + headerHeight, width - margin, margin + headerHeight));
        group.append(createLine(margin, margin + headerHeight, width - margin, margin + headerHeight));
        group.append(createLine(margin + columnWidth, margin, margin + columnWidth, height - margin));
        group.append(createLine(margin + columnWidth * 2, margin, margin + columnWidth * 2, height - margin));
        group.append(createText(margin + columnHalfWidth, margin + 15, 'Dijagram toka mikrooperacija'));
        group.append(createText(margin + columnHalfWidth * 3, margin + 15, 'Dijagram toka upravljačkih signala'));
        group.append(createText(margin + columnHalfWidth * 5, margin + 15, 'Sekvenca upravljačkih signala'));
        var lowerRowMargin = height - margin - footerRowHeight;
        var upperRowMargin = lowerRowMargin - footerRowHeight;
        group.append(createLine(margin, lowerRowMargin, width - margin, lowerRowMargin));
        group.append(createLine(margin, upperRowMargin, width - margin, upperRowMargin));
        var pageHeight = height - 2 * margin - FIELDS.length * footerRowHeight;
        var currentY = margin + headerHeight;
        pageHeight -= currentY;
        while (pageHeight > 0 && instructions.length > 0) {
            var instruction = instructions.shift();
            var generationResult = generateInstructionDiagram(parameters, instruction, currentY);
            var heightDiff = generationResult[1] - currentY;
            currentY = generationResult[1];
            pageHeight -= heightDiff;
            if (pageHeight >= 0) {
                group.appendChild(generationResult[0]);
            } else {
                instructions.unshift(instruction);
            }
        }
        FIELDS.forEach(function(row, rowIndex) {
            row.forEach(function(cell, columnIndex) {
                var textX = margin + columnIndex * columnWidth + 10;
                var textY = height - margin - (FIELDS.length - rowIndex - 0.5) * footerRowHeight;
                group.append(createTextNotCentered(textX, textY, cell + ':'));
            });
        });
        return group;
    }
 
    function generate(event) {
        event.preventDefault();
        $('#diagram-generator-error').text('');
        var newPage = $('#diagram-generator-new-page').prop('checked');
        var instructions = [];
        var comment = '';
        var config = parseConfig(fileContents.configuration);
        if (!config) {
            $('#diagram-generator-error').text('Error while parsing configuration.');
             return;
             return;
         }
         }
         var lines = fileContents.microcode.split('\n');
         var $optionsFieldset = createFieldset(msg('options'), [
        var labels = {};
             createFormRow(msg('microcode'), {
        for (var i = 0, l = lines.length; i < l; ++i) {
                 change: updateFile,
            var line = lines[i].trim();
                 id: 'microcode',
            if (!line) {
                 type: 'file'
                continue;
            }),
             }
            createFormRow(msg('configuration'), {
            if (line.startsWith('!')) {
                 change: updateFile,
                 comment = line;
                 id: 'configuration',
            } else {
                type: 'file'
                var instruction = parseLine(comment, line, config);
             }),
                 comment = '';
             createFormRow(msg('student-name'), {
                 for (var j = 0, l2 = instruction.labels.length; j < l2; ++j) {
                id: 'diagram-generator-student-name',
                    var label = instruction.labels[j];
                placeholder: 'Pera Pisar',
                    if (labels[label]) {
                type: 'text'
                        instruction.error = 'Duplicate label ' + label;
            }),
                        break;
             createFormRow(msg('student-index'), {
                    }
                 id: 'diagram-generator-student-index',
                    labels[label] = instruction.address;
                placeholder: '2021/0000',
                }
                type: 'text'
                if (instruction.error) {
            })
                    $('#diagram-generator-error').text('Microcode error: ' + instruction.error + ' at line ' + (i + 1));
         ]);
                    return;
         var $advancedFieldset = createFieldset(msg('advanced-options'), [
                 }
             createFormRow(msg('page-width'), {
                 instructions.push(instruction);
                id: 'diagram-generator-page-width',
            }
                 type: 'number',
        }
                value: 1200
        var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
            }),
        var parameters = {
            createFormRow(msg('page-height'), {
            arrowHeight: 15,
                id: 'diagram-generator-page-height',
            headerHeight: 20,
                type: 'number',
             height: 1680,
                value: 1560
             indexNum: $('#diagram-generator-student-index').val(),
            }),
            footerRowHeight: 50,
            createFormRow(msg('left-column-header'), {
            margin: 50,
                id: 'diagram-generator-left-column-header',
            nameSurname: $('#diagram-generator-student-name').val(),
                type: 'text',
            subjectName: 'Osnovi računarske tehnike 2',
                value: 'Dijagram toka mikrooperacija'
            width: 1200
            }),
        };
            createFormRow(msg('middle-column-header'), {
        var height = parameters.height;
                id: 'diagram-generator-middle-column-header',
        svg.setAttributeNS('http://www.w3.org/2000/svg', 'width', parameters.width);
                type: 'text',
        var numPages = 0;
                value: 'Dijagram toka upravljačkih signala'
        while (instructions.length > 0) {
            }),
            var page = createPage(parameters, instructions);
            createFormRow(msg('right-column-header'), {
             page.setAttributeNS('http://www.w3.org/2000/svg', 'transform', 'translate(0, ' + numPages * height + ')');
                id: 'diagram-generator-right-column-header',
            svg.appendChild(page);
                type: 'text',
            ++numPages;
                value: 'Sekvenca upravljačkih signala'
        }
            }),
        var pageNodes = svg.children;
            createFormRow(msg('arrow-height'), {
        var numFieldRows = FIELDS.length;
                id: 'diagram-generator-arrow-height',
        var numFieldColumns = FIELDS[0].length;
                type: 'number',
        var numFields = numFieldRows * numFieldColumns;
                value: 15
        for (var pageIndex = 0; pageIndex < numPages; ++pageIndex) {
            }),
            var fieldNodes = Array.prototype.slice.call(pageNodes[pageIndex].children, -numFields);
            createFormRow(msg('header-height'), {
            var fields = [];
                id: 'diagram-generator-header-height',
            for (var rowIndex = 0; rowIndex < numFieldRows; ++rowIndex) {
                type: 'number',
                 var fieldRow = [];
                value: 20
                for (var columnIndex = 0; columnIndex < numFieldColumns; ++columnIndex) {
            }),
                    fieldRow.push(fieldNodes[rowIndex * numFieldColumns + columnIndex]);
            createFormRow(msg('footer-row-height'), {
                }
                id: 'diagram-generator-footer-row-height',
                fields.push(fieldRow);
                type: 'number',
            }
                value: 50
            var today = new Date();
            }),
            var formattedDate = today.getDate() + '. ' + (today.getMonth() + 1) + '. ' + today.getFullYear() + '.';
            createFormRow(msg('margin'), {
            fields[0][0].textContent += ' ' + parameters.nameSurname;
                id: 'diagram-generator-margin',
            fields[0][1].textContent += ' ' + parameters.indexNum;
                type: 'number',
            fields[1][0].textContent += ' ' + parameters.subjectName;
                value: 50
            fields[1][1].textContent += ' ' + formattedDate;
            }),
            fields[1][2].textContent += ' ' + (pageIndex + 1) + '/' + numPages;
            createFormRow(msg('subject-name'), {
        }
                id: 'diagram-generator-subject-name',
        var totalHeight = numPages * height;
                type: 'text',
        svg.setAttributeNS('http://www.w3.org/2000/svg', 'height', totalHeight);
                value: 'Osnovi računarske tehnike 2'
        svg.setAttributeNS('http://www.w3.org/2000/svg', 'viewBox', '0 0 ' + parameters.width + ' ' + totalHeight);
            })
        svg.setAttributeNS('http://www.w3.org/2000/svg', 'version', '1.1');
        ]);
        svg.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
        var $actionsFieldset = createFieldset(msg('actions'), [
         svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
            createFormButton(msg('generate-this-page'), {
         if (newPage) {
                click: generate,
            var imageWindow = window.open('');
                id: 'diagram-generator-this-page'
             imageWindow.document.write(svg.outerHTML);
            }),
        } else {
            createFormButton(msg('generate-new-page'), {
            $('#diagram-generator-result').html(svg.outerHTML);
                click: generate,
        }
                id: 'diagram-generator-new-page'
    }
 
    function init() {
        $('#diagram-generator').append(
            $('<form>', {
                 html: [
                    $('<p>', {
                        html: [
                            $('<label>', {
                                'for': 'microcode',
                                'text': 'Microcode:'
                            }),
                            $('<input>', {
                                change: updateFile,
                                name: 'microcode',
                                id: 'microcode',
                                type: 'file'
                            })
                        ]
                    }),
                    $('<p>', {
                        html: [
                            $('<label>', {
                                'for': 'configuration',
                                'text': 'Configuration:'
                            }),
                            $('<input>', {
                                change: updateFile,
                                name: 'configuration',
                                id: 'configuration',
                                type: 'file'
                            })
                        ]
                    }),
                    $('<p>', {
                        html: [
                            $('<label>', {
                                'for': 'diagram-generator-student-name',
                                'text': 'Student name:'
                            }),
                            $('<input>', {
                                name: 'diagram-generator-student-name',
                                id: 'diagram-generator-student-name',
                                placeholder: 'Pera Pisar',
                                type: 'text'
                            })
                        ]
                    }),
                    $('<p>', {
                        html: [
                            $('<label>', {
                                'for': 'diagram-generator-student-index',
                                'text': 'Student index:'
                            }),
                            $('<input>', {
                                name: 'diagram-generator-student-index',
                                id: 'diagram-generator-student-index',
                                placeholder: '2021/0000',
                                type: 'text'
                            })
                        ]
                    }),
                    $('<p>', {
                        html: [
                            $('<input>', {
                                id: 'diagram-generator-submit',
                                type: 'submit',
                                value: 'Generate'
                            }),
                            $('<label>', {
                                'for': 'diagram-generator-new-page',
                                'text': 'Generate on new page'
                            }),
                            $('<input>', {
                                id: 'diagram-generator-new-page',
                                name: 'diagram-generator-new-page',
                                type: 'checkbox'
                            })
                        ]
                    }),
                    $('<p>', {
                        id: 'diagram-generator-error'
                    })
                ],
                submit: generate
             }),
             }),
            createFormButton(msg('generate-download-svg'), {
                click: generate,
                id: 'diagram-generator-download-svg'
            })
        ]);
        $('#diagram-generator').html([
            $('<div>', {
                html: $('<form>', {
                    html: [
                        $optionsFieldset,
                        $advancedFieldset,
                        $actionsFieldset,
                        $('<textarea>', {
                            id: 'diagram-generator-error',
                            placeholder: msg('errors-area'),
                            readonly: true,
                            rows: 10
                        })
                    ]
                }).addClass(['mw-htmlform', 'mw-htmlform-ooui', 'oo-ui-layout', 'oo-ui-formLayout'])
            }).addClass([
                'mw-htmlform-ooui-wrapper',
                'oo-ui-layout',
                'oo-ui-panelLayout',
                'oo-ui-panelLayout-padded',
                'oo-ui-panelLayout-framed'
            ]),
             $('<div>', {
             $('<div>', {
                 id: 'diagram-generator-result'
                 id: 'diagram-generator-result'
             })
             })
         );
         ]).addClass('initialized');
        if ($.fn.makeCollapsible) {
            $optionsFieldset.makeCollapsible();
            $advancedFieldset.makeCollapsible({
                collapsed: true
            });
            $actionsFieldset.makeCollapsible();
        } else {
            $('.mw-collapsible-toggle')
                .click(collapsibleShim)
                .keydown(collapsibleShim);
            $advancedFieldset
                .find('.mw-collapsible-content')
                .hide();
        }
         updateSubmitEnabled();
         updateSubmitEnabled();
     }
     }


     $(init);
     lib.msg = msg;
    if (window.mw && window.mw.hook) {
        mw.hook('wikipage.content').add(init);
    } else {
        $(init);
    }
})();
})();

Тренутна верзија на датум 22. фебруар 2023. у 19:53

(function() {
    'use strict';
    var lib = mw.libs.diagramGenerator;
    var fileContents = {};

    function msg(message) {
        var prefixedMessage = 'gadget-diagram-generator-' + message;
        var args = Array.prototype.slice.call(arguments, 1);
        var mwMsg = mw.message.apply(mw.message, [prefixedMessage].concat(args));
        return mwMsg.plain();
    }

    function updateSubmitEnabled() {
        if (fileContents.microcode && fileContents.configuration) {
            $('.diagram-generator-action')
                .removeAttr('disabled')
                .parent()
                .removeClass('oo-ui-widget-disabled')
                .addClass('oo-ui-widget-enabled');
        } else {
            $('.diagram-generator-action')
                .attr('disabled', '')
                .parent()
                .removeClass('oo-ui-widget-enabled')
                .addClass('oo-ui-widget-disabled');
        }
    }

    function fileLoaded(id) {
        fileContents[id] = this.result;
        updateSubmitEnabled();
    }

    function updateFile(event) {
        var input = event.target;
        delete fileContents[input.id];
        var files = input.files;
        if (files.length === 0) {
            return;
        }
        var reader = new FileReader();
        reader.addEventListener('load', fileLoaded.bind(reader, input.id));
        reader.readAsText(files[0]);
        updateSubmitEnabled();
    }

    function downloadSVG(svg) {
        var contents = [
            '<?xml version="1.0" encoding="utf-8"?>',
            '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">',
            svg.outerHTML
        ].join('\n');
        var blob = new Blob([contents], {
            type: 'image/svg+xml'
        });
        var blobUrl = URL.createObjectURL(blob);
        var downloadLink = document.createElement('a');
        downloadLink.href = blobUrl;
        downloadLink.download = 'Diagram.svg';
        document.body.appendChild(downloadLink);
        downloadLink.click();
        URL.revokeObjectURL(blobUrl);
        downloadLink.remove();
    }

    function printPages(width, height, pages) {
        var imageWindow = window.open('');
        pages.forEach(function(page) {
            var svg = lib.createSVG(width, height);
            svg.appendChild(page);
            svg.style.breakAfter = 'page';
            imageWindow.document.write(svg.outerHTML);
        });
        imageWindow.document.close();
        imageWindow.focus();
        imageWindow.print();
    }

    function postGeneration(parameters, actionId, pages) {
        switch (actionId) {
            case 'diagram-generator-this-page':
                var svg = lib.createSVGWithPages(parameters.width, parameters.height, pages);
                $('#diagram-generator-result').html(svg.outerHTML);
                break;
            case 'diagram-generator-new-page':
                printPages(parameters.width, parameters.height, pages);
                break;
            case 'diagram-generator-download-svg':
                downloadSVG(lib.createSVGWithPages(parameters.width, parameters.height, pages));
                break;
            default:
                // ???
                break;
        }
    }

    function getParameters() {
        return {
            arrowHeight: Number($('#diagram-generator-arrow-height').val()),
            columnHeaderLeft: $('#diagram-generator-left-column-header').val(),
            columnHeaderMiddle: $('#diagram-generator-middle-column-header').val(),
            columnHeaderRight: $('#diagram-generator-right-column-header').val(),
            headerHeight: Number($('#diagram-generator-header-height').val()),
            height: Number($('#diagram-generator-page-height').val()),
            ignoreErrors: false,
            indexNum: $('#diagram-generator-student-index').val(),
            footerRowHeight: Number($('#diagram-generator-footer-row-height').val()),
            margin: Number($('#diagram-generator-margin').val()),
            nameSurname: $('#diagram-generator-student-name').val(),
            subjectName: $('#diagram-generator-subject-name').val(),
            width: Number($('#diagram-generator-page-width').val())
        };
    }

    function logError(error) {
        $('#diagram-generator-error').val($('#diagram-generator-error').val() + error + '\n');
    }

    function generate(event) {
        event.preventDefault();
        $('#diagram-generator-error').val('');
        var config = lib.parseConfig(fileContents.configuration);
        if (!config) {
            logError(msg('error-parse-config'));
            return;
        }
        var instructionsResult = lib.getInstructions(config, fileContents.microcode);
        if (instructionsResult.errors.length > 0) {
            instructionsResult.errors.forEach(logError);
            return;
        }
        var instructions = instructionsResult.result;
        var parameters = getParameters();
        var pagesResult = lib.getPages(parameters, config, instructions);
        pagesResult.errors.forEach(logError);
        postGeneration(parameters, event.currentTarget.id, pagesResult.result);
    }

    function createFieldset(text, contents) {
        var $icon = $('<span>').addClass([
            'oo-ui-widget',
            'oo-ui-widget-enabled',
            'oo-ui-iconElement-icon',
            'oo-ui-iconElement',
            'oo-ui-labelElement-invisible',
            'oo-ui-iconWidget'
        ]);
        return $('<fieldset>', {
            html: [
                $('<legend>', {
                    html: [
                        $('<span>', {
                            'class': 'oo-ui-labelElement-label',
                            'text': text
                        }),
                        $icon.clone().addClass('oo-ui-icon-expand'),
                        $icon.clone().addClass('oo-ui-icon-collapse')
                    ],
                    role: 'button',
                    tabindex: 0
                }).addClass([
                    'oo-ui-fieldsetLayout-header',
                    'mw-collapsible-toggle',
                    'mw-collapsible-toggle-expanded'
                ]),
                $('<div>', {
                    'class': 'oo-ui-fieldsetLayout-group mw-collapsible-content',
                    'html': $('<div>', {
                        'class': 'oo-ui-widget oo-ui-widget-enabled',
                        'html': contents
                    })
                })
            ]
        }).addClass([
            'oo-ui-layout',
            'oo-ui-labelElement',
            'mw-collapsibleFieldsetLayout',
            'oo-ui-fieldsetLayout',
            'mw-collapsible',
            'mw-made-collapsible'
        ]);
    }

    function createFormRow(text, options) {
        var inputOptions = $.extend({}, options, {
            'class': 'oo-ui-inputWidget-input',
            'name': options.id
        });
        return $('<div>', {
            html: $('<div>', {
                'class': 'oo-ui-fieldLayout-body',
                'html': [
                    $('<span>', {
                        'class': 'oo-ui-fieldLayout-header',
                        'html': $('<label>', {
                            'class': 'oo-ui-labelElement-label',
                            'for': options.id,
                            'text': text + ':'
                        })
                    }),
                    $('<div>', {
                        'class': 'oo-ui-fieldLayout-field',
                        'html': $('<div>', {
                            html: $('<input>', inputOptions)
                        }).addClass([
                            'oo-ui-widget',
                            'oo-ui-widget-enabled',
                            'oo-ui-inputWidget',
                            'oo-ui-textInputWidget',
                            'oo-ui-textInputWidget-type-text'
                        ])
                    })
                ]
            })
        }).addClass([
            'mw-htmlform-field-HTMLTextField',
            'oo-ui-layout',
            'oo-ui-labelElement',
            'oo-ui-fieldLayout',
            'oo-ui-fieldLayout-align-top'
        ]);
    }

    function createFormButton(text, options) {
        return $('<span>', $.extend({
            'class': [
                'oo-ui-buttonElement',
                'oo-ui-buttonElement-framed',
                'oo-ui-flaggedElement-primary',
                'oo-ui-flaggedElement-progressive',
                'oo-ui-labelElement',
                'oo-ui-widget-enabled',
                'oo-ui-inputWidget'
            ].join(' ')
        }, options)).append(
            $('<button>', {
                'class': 'oo-ui-buttonElement-button diagram-generator-action',
                'tabindex': '0'
            }).append(
                $('<span>', {
                    'class': 'oo-ui-labelElement-label label',
                    'text': text
                })
            )
        );
    }

    function collapsibleShim(event) {
        if (event.keyCode && ![13, 32].includes(event.keyCode)) {
            return;
        }
        $(event.currentTarget)
            .parent()
            .find('.mw-collapsible-content')
            .toggle();
    }

    function init() {
        if ($('#diagram-generator').hasClass('initialized')) {
            return;
        }
        var $optionsFieldset = createFieldset(msg('options'), [
            createFormRow(msg('microcode'), {
                change: updateFile,
                id: 'microcode',
                type: 'file'
            }),
            createFormRow(msg('configuration'), {
                change: updateFile,
                id: 'configuration',
                type: 'file'
            }),
            createFormRow(msg('student-name'), {
                id: 'diagram-generator-student-name',
                placeholder: 'Pera Pisar',
                type: 'text'
            }),
            createFormRow(msg('student-index'), {
                id: 'diagram-generator-student-index',
                placeholder: '2021/0000',
                type: 'text'
            })
        ]);
        var $advancedFieldset = createFieldset(msg('advanced-options'), [
            createFormRow(msg('page-width'), {
                id: 'diagram-generator-page-width',
                type: 'number',
                value: 1200
            }),
            createFormRow(msg('page-height'), {
                id: 'diagram-generator-page-height',
                type: 'number',
                value: 1560
            }),
            createFormRow(msg('left-column-header'), {
                id: 'diagram-generator-left-column-header',
                type: 'text',
                value: 'Dijagram toka mikrooperacija'
            }),
            createFormRow(msg('middle-column-header'), {
                id: 'diagram-generator-middle-column-header',
                type: 'text',
                value: 'Dijagram toka upravljačkih signala'
            }),
            createFormRow(msg('right-column-header'), {
                id: 'diagram-generator-right-column-header',
                type: 'text',
                value: 'Sekvenca upravljačkih signala'
            }),
            createFormRow(msg('arrow-height'), {
                id: 'diagram-generator-arrow-height',
                type: 'number',
                value: 15
            }),
            createFormRow(msg('header-height'), {
                id: 'diagram-generator-header-height',
                type: 'number',
                value: 20
            }),
            createFormRow(msg('footer-row-height'), {
                id: 'diagram-generator-footer-row-height',
                type: 'number',
                value: 50
            }),
            createFormRow(msg('margin'), {
                id: 'diagram-generator-margin',
                type: 'number',
                value: 50
            }),
            createFormRow(msg('subject-name'), {
                id: 'diagram-generator-subject-name',
                type: 'text',
                value: 'Osnovi računarske tehnike 2'
            })
        ]);
        var $actionsFieldset = createFieldset(msg('actions'), [
            createFormButton(msg('generate-this-page'), {
                click: generate,
                id: 'diagram-generator-this-page'
            }),
            createFormButton(msg('generate-new-page'), {
                click: generate,
                id: 'diagram-generator-new-page'
            }),
            createFormButton(msg('generate-download-svg'), {
                click: generate,
                id: 'diagram-generator-download-svg'
            })
        ]);
        $('#diagram-generator').html([
            $('<div>', {
                html: $('<form>', {
                    html: [
                        $optionsFieldset,
                        $advancedFieldset,
                        $actionsFieldset,
                        $('<textarea>', {
                            id: 'diagram-generator-error',
                            placeholder: msg('errors-area'),
                            readonly: true,
                            rows: 10
                        })
                    ]
                }).addClass(['mw-htmlform', 'mw-htmlform-ooui', 'oo-ui-layout', 'oo-ui-formLayout'])
            }).addClass([
                'mw-htmlform-ooui-wrapper',
                'oo-ui-layout',
                'oo-ui-panelLayout',
                'oo-ui-panelLayout-padded',
                'oo-ui-panelLayout-framed'
            ]),
            $('<div>', {
                id: 'diagram-generator-result'
            })
        ]).addClass('initialized');
        if ($.fn.makeCollapsible) {
            $optionsFieldset.makeCollapsible();
            $advancedFieldset.makeCollapsible({
                collapsed: true
            });
            $actionsFieldset.makeCollapsible();
        } else {
            $('.mw-collapsible-toggle')
                .click(collapsibleShim)
                .keydown(collapsibleShim);
            $advancedFieldset
                .find('.mw-collapsible-content')
                .hide();
        }
        updateSubmitEnabled();
    }

    lib.msg = msg;
    if (window.mw && window.mw.hook) {
        mw.hook('wikipage.content').add(init);
    } else {
        $(init);
    }
})();