PropertyEditor.js

Note: After saving, 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)
  • Internet Explorer: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Go to Menu → Settings (Opera → Preferences on a Mac) and then to Privacy & security → Clear browsing data → Cached images and files.
/* globals console, propertyInfo, stratics, $ */
/* jshint esversion: 6 */
var loadEditor = function () {
    "use strict";

    var br = '<br>',
        div = 'div',
        input = 'input',
        legend = 'legend',
        nbsp = '&nbsp;',
        number = 'number',
        span = 'span',
        loc = location.href.toString(),
        counter = 0,
        arrays,
        i;

    /* Determine what type of item is being edited. */
    var $mwtextarea = $('.wikiEditor-ui-text textarea, #editform textarea'),
        itemType;
    var mwtext = $mwtextarea.val();
    switch (true) {
    case /^{{RareItem/m.test(mwtext):
        itemType = 'rare';
        break;
    case /^{{InfoBox[\s|_]UOItem/m.test(mwtext):
        itemType = 'common';
        break;
    default:
        itemType = 'new';
    }
    $mwtextarea.data('type', itemType);

    /* Reads in info from the JSON object, populates a select with options */
    var populateSelect = function ($select, arr, allOption = false) {
            var options = [stratics.f.util.makehtml('option', {
                value: "none"
            })];
            $select.empty();
            if (allOption) {
            	options.push(stratics.f.util.makehtml('option', {
                	value: "all"
            	}, 'All Properties'));            
            }
            for (i = 0; i < arr.length; i++) {
                options.push(stratics.f.util.makehtml('option', {
                    value: i
                }, arr[i].name));
            }
            return $select.append(options)
                .sortOptions()
                .val("")
                .data('source', arr);
        },
        /* Reads the select's source array, returns the type of the element at [value] */
        getOptionType = function ($select, value) {
            return $select.data('source')[value].type;
        },
        /* Creates an arbitrary element */
        makeElem = function (elem, num, args, label, after) {
            if (typeof args.id === 'undefined') { // Allow for defined element ids
                if (typeof args.type === 'undefined') {
                    args.id = elem + num;
                } else {
                    args.id = args.type + num;
                }
            }
            if (typeof label === 'undefined') {
                return stratics.f.util.makehtml(elem, args);
            } else {
                if (typeof after === 'undefined') {
                    return stratics.f.util.makehtml('label', {}, label + nbsp + stratics.f.util.makehtml(elem, args));
                } else {
                    return stratics.f.util.makehtml('label', {}, stratics.f.util.makehtml(elem, args) + nbsp + label);
                }
            }
        },
        /* Creates an arbitrary element */
        makeFieldset = function (id, name) {
            return $('<fieldset>', {
                    id: 'fieldset' + id
                })
                .append(stratics.f.util.makehtml(legend, {
                    style: 'color:#FFF;font-weight:bolder;'
                }, name))
                .append(br);
        },
        /* Creates an input[type=number] element */
        makeNumber = function (j, val) {
            return makeElem(input, j, {
                type: number,
                min: 0,
                step: 1,
                style: 'width: 6em;',
                value: val
            });
        },
        /* Parse existing data from template */
        readTemplate = function () {
            var itemType = $mwtextarea.data('type'),
                linePattern = /\s*\|\s*(\S+\s*=\s*.+)/mg,
                argPattern = /([a-zA-Z\s_]+)+=\s*(.+)/,
                templateBody = '',
                paramLines,
                paramPairs = [],
                paramLookup = [],
                pattern,
                result;

            if (itemType !== 'new') {
                /* Set the pattern based on which template is being used. */
                if (itemType === 'rare') {
                    pattern = /{{RareItem([\s\S]*)^}}/m;
                } else if (itemType === 'common') {
                    pattern = /{{InfoBox[\s|_]UOItem([\s\S]*)^}}/m;
                }

                /* Extract the template's contents */
                mwtext = $mwtextarea.val();
                mwtext.replace(pattern, function (match, p1) {
                    templateBody = p1; // Store the contents of the template
                    return ''; // Remove the template from the page's text
                });

                /* Extract the parameters from the template */
                paramLines = templateBody.split(linePattern)
                    .filter(function (el) {
                        return el.trim()
                            .length !== 0;
                    });

                /* Split each parameter into argument+value pairs */
                for (i = 0; i < paramLines.length; i++) {
                    result = paramLines[i].match(argPattern)
                        .slice(1);
                    result[0] = result[0].trim();
                    paramPairs.push(result);
                    paramLookup.push(paramPairs[i][0]);
                }
            }
            $mwtextarea.data('params', {
                pairs: paramPairs,
                lookup: paramLookup
            });
        },
        /* Get the index, if any, of a parameter in the parameter store */
        lookupParamIndex = function (paramName) {
            return $mwtextarea.data('params')
                .lookup.indexOf(paramName);
        },
        /* Retrieve a stored parameter, removing it from the both the parameter lookup and the parameter store */
        spliceParam = function (num) {
            arrays = $mwtextarea.data('params');
            i = arrays.pairs.splice(num, 1);
            arrays.lookup.splice(num, 1);
            $mwtextarea.data('params', arrays);
            return i;
        },
        /* Retrieve a stored param pair, removing it from the param store */
        getStoredParam = function (match) {
            var index = lookupParamIndex(match);
            if (index > -1) {
                var storedVal = spliceParam(index);
                return storedVal;
            }
            return '';
        },
        /* Retrieve a stored param pair, removing it from the param store */
        addParamToStore = function (arr) {
            arrays = $mwtextarea.data('params');
            arrays.pairs.push(arr);
            arrays.lookup.push(arr[0]);
            $mwtextarea.data('params', arrays);
            return;
        },
        /* Create a text input field */
        makeTextField = function (param, width, args) {
            var val = getStoredParam(param);
            val = (val.length === 0) ? '' : val[0][1];
            if (typeof args.placeholder === 'undefined') {
                args.placeholder = '';
            }
            if (typeof args.descriptor === 'undefined') {
              return $('<span>' + makeElem(input, counter++, $.extend(args, {
                        type: 'text',
                        value: val,
                        style: 'width:' + width + '%;margin-left:1%;'
                    })) + '</span>')
                    .data('param', param);
            } else {
                var descriptor = args.descriptor;
                delete args.descriptor;
                return $(makeElem(input, counter++, $.extend(args, {
                        type: 'text',
                        value: val,
                        style: 'width:' + width + '%;'
                    }), descriptor))
                    .data('param', param);
            }
        },
        /* Create a box to display a single property */
        makeNewPropertyBox = function (propNum) {
            return $(stratics.f.util.makehtml(div, {
                'data-propnum': propNum,
                'class': 'ui-corner-all ui-widget-content',
                style: 'background:#101010;padding:4px;margin:1px;'
            }));
        },
        /* Fill a property box with a property's text */
        fillPropertyBox = function ($prop, arr) {
            return $prop.append(stratics.f.util.makehtml(span, {}, arr.join(': ')));
        },
        /* Add a button to remove a property */
        addRemovePropertyButton = function ($prop, arr) {
            return $prop.append($(stratics.f.util.makehtml(input, {
                    'class': 'removePropButton',
                    style: 'color:brown;font-weight:900;padding:0 .2em;float:right;margin:-2px -3px;',
                    type: 'button',
                    value: '✗'
                }))
                .button());
        },
        /* Attach a property box to the property fieldset */
        attachPropertyBox = function ($prop) {
            $('#propDisplay')
                .append($prop);
            return;
        },
        /* Process a single property into the property list */
        addToPropList = function (propNum, arr) {
            var $prop = makeNewPropertyBox(propNum);
            $prop = fillPropertyBox($prop, arr);
            $prop = addRemovePropertyButton($prop);
            attachPropertyBox($prop);
            return;
        },
        /* Cleans the property array (Basically, a chance to remove/change depricated parameters) */
        cleanPropArray = function () {
            getStoredParam('crafted'); // This has the effect of deleting the depricated 'crafted' param from the storage arrays
            return;
        },
        /* Process an array of properties into the property list, and into the property store, if needed */
        addArrayToPropertyList = function (arr) {
            var goodData = [],
                startNum = $('#propDisplay')
                .children()
                .length;

            /* Validate the new data */
            if (!Array.isArray(arr)) { // Not passed an array
                if (typeof console !== 'undefined') {
                    console.log('Expected an array, received ' + arr + ' instead.');
                }
                return;
            }
            if (arr.length === 0) { // Passed an empty array
                return;
            }
            if (!Array.isArray(arr[0])) { // Passed a one-dimensional array
                arr = [arr]; // Convert to a two-dimensional array
            }
            for (i = 0; i < arr.length; i++) { // Test that each parameter has a definition
                if (arr[i].length == 2 && arr[i][1].length !== 0) {
                    goodData.push(arr[i]);
                }
            }

            /* Process the new data */
            for (i = 0; i < goodData.length; i++) {
                addToPropList(startNum++, goodData[i]); // Display the new property
                if ($mwtextarea.data('params')
                    .lookup.length < startNum) // If this is a new property,
                    addParamToStore(goodData[i]); // then add it to the data store
            }
            cleanPropArray();
            return;

        },
        /* Create the disambiguation tab */
        buildDisambigTab = function (option) {
            $('#DisambigInfo')
                .append([
                    stratics.f.util.makehtml(span, {}, 'Often, multiple things in UO share the same name. To allow them to each have their own page, we use disambiguation pages.'), br, br,
                    stratics.f.util.makehtml(span, {}, 'If you are trying to add an item, and find that a page already exists for an item with that name, do not change that page. Instead, create a new page with a descriptive and unique name in parenthesis.'), br, br,
                    stratics.f.util.makehtml(span, {}, 'If you were adding an item from Sonoma named "A Fireworks Wand", you might instead create a page named "UO:A Fireworks Wand (Sonoma Item)".'), br, br,
                    stratics.f.util.makehtml(span, {}, 'Then, on both pages, make sure to set this field. Using that same example, you would put "A Fireworks Wand" in this blank.'),
                    stratics.f.util.makehtml(span, {}, 'Then go to "UO:A Fireworks Wand (disambiguation)". If it does not yet exist, create it, and add this as the entire text of the page: "{{DisambiguationPage}}" (without the quotes).'), br, br,
                    br, br,
                    makeTextField('disambiguation_page', 79, {
                        placeholder: '(Disambiguation Wikipage)',
                        descriptor: 'Disambiguation Wikipage:'
                    })
                ]);
        },
        /* Create the rare item fields for the dialog */
        buildRareFields = function (option) {
            var getBoolValue = function (val) {
                    val = (typeof val === 'undefined' || val.length === 0) ? '' : val[0][1];
                    switch (val.toLowerCase()) {
                    case '1':
                    case 'yes':
                    case 'true':
                        val = 1;
                        break;
                    case '0':
                    case 'no':
                    case 'none':
                    case 'false':
                        val = 0;
                        break;
                    default:
                        val = -1;
                    }
                    return val;
                },
                makeCheckboxField = function (param, descriptor) {
                    var val = getStoredParam(param),
                        $elem = $(makeElem(input, counter++, {
                            type: 'checkbox',
                        }, descriptor, 1))
                        .data('param', param);
                    val = getBoolValue(val);
                    if (val > 0) {
                        $elem.find('input')
                            .prop('checked', true);
                    }
                    return $elem;
                },
                makeTriStateField = function (param, descriptor) {
                    var val = getStoredParam(param),
                        $select = $(makeElem('select', counter++, {
                            style: 'float:right;'
                        })),
                        option1 = stratics.f.util.makehtml('option', {
                            value: '-1'
                        }, "Don't Know"),
                        option2 = stratics.f.util.makehtml('option', {
                            value: '0'
                        }, "No"),
                        option3 = stratics.f.util.makehtml('option', {
                            value: '1'
                        }, "Yes"),
                        $label = $(stratics.f.util.makehtml('label', {
                            style: 'text-align:center;line-height:24px;padding-right:7px;'
                        }, descriptor + nbsp))
                        .data('param', param);

                    $select.append([option1, option2, option3])
                        .val(getBoolValue(val));
                    return $label.append($select);
                },
                makeTypeSelect = function () {
                    var $select = $(makeElem('select', counter++, {
                            id: 'addTypeSelect',
                            type: 'typeSelect',
                            style: 'padding-left:3px;'
                        })),
                        options = [];
                    for (var i = 0; i < itemTypes.length; i++) {
                        options.push(stratics.f.util.makehtml('option', {
                            value: itemTypes[i]
                        }, itemTypes[i]));
                    }
                    return $select.append(options);
                };

            var brclear = '<br style="clear:both;"/>',
                fourColumnCSS = [
                    'column-count: 4;',
                    'column-gap: 20px;',
                    '-moz-column-count: 4;',
                    '-moz-column-gap: 20px;',
                    '-webkit-column-count: 4;',
                    '-webkit-column-gap: 20px;>'
                ];
            var fourColumnDiv = '<div style="' + fourColumnCSS.join() + '">',
                itemTypes = [
                    'Advanced Character Program',
                    'Beta',
                    'Bug',
                    'Christmas',
                    'Cleanup Britannia',
                    'Cornucopia',
                    'Craftable',
                    'Daily',
                    'Developer',
                    'EM',
                    'Exploit',
                    'Faction',
                    'Gift',
                    'GM',
                    'Halloween',
                    'Hourly',
                    'IGM',
                    'Khaldun',
                    'Khaldun Birth',
                    'Loot',
                    'Magic Moments',
                    'Mesanna',
                    'Monthly',
                    'New Player',
                    'NPC Vendor',
                    'Original Necromancy',
                    'Plague Of Despair',
                    'Prepatch',
                    'Quest',
                    'SA Birth',
                    'Seer',
                    'Server Birth',
                    'Sigil',
                    'Stacking Bug',
                    'Store',
                    'Stratics',
                    'T2A Birth',
                    'Trammel Birth',
                    'Trick Or Treat',
                    'Trinsic Invasion',
                    'Unapproved EM',
                    'Weekly'
                ];

            $('#fieldset2')
                .append([
                    makeTextField('name', 86, {
                        placeholder: '(Item name)',
                        descriptor: 'Item Name:'
                    }),
                    br, br,
                    makeTextField('quantity', 12, {
                        placeholder: '(Quantity)',
                        descriptor: 'Quantity:'
                    }),
                    $(stratics.f.util.makehtml('label', {
                        style: 'text-align:center;line-height: 24px;float:right;'
                    }, 'Type(s) of item:' + nbsp))
                    .data('param', 'type')
                    .append([
                        makeTypeSelect(),
                        $(stratics.f.util.makehtml(input, {
                            style: 'color:#f58400;font-weight:900;padding:0 .4em;margin-top:3px;',
                            type: 'button',
                            id: 'addTypeButton',
                            value: '➤'
                        }))
                        .button(),
                        makeTextField('type', 30, {
                            id: 'addTypeDisplay',
                            placeholder: '(Item type)'
                        })
                        .css({
                            backgroundColor: 'rgb(187, 187, 187)',
                            color: '#101010'
                        })
                    ]),
                    br, br,
                    stratics.f.util.makehtml(div, {
                        style: 'margin:-8px 0;'
                    }, "When was this item available? (If just for one day, leave the end date empty.)"),
                    br,
                    makeTextField('year', 12, {
                        placeholder: 'YYYY',
                        descriptor: 'Start date:'
                    }),
                    makeTextField('month', 9, {
                        placeholder: 'MM'
                    }),
                    makeTextField('day', 9, {
                        placeholder: 'DD'
                    }), nbsp,
                    stratics.f.util.makehtml(span, {}, '-'), nbsp,
                    makeTextField('year_end', 12, {
                        placeholder: 'YYYY',
                        descriptor: 'End date:'
                    }),
                    makeTextField('month_end', 9, {
                        placeholder: 'MM'
                    }),
                    makeTextField('day_end', 9, {
                        placeholder: 'DD'
                    }),
                    br,
                    makeTextField('season', 20, {
                        placeholder: 'Season',
                        descriptor: 'Season(s):'
                    }),
                    $(stratics.f.util.makehtml(div, {
                        style: 'display:inline-block;padding: 0em 0 1em 1em;line-height: 1.5em;transform: translateY(11px);'
                    }))
                    .append([
                        stratics.f.util.makehtml(span, {}, nbsp + "If multiple seasons, separate with commas. Example: '12,13'"), br,
                        makeCheckboxField('noseason', 'Check this box if the term "season" would not apply to this item.')
                    ]),
                    br,
                    stratics.f.util.makehtml(div, {
                        style: 'color: brown;font-weight: 900;padding-top:6px;'
                    }, nbsp + "This item was available on which shards?"), br,
                    $(fourColumnDiv)
                    .append([
                        makeCheckboxField('Arirang', 'Arirang'), br,
                        makeCheckboxField('Asuka', 'Asuka'), br,
                        makeCheckboxField('Atlantic', 'Atlantic'), br,
                        makeCheckboxField('Baja', 'Baja'), br,
                        makeCheckboxField('Balhae', 'Balhae'), br,
                        makeCheckboxField('Catskills', 'Catskills'), br,
                        makeCheckboxField('Chesapeake', 'Chesapeake'), br,
                        makeCheckboxField('Drachenfels', 'Drachenfels'), br,
                        makeCheckboxField('Europa', 'Europa'), br,
                        makeCheckboxField('Formosa', 'Formosa'), br,
                        makeCheckboxField('GL', 'Great Lakes'), br,
                        makeCheckboxField('Hokuto', 'Hokuto'), br,
                        makeCheckboxField('Izumo', 'Izumo'), br,
                        makeCheckboxField('LA', 'Lake Austin'), br,
                        makeCheckboxField('LS', 'Lake Superior'), br,
                        makeCheckboxField('Legends', 'Legends'), br,
                        makeCheckboxField('Mizuho', 'Mizuho'), br,
                        makeCheckboxField('Mugen', 'Mugen'), br,
                        makeCheckboxField('Napa', 'Napa Valley'), br,
                        makeCheckboxField('Oceania', 'Oceania'), br,
                        makeCheckboxField('Origin', 'Origin'), br,
                        makeCheckboxField('Pacific', 'Pacific'), br,
                        makeCheckboxField('Sakura', 'Sakura'), br,
                        makeCheckboxField('Sonoma', 'Sonoma'), br,
                        makeCheckboxField('SP', 'Siege Perilous'), br,
                        makeCheckboxField('Wakoku', 'Wakoku'), br,
                        makeCheckboxField('Yamato', 'Yamato'), br
                    ]),
                    $('<div style="float:left;padding-top:7px;">')
                    .append([
                        stratics.f.util.makehtml(div, {
                            style: 'color: brown;font-weight: 900;'
                        }, nbsp + "Is the item..."), br,
                        makeTriStateField('spawning', 'Currently spawning'), brclear,
                        makeTriStateField('legal', 'Legal to own'), brclear,
                        makeTriStateField('animated', 'Animated'), brclear,
                        makeTriStateField('dyable', 'Dyable'), brclear,
                        makeTriStateField('newbie', 'Newbiefied'), brclear,
                        makeTriStateField('translucent', 'Translucent'), brclear,
                        makeTriStateField('wearable', 'Wearable'), brclear,
                        makeTriStateField('wieldable', 'Wieldable')
                    ]),
                    $('<div style="float:right;padding-top:7px;">')
                    .append([
                        stratics.f.util.makehtml(div, {
                            style: 'color: brown;font-weight: 900;'
                        }, nbsp + "Does the item..."), br,
                        makeTriStateField('stackable', 'Stack'), brclear,
                        makeTriStateField('container', 'Work as a container'), brclear,
                        makeTriStateField('doubleclick', 'Have an effect when double-clicked'), brclear,
                        makeTriStateField('eatable', 'Disapear when eaten'), brclear,
                        makeTriStateField('turnable', 'Rotate with a deco tool'), brclear,
                        makeTriStateField('runebook', 'Work like a runebook'), brclear,
                        makeTriStateField('sound', 'Make sounds'), brclear,
                        makeTriStateField('spellbook', 'Work like a spellbook'), brclear,
                        makeTriStateField('walkover', 'Have an effect when walked on')
                    ])
                ]);
            $('#fieldset4')
                .append([
                    makeTextField('engraving', 80, {
                        placeholder: '(Item engraving)',
                        descriptor: 'Item Engraving:'
                    }),
                    br, br,
                    stratics.f.util.makehtml(span, {}, "What is the name of this item when the client is set to use Japanese?"),
                    br,
                    stratics.f.util.makehtml(div, {}, "(If the name is not localized, copy the English name here.)"),
                    br,
                    makeTextField('japanesename', 88, {
                        placeholder: '(Japanese localized name)'
                    }),
                    br, br,
                    stratics.f.util.makehtml(div, {}, "Is there an unofficial name this is known by?"),
                    br,
                    makeTextField('aka', 100, {
                        placeholder: '(Unofficial name)'
                    }),
                    br, br,
                    stratics.f.util.makehtml(div, {}, "List any minor variations in the name of this item (due to typos, etc.):"),
                    br,
                    makeTextField('altname1', 49, {
                        placeholder: '(Alternate name 1)'
                    }),
                    makeTextField('altname4', 49, {
                        placeholder: '(Alternate name 4)'
                    }),
                    makeTextField('altname2', 49, {
                        placeholder: '(Alternate name 2)'
                    }),
                    makeTextField('altname5', 49, {
                        placeholder: '(Alternate name 5)'
                    }),
                    makeTextField('altname3', 49, {
                        placeholder: '(Alternate name 3)'
                    }),
                    makeTextField('altname6', 49, {
                        placeholder: '(Alternate name 6)'
                    }),
                    br
                ]);
            /* At this point, all that is left in the textarea's stored params is item parameters. */
            cleanPropArray();
            addArrayToPropertyList($mwtextarea.data('params')
                .pairs);
        },
        /* Reads the selected option's JSON source, builds the appropriate data fields */
        handleOption = function (option) {
            $proptext.html(propertyInfo.descriptions[0][option.description]);

            var type = [].concat(option.type),
                def = [].concat(option.defaultval),
                elems = [],
                i, j, select, options, contStrings;
            for (i = 0; i < type.length; i++) {
                switch (type[i]) {
                case 'bool':
                    elems.push(makeElem(input, i, {
                        type: 'checkbox',
                        style: 'width:2em;height:2em;margin-left:4em;'
                    }));
                    break;
                case 'container':
                    elems.push(stratics.f.util.makehtml(span, {}, nbsp + 'Value:' + nbsp));
                    contStrings = ['/', 'items' + nbsp + nbsp, '/', 'stones'];
                    for (j = 0; j < 4;) {
                        elems.push(makeNumber(j, def[j]));
                        elems.push(stratics.f.util.makehtml(span, {}, nbsp + contStrings[j++] + nbsp));
                    }
                    break;
                case 'elementGroup':
                    contStrings = ['Physical', 'Fire', 'Cold', 'Poison', 'Energy'];
                    for (j = 0; j < 5;) {
                        elems.push(makeNumber(j, def[j]));
                        elems.push(stratics.f.util.makehtml(span, {}, nbsp + contStrings[j++] + '<br>'));
                    }
                    break;                    
                case 'range':
                    elems.push(stratics.f.util.makehtml(span, {}, nbsp + 'Value:' + nbsp));
                    elems.push(makeNumber(i, def[i]));
                    elems.push(stratics.f.util.makehtml(span, {}, nbsp + '-' + nbsp));
                    elems.push(makeNumber(i, def[i]));
                    break;
                case 'int':
                    if (def != 'varies') {
                        elems.push(stratics.f.util.makehtml(span, {}, nbsp + 'Value:' + nbsp));
                        elems.push(makeNumber(i, def[i]));
                        break;
                    }
                    /* falls through */
                case 'text':
                    elems.push(stratics.f.util.makehtml(span, {}, nbsp + 'Value:' + nbsp));
                    elems.push(makeElem(input, i, {
                        type: 'text',
                        value: def[i]
                    }));
                    break;
                case 'state':
                    elems.push(stratics.f.util.makehtml(span, {}, nbsp + 'Value:' + nbsp));
                    var states = option.states;
                    select = makeElem('select', i, {});
                    options = [stratics.f.util.makehtml('option', {
                        value: "none"
                    })];
                    for (j = 0; j < states.length; j++) {
                        options.push(stratics.f.util.makehtml('option', {
                            value: states[j].output
                        }, states[j].name));
                    }
                    elems.push($(select)
                        .append(options)
                        .data('type', 'state'));
                    break;
                case 'list':
                    elems.push(stratics.f.util.makehtml(span, {}, nbsp + 'Value:' + nbsp));
                    var values = option.values;
                    select = makeElem('select', i, {});
                    options = [stratics.f.util.makehtml('option', {
                        value: "none"
                    })];
                    for (j = 0; j < values.length; j++) {
                        options.push(stratics.f.util.makehtml('option', {
                            value: values[j]
                        }, values[j]));
                    }
                    elems.push($(select)
                        .append(options)
                        .data('type', 'list'));
                    break;
                }
            }
            $toolmenu.data('option', option);
            return elems;
        };

    /* Create popup dialog, tab structure, and data fieldsets */
    var $toolmenu = $(stratics.f.util.makehtml('div', {
            id: 'toolmenu'
        })),
        $div2 = $('<div id="RareInfo1">')
        .hide(),
        $div4 = $('<div id="RareInfo2">')
        .hide(),
        $div3 = $('<div id="PropInfo">')
        .hide(),
        $div5 = $('<div id="DisambigInfo">')
        .hide(),
        $btndiv = $('<div id="button-wrapper">')
        .append([
            $(stratics.f.util.makehtml(input, {
                id: 'saveButton',
                style: 'background:#111!important;color:#e0ac43;font-weight:900;padding:3px 15px;position:absolute;right:30%;bottom:0;',
                type: 'button',
                value: 'Save all changes',
            }))
            .button()
            .hover(function () {
                this.style.backgroundColor = '#666!important';
            }, function () {
                this.style.backgroundColor = '#111!important';
            })
            .hide(),
            $(stratics.f.util.makehtml(input, {
                id: 'cancelButton',
                style: 'background:#111!important;color:salmon;font-weight:900;padding:3px 15px;position:absolute;left:30%;bottom:0;',
                type: 'button',
                value: 'Cancel editing'
            }))
            .button()
            .hover(function () {
                this.style.backgroundColor = '#666!important';
            }, function () {
                this.style.backgroundColor = '#111!important';
            })
        ]),
        $propertyBox = $('<div id="PropBox">'),
        $field1 = makeFieldset(1, 'Type of item'),
        $field2 = makeFieldset(2, ''),
        $field4 = makeFieldset(4, ''),
        $field3 = makeFieldset(3, 'Select a property to add to this item'),
        $field5 = makeFieldset(5, 'Item Properties')
        .css('margin', '0 -10px')
        .append('<div id="propDisplay">'),
        $field6 = makeFieldset(4, 'Item Disambiguation'),
        tab1 = stratics.f.util.makehtml('li', {}, stratics.f.util.makehtml('a', {
            href: loc + '#RareInfo1',
            id: 'tab1'
        }, 'Rare Item Information 1')),
        tab3 = stratics.f.util.makehtml('li', {}, stratics.f.util.makehtml('a', {
            href: loc + '#RareInfo2',
            id: 'tab3'
        }, 'Rare Item Information 2')),
        tab2 = stratics.f.util.makehtml('li', {}, stratics.f.util.makehtml('a', {
            href: loc + '#PropInfo',
            id: 'tab2'
        }, 'Property Info')),
        tab4 = stratics.f.util.makehtml('li', {}, stratics.f.util.makehtml('a', {
            href: loc + '#DisambigInfo',
            id: 'tab4'
        }, 'Item Disambiguation')),
        $tabsInfo = $('<div>', {
            id: 'tabs'
        })
        .hide();

    var $tabsList = $('<ul>');
    if ($mwtextarea.data('type') == 'rare') {
        $tabsList.append([tab1, tab3, tab2, tab4]);
    } else if ($mwtextarea.data('type') == 'common') {
        $tabsList.append([tab2, tab4]);
    }

    $propertyBox.append($field5);
    $toolmenu.append([
        $field1,
        $div2.append($field2),
        $div4.append($field4),
        $div3.append($field3),
        $div5.append($field6),
        $btndiv
    ]);
    $field1.before($tabsInfo.append($tabsList));

    /* Initialize type of item section buttons */
    var typeQuestion = stratics.f.util.makehtml(span, {}, 'Is this a rare item?'),
        $typeBtnCommon = $(makeElem(input, 'CommonItem', {
            type: 'button',
            value: 'No'
        }))
        .button(),
        $typeBtnRare = $(makeElem(input, 'RareItem', {
            type: 'button',
            value: 'Yes'
        }))
        .button();

    /* Add type of item section buttons to the page */
    $field1.append(
        [br,
            typeQuestion,
            br, br, br,
            $typeBtnCommon,
            nbsp, nbsp, nbsp,
            $typeBtnRare
        ]);

    /* Add click handlers to the type of item section buttons */
    $typeBtnCommon.click(function () {
        var $mwtextarea = $('.wikiEditor-ui-text textarea');
        $mwtextarea.data('type', 'common');
        $('#fieldset1,#PropInfo,#DisambigInfo,#buttonSubmitProp,#tabs')
            .toggle();
        $tabsList.append([tab2, tab4]);
        $("#toolmenu")
            .tabs();
    });
    $typeBtnRare.click(function () {
        var $mwtextarea = $('.wikiEditor-ui-text textarea');
        $mwtextarea.data('type', 'rare');
        $('#fieldset1,#RareInfo1,#RareInfo2,#PropInfo,#DisambigInfo,#buttonSubmitProp,#tabs')
            .toggle();
        $tabsList.append([tab1, tab3, tab2, tab4]);
        $("#toolmenu")
            .tabs();
        buildRareFields();
    });

    /* Initialize selects and info collection area for item property section */
    var $select1 = populateSelect($('<select>', {
            id: 'select1'
        }), propertyInfo.category, true),
        $propinfo = makeFieldset('propInfo', ''),
        $proptext = $('<div>', {
            id: 'propText'
        }),
        $select2 = $('<select>', {
            id: 'select2'
        }),
        $submitProp = $(makeElem(input, 'SubmitProp', {
            style: 'position: absolute;bottom: 7%;right: 3%;color:rgb(224, 172, 67);',
            type: 'button',
            value: 'Add this property'
        }))
        .button()
        .hide();

    /* Add selects and info collection area for item property section to the page */
    $field3.append(
            [$select1,
                nbsp,
                $select2.hide(),
                br, br,
                $proptext,
                br, br,
                $propinfo
            ])
        .after($submitProp)
        .after(br + br + br + br + br + br);

    /* Parse existing template data */
    readTemplate();

    /* Initialize popup dialog */
    $toolmenu.appendTo('body')
        .dialog({
            title: 'Item Template Editor',
            autoOpen: false,
            position: {
                my: 'center',
                at: 'center-125',
                of: window
            },
            modal: true,
            width: 890,
            height: 900,
            close: function () {
                $('#PropBox')
                    .dialog('close');
            },
            open: function () {
                if ($('#PropBox')
                    .length > 0 && !$('#PropBox')
                    .dialog("isOpen")) {
                    $('#PropBox')
                        .dialog('open');
                }
            }
        });
    $propertyBox.dialog({
            autoOpen: false,
            height: 700,
            width: 250,
            position: {
                my: 'left bottom',
                at: 'right bottom',
                of: $('#toolmenu')
                    .parent()
            }
        })
        .siblings('div.ui-dialog-titlebar')
        .remove();
    buildDisambigTab();
    switch ($('.wikiEditor-ui-text textarea')
        .data('type')) {
    case 'common':
        $('#fieldset1')
            .hide();
        $('#PropInfo,#DisambigInfo,#buttonSubmitProp')
            .show();
        break;
    case 'rare':
        $('#fieldset1')
            .hide();
        $('#RareInfo1,#RareInfo2,#PropInfo,#DisambigInfo,#buttonSubmitProp,#tabs')
            .show();
        $("#toolmenu")
            .tabs();
        buildRareFields();
    }

    /* Click handler for the 'save edits' button */
    $('#saveButton')
        .click(function () {
            var rareData = [],
                $parents = $('#RareInfo1, #RareInfo2, #DisambigInfo');

            $parents.find('input[type=text]')
                .filter(function () {
                    return !!this.value;
                })
                .each(function () {
                    var $this = $(this);
                    rareData.push([$this.parent()
                        .data('param'), $this.val()
                    ]);
                });
            $parents.find('input[type=checkbox]:checked')
                .each(function () {
                    var $this = $(this);
                    rareData.push([$this.parent()
                        .data('param'), 1
                    ]);
                });
            $parents.find('select:not(#addTypeSelect)')
                .filter(function () {
                    return this.value > -1;
                })
                .each(function () {
                    var $this = $(this);
                    rareData.push([$this.parent()
                        .data('param'), $this.val()
                    ]);
                });

            var outputArr = $.merge(rareData, $mwtextarea
                .data('params')
                .pairs);

            var disambig = outputArr.indexOf('disambiguation_page'),
                outputStr = '';

            if ($mwtextarea.data('type') == 'common' && disambig > -1) {
                disambig = outputArr.splice(disambig, 1);
                disambig = '\n{{UO Disambiguation Header\n| ' + disambig.join(' = ') + '\n}}';
            } else {
                disambig = '';
            }

            for (i = 0; i < outputArr.length; i++) {
                outputStr += '| ' + outputArr[i].join(' = ') + '\n';
            }
            outputStr = '{{' + ($mwtextarea.data('type') == 'common' ? 'InfoBox_UOItem' : 'RareItem') + '\n' + outputStr + '}}' + disambig;
            $mwtextarea.val(outputStr + $mwtextarea.val()
                .replace(/{{(?:RareItem|InfoBox[\s|_]UOItem)[\s\S]*}}/, ''));
            $toolmenu.dialog("close");
            $('#wpSave')
                .trigger('click');
        });

    /* Click handler for the 'cancel editing' button */
    $('#cancelButton')
        .click(function () {
            $toolmenu.dialog("close");
        });

    /* Click handler for the 'add type' button */
    $('#addTypeButton')
        .click(function () {
            $('#saveButton')
                .show();
            var $typeDisplay = $('#addTypeDisplay');
            var types = $typeDisplay.val()
                .split(/,\s?/g);
            types.push($('#addTypeSelect option:selected')
                .val());
            $typeDisplay.val([...new Set(types)].join(', '));
        });

    /* Click handler for the 'delete property' button */
    $('#propDisplay')
        .on('click', '.removePropButton', function () {
            $('#saveButton')
                .show();
            var $parent = $(this)
                .parent();
            spliceParam($parent.attr('data-propnum'));
            $('#propDisplay')
                .empty();
            addArrayToPropertyList($mwtextarea.data('params')
                .pairs);
        });

    /* Show the 'save edits' button whenever any edit has been made */
    $('#toolmenu')
        .on('change keyup', 'select, input', function () {
            $('#saveButton')
                .show();
        });

    /* Change handler for select 1 */
    $select1.change(function () {
        var $this = $(this);
        var selected = $this.find(':selected')
            .val();
        var thisOption = $select1.data('source')[selected];
        $proptext.empty();
        $propinfo.empty();
        if (selected !== "none") {
            if (selected === "all") {
	            let allOpt = [];
				$('#select1').data('source').forEach(group => {
    				if (group.hasOwnProperty('options')) {
				        allOpt.push(...group.options);
					}
				});
				allOpt = allOpt.sort((a, b) => (a.name > b.name) ? 1 : -1);
               	populateSelect($select2, allOpt);
            } else {
            	var type = getOptionType($this, selected);
            	if (type == 'option') {
	                $select2.show();
    	            populateSelect($select2, thisOption.options);
        	    } else {
            	    $select2.hide();
                	$propinfo.append(handleOption(thisOption));
            	}
            }
        } else {
            $select2.hide();
        }
    });

    /* Change handler for select 2 */
    $select2.change(function () {
        var $this = $(this);
        var selected = $this.find(':selected')
            .val();
        var thisOption = $select2.data('source')[selected];
        $proptext.empty();
        $propinfo.empty();
        if (selected !== "none") {
            handleOption(thisOption);
            $propinfo.append(handleOption(thisOption));
        }
    });

    /* Save Property button handler */
    $submitProp.click(function () {
        var $propField = $('#fieldsetpropInfo');
        var $children = $propField.children(':not(span)');
        var childCount = $children.length,
            showError = function (err) {
                $('#propText')
                    .html(stratics.f.util.makehtml('span', {
                        style: 'color:red;'
                    }, err));
                errorcount++;
            },
            highlightField = function ($obj) {
                $obj.css('background', 'brown')
                    .addClass('lastError');
            };

        $('#propText')
            .empty();
        $('.lastError')
            .css('background', '#101010')
            .removeClass('lastError');

        /* Check that any property fields exist */
        if (childCount === 0) {
            showError("You don't have any property selected!");
        } else {

            var option = $('#toolmenu')
                .data('option'),
                values = [],
                errorcount = 0,
                val;

            if (option === 'undefined') {
                showError("You don't have any property selected!");
            } else {
                var output = [].concat(option.output);

                for (var i = 0; i < childCount; i++) {
                    var $thisChild = $children.eq(i);
                    var eletype = $thisChild.prop('nodeName');
                    if (eletype === 'SELECT') {
                        var type = $thisChild.data('type');
                        val = $thisChild.val();
                        if (val === 'none') {
                            highlightField($thisChild);
                            showError("You haven't set a value for this property!");
                        } else {
                            if (type === 'list') {
                                if (/#/.test(output[i])) {
                                    values.push(output[i].replace('#', val.replace("Just 'Mana Burst'", ''))
                                        .split('='));
                                } else {
                                    values.push([output[i], val]);
                                }
                            } else if (type === 'state') {
                                values.push(val.split('='));
                            }
                        }
                    } else if (eletype === 'INPUT') {
                        switch ($thisChild.attr('type')) {
                        case 'checkbox':
                            val = $thisChild.is(':checked');
                            if (val === false) {
                                highlightField($thisChild);
                                showError("Only add this property if it is true (ie, the checkbox is checked).");
                            } else {
                                values.push([output[i], 1]);
                            }
                            break;
                        case 'number':
                            /* falls through */
                        case 'text':
                            val = $thisChild.val();
                            if (val.length === 0) {
                                highlightField($thisChild);
                                showError("You haven't set a value for this property!");
                            } else {
                                if (/#/.test(output[i])) {
                                    values.push(output[i].replace('#', val)
                                        .split('='));
                                } else {
                                    output[i] = output[i].split('|');
                                    if (output[i].length == 2) {
                                        values.push(output[i][0].split('='));
                                        values.push([output[i][1], val]);
                                    } else {
                                        values.push([output[i][0], val]);
                                    }
                                }

                            }
                        }
                    } else {
                        /* This should be unreachable. */
                    }
                }
                /* Check for any redundant properties */
                var errCnt = 0,
                    matchIndex;

                for (var v = 0; v < values.length; v++) {
                    matchIndex = lookupParamIndex(values[v][0]);
                    if (matchIndex > -1) {
                        errCnt++;
                        highlightField($('#propDisplay div:eq(' + matchIndex + ')'));
                    }
                }
                if (errCnt == 1) {
                    showError("This property already exists.");
                } else if (errCnt > 1) {
                    showError("These properties already exist.");
                }

                if (errorcount === 0) {
                    $('#saveButton')
                        .show();
                    addArrayToPropertyList(values);
                    $proptext.empty();
                    $propinfo.empty();
                    $select1.val('none')
                        .trigger('change');
                }
            }
        }
    });
    $('.namespace')
        .after($('<a>')
            .addClass('buttonTrigger')
            .text('Open Item Editor')
            .click(function (e) {
                e.preventDefault();
                $('#toolmenu')
                    .dialog('open');
            }));
};