AtlasTools.js

Revision as of 15:03, 30 March 2017 by BrianFreud (talk | contribs)

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.
/* jshint esversion: 6, bitwise: false */
/* globals stratics, mw, jQuery */

(function ($) {
 "use strict";

/* Init */
$('#editlocation').append('<input type="button" value="Click to select a pin to edit" id="pinSelect"/><div id="pinEditDiv">')
    .find('.pinEditCancel, .pinEditStore, .pinEditSubmit')
    .invis()
    .add('#pinSelect')
    .button();

var makehtml = stratics.f.util.makehtml,
    nbsp = '&nbsp;',
    br = '<br>';

/* Remove unwanted default title attributes that cause browser tooltips. */
$('a').each(function() {
    $(this).removeAttr("title");
});

/* Keep the map visible while scrolling. */
if (!/&curid/.test(location.search)) { // Don't take effect on edit diff pages
    var $body = $('#bodyContent'),
        $map = $('.map-holder:not(.noscroll)'),
        $doc = $(document);
    if ($map.length > 0) {
        var mapleft = $map[0].getBoundingClientRect().left;
        $map.css({
            position: 'fixed',
            left: mapleft,
            top: $body.offset().top - $doc.scrollTop()
        });
        $(window).scroll(function() {
            var ymax = $body.offset().top + $body.height() - $map.height() - $doc.scrollTop();
            if (ymax < 0) {
                $map.css('top', ymax);
            } else if ($('#mw-head').isOnScreen()) {
                $map.css('top', $body.offset().top - $doc.scrollTop());
            } else {
                $map.css('top', $('#navigation').height());
            }
        });
    }
}

/* Flash icons on hover. */
$("span.pin").each(function () {
    $(this).find('img').before('<span>');
});
$("#UOLinksList").on("mouseover", "span", function() {
    var pair = $(this).attr("data-pair") || '';
    pair = '#' + pair.replace(/\:/g,'\\:');
    $(pair).addClass('highlight').find('span').addClass('pin-circle');
}).on("mouseout", "span", function() {
    var pair = $(this).attr("data-pair") || '';
    pair = '#' + pair.replace(/\:/g,'\\:');
    $(pair).removeClass('highlight').find('span').removeClass('pin-circle');
});

/* Add jCanvas (http://projects.calebevans.me/jcanvas/docs/) */
$('head').append(makehtml('script', {
    src: 'https://cdnjs.cloudflare.com/ajax/libs/jcanvas/16.7.3/jcanvas.min.js'
}));

/* Adds a new canvas layer for each map */
var addCanvas = function(thisClass, z) {
    $('.map:not(.mapcanvas)').each(function() {
        var $img = $(this).find('img');
        $(this).after(makehtml('div', {
                class: 'map mapcanvas',
                style: $(this).attr('style') + ((z === undefined) ? '' : 'z-index:' + z)
            },
            makehtml('canvas', {
                class: thisClass,
                width: $img.attr('width'),
                height: $img.attr('height'),
            })
        ));
    });
};

/* Create the trigger icon */
$('.map-holder').append(makehtml('aside', {
    class: 'pin menutrigger',
    style: 'font-size: 25px;color:rgb(224, 171, 67);'
}, '&#9881;'));

/* Create and add the base tab html */
var loc = location.href.toString().split('#')[0],
    tab1 = makehtml('li', {}, makehtml('a', {
        href: loc + '#mapoptions'
    }, 'Options')),
    tab2 = makehtml('li', {}, makehtml('a', {
        href: loc + '#mylocation'
    }, 'Find your location')),
    tab3 = makehtml('li', {}, makehtml('a', {
        href: loc + '#showhide'
    }, 'Show/hide icons')),
    tab4 = makehtml('li', {}, makehtml('a', {
        href: loc + '#addlocation'
    }, 'Add location')),
    tab5 = makehtml('li', {}, makehtml('a', {
        href: loc + '#editlocation'
    }, 'Edit location'));
$("#toolmenu").prepend(makehtml('ul', {}, tab1 + tab2 + tab3 + tab4 + tab5));

/* Create UI for 'find me' tab */
var xinput = makehtml('input', {
        id: 'xcoord',
        maxlength: '4',
        size: '4',
        style: 'margin-top: 13px;padding: 2px 5px;min-height: 0;',
        type: 'text'
    }),
    yinput = makehtml('input', {
        id: 'ycoord',
        maxlength: '4',
        size: '4',
        style: 'margin-top: 13px;padding: 2px 5px;min-height: 0;margin-right: 7px;',
        type: 'text'
    }),
    findbutton = makehtml('input', {
        id: 'findme',
        type: 'button',
        value: 'Find me'
    }),
    removebutton = makehtml('input', {
        id: 'findme',
        type: 'button',
        value: 'Remove me from the map'
    });
$('#mylocation').append('x: ' + xinput + 'y: ' + yinput + ' ' + findbutton + ' ' + removebutton);

/* Populate the list of pintypes being used */
var $checkboxes = $(makehtml('div', {
    id: 'pinlist'
}));
$('.pin-types').each(function() {
    var inputText = $(this).attr('data-name'),
        inputType = $(this).attr('data-pin');
    $checkboxes.append(makehtml('label', {}, makehtml('input', {
        'data-type': inputType,
        type: 'checkbox',
        checked: 'true'
    }, inputText)) + '<br>');
});
$('#showhide').append($checkboxes);

/* Populate the options tab for the gridlines option */
$('#mapoptions').append(makehtml('label', {}, makehtml('input', {
    'data-option': 'gridlines',
    type: 'checkbox',
  }, 'Show gridlines')));

/* Populate the options tab for the radar map option */
$('#mapoptions').append('<br><br>' + makehtml( + 'label', {}, makehtml('input', {
    'data-option': 'radarMap',
    type: 'checkbox',
  }, 'Tilt map like radar view')));

/* Enable the HTML for the add location tab */
var $addloc = $('#addlocation');
$addloc.html($addloc.html().replace(/&lt;/g,'<').replace(/&gt;/g,'>'));

/* Add the control buttons for the pin edit tabs */
$('.editPinButtons').append([
    '<input type="button" class="pinEditCancel" value="Cancel editing this pin" />',
    '<input type="button" class="pinEditStore" value="Store edits to this pin" />',
    '<input type="button" class="pinEditSubmit" value="Submit all stored edits" />'
]);

/* Initialize tabs and popup dialog */
$("#toolmenu").tabs({
    activate: function (event, ui) {
        var active = $('#toolmenu').tabs('option', 'active');
        $("#toolmenu").dialog({
            height: {
                mylocation:   225,
                mapoptions:   285,
                showhide:     600,
                addlocation:  500,
                editlocation: 400
            }[$("#toolmenu ul>li a").eq(active).attr('href').split('#')[1]]
        });
    }
}).dialog({
    autoOpen: false,
    draggable: true,
    width: 700,
    height: 285
});
$('.pinEditCancel, .pinEditStore, .pinEditSubmit').invis();

/* Add event handler for opening the dialog */
$(".menutrigger").on("click", function() {
    $('.mapmarker').remove();
    $("#toolmenu").dialog("open").parent().css({
        opacity: '.8'
    });
    $(this).parent().find('div:eq(0)').append(makehtml('span', {
        class: 'mapmarker'
    }));
});

/* Add event handler for 'find me' button */
$('#findme').click(function() {

    var mainAtlas = /^UO\:Atlas\/[A-Za-z]+\//.test(mw.config.values.wgPageName);
    $('.mapmarker').after(makehtml('span', {
            class: 'yourposition ' + $('.mapmarker').prev().attr('class'),
            'data-name': 'Your location',
            style: 'left: ' + $("#xcoord").val() + 'px; top: ' + $("#ycoord").val() + 'px;'
        },
        makehtml('img', {
            src: 'http://stratics.com/w/images/b/b4/UO-icon-player.png',
            width: (mainAtlas ? 6 : 12),
            height: (mainAtlas ? 13 : 26)
        })
    ));
    $("#toolmenu").dialog("close");
});

/* Add event handler for 'remove me' button */
$('#removeme').click(function() {
    $('.yourposition').remove();
});

/* Add event handler for changes to the pin list checkboxes */
$('#pinlist').on('change', 'input', function() {
    var pintype = $(this).attr('data-type');
    $('.' + pintype)[this.checked ? 'show' : 'hide']();
});

/* Add container for the radar map option */
$('.map').parent().wrap('<div class="mapCollection"><div>');

/* Add event handler for selections on the options tab */
$('#mapoptions').on('change', 'input', function() {
    var option = $(this).attr('data-option');
    switch (option) {
        case 'gridlines':
        case 'serverlines':
            $('.' + option).toggle();
            break;
        case 'radarMap':
            $('.mapCollection').toggleClass('radarMap');
            break;
    }
});

/* Create gridline overlays */
addCanvas('gridlines', 1);
$('.gridlines').hide().each(function() {
    var x,
        y,
        width = $(this).width(),
        height = $(this).height(),
        scale = $(this).parent().parent().getScale(),
        insets = $(this).parent().getInsets();
    var visibleX = parseInt(insets[3], 10) + 4 + (96 / scale),
        visibleY = parseInt(insets[0], 10) + (72 / scale),
        linewidth = Math.ceil(5 / scale),
        fontsize = Math.ceil(60 / scale) * 1.4;

    fontsize = (fontsize > 60) ? 60 : fontsize;

    for (y = 0; y < height; y = y + 100) {
        $(this).drawLine({
            strokeStyle: 'rgb(224, 171, 67)',
            strokeWidth: linewidth,
            x1: 0,
            y1: y,
            x2: width,
            y2: y
        }).drawText({
            fillStyle: '#fff',
            x: visibleX,
            y: y + 5,
            fontSize: fontsize,
            fontFamily: 'Verdana, sans-serif',
            text: y
        });
    }

    for (x = 100; x < width; x = x + 100) {
        $(this).drawLine({
            strokeStyle: 'rgb(224, 171, 67)',
            strokeWidth: linewidth,
            x1: x,
            y1: 0,
            x2: x,
            y2: height
        });
    }

    for (x = 100; x < width; x = x + ((scale == 1) ? 200 : 100)) {
        $(this).drawText({
            fillStyle: '#fff',
            x: x,
            y: visibleY,
            fontSize: fontsize,
            fontFamily: 'Verdana, sans-serif',
            text: x
        });
    }
});

/* Add functionality for the Add Location tab */
addCanvas('addlocation', 3);
$('#getlocbtn').click(function() {
    $("#toolmenu").dialog("close");
    $('.addlocation,.gridlines,.serverlines').css('cursor', 'crosshair').click(function(event) {
        var $this = $(this),
            rect = this.getBoundingClientRect();
        var x = Math.round($this.width() / rect.width * (event.clientX - rect.left)),
            y = Math.round($this.height() / rect.height * (event.clientY - rect.top));
        var makeWiki = function() {
            var str = '-->{{UOAddPin|';
            str += $('#getlocselect').val() + '|';
            str += x + '|' + y + '|' + $('#getlocdescription').val();
            var wiki = $('#getlocwikipage').val();
            if (wiki.length > 0) {
                str += '|link=' + wiki;
            }
            str += '}}<!--';
            return str;
        };

        $('.addlocation,.gridlines,.serverlines').css('cursor', 'auto');
        $('#getloccoords').text(x + ' x ' + y);
        $('#getlocresult').text(makeWiki);
        $('#getloctext').show();
        $("#toolmenu").dialog("open");
        $('#getlocwikipage, #getlocselect, #getlocdescription').on("change keyup", function() {
            $('#getlocresult').text(makeWiki);
        });
    });
});

/* Create a large box for directions in the bottom left corner */
stratics.f.util.$makeDirections = () => {
    return $('<div>', {
        'class': 'largeFloatingInstructions',
        id: 'instructionBox'
    }).appendTo('body');
};

/* Destroys the large box for directions in the bottom left corner */
stratics.f.util.removeDirections = () => {
    return $('#instructionBox').remove();
};

/* Empties the edit pin tab, resetting it back to initial state */
stratics.f.atlas.ui.resetEditPin = () => {
    $('#pinSelect, #pinEditDiv').show();
    $('.pinEditCancel, .pinEditStore').invis();
    $('#toolmenu').tabs('enable');
    return $('#pinEditDiv').empty();
};

/* Event handler for markup AJAX request */
$('body').on('markupArrived', function () {
    let wiki = stratics.wiki,
        marker = 'ADD LOCATIONS HERE.  DO NOT MODIFY ABOVE THIS LINE.';
    var markup = stratics.wiki.markup;

    $('#toolmenu').dialog('close');
    stratics.f.util.$makeDirections().html('Submitting edits, please wait...');

    if (markup.length > 0 && wiki.openEdits.length > 0) {
        let edits = wiki.openEdits;
        for (let edit of edits) {
            if (Array.isArray(edit)) { // Pin edit
                markup = markup.replace(edit[0], edit[1]);
            } else if ('string' === typeof edit) { // Pin addition
                markup = markup.replace(marker, marker + '\n' + edit);
            }
        }
    }

    stratics.wiki.markup = markup;
    stratics.f.wiki.submitMarkup('[Pin Editor]: Edits to ' + wiki.openEdits.length + ' pins.', stratics.wiki.markup); // Asynch
    wiki.openEdits = [];
    stratics.wiki.markup = '';
});

/* Creates a string of markup with data for a single pin */
stratics.f.wiki.buildPinMarkup = (data) => {
    let retval = '-->{{UOAddPin|' + [data.type.replace('_',' '), data.x, data.y, data.name].join('|');
    if (data.hasOwnProperty('page') && undefined !== data.page && data.page.length > 0) {
        retval += '|link=' + data.page;
    }
    return retval + '}}<!--';
};

/* Reset a pin to its original location */
stratics.f.wiki.setPinCoordsOrig = ($pin) => {
    let [x, y] = $pin.attr('id').split(':'),
        offset = {
            x: ~~$pin.attr('data-offsetX'),
            y: ~~$pin.attr('data-offsetY')
        };

    $pin.css({
        left: (~~x - offset.x) + 'px',
        top: (~~y - offset.y) + 'px'
    });  
};

/* Gets the original markup for a pin */
stratics.f.wiki.getPinMarkupOrig = (data) => {
    let info = $('#editlocation').data('selection').attr('id').split(':');

    return stratics.f.wiki.buildPinMarkup({
        x: info[0],
        y: info[1],
        type: info[2],
        name: $('#editName').data('initial'),
        page: $('#editPage').data('initial')
    });
};

/* Gets the new markup for a pin on the edit tab */
stratics.f.wiki.getPinMarkupEdit = (data) => {
    return stratics.f.wiki.buildPinMarkup({
        x: $('#editX').val(),
        y: $('#editY').val(),
        type: $('#editType').val(),
        name: $('#editName').val(),
        page: $('#editPage').val()
    });
};

/* Gets the new markup for a pin on the add tab */
stratics.f.wiki.getPinMarkupAdd = (data) => {
    let info = $('#getloccoords').split(' x ');

    return stratics.f.wiki.buildPinMarkup({
        x: info[0],
        y: info[1],
        type: $('#getlocselect').val(),
        name: $('#getlocdescription').val(),
        page: $('#getlocwikipage').val()
    });
};

/* Allows the keyboard to be used to control the arrow buttons on the edit pin tab. */
stratics.f.atlas.movePinByKey = e => {
    var key = stratics.keycodes[e.which] || false;
    if (!!key && e.which > 32 && e.which < 41) {
        e.preventDefault();
        e.stopPropagation();
    }
    $('#arrow' + key).trigger('click');
};

/* Handles a change to a pin's location on the X axis */
stratics.f.atlas.changePinX = function (amount) {
    var $pin = stratics.f.atlas.changePinInput($('#editX'), amount);
    var left = parseInt($pin.css('left'), 10);
    $pin.css('left', ~~left + ~~amount);
    return;
};

/* Handles a change to a pin's location on the Y axis */
stratics.f.atlas.changePinY = function (amount) {
    var $pin = stratics.f.atlas.changePinInput($('#editY'), amount);
    var top = parseInt($pin.css('top'), 10);
    $pin.css('top', ~~top + ~~amount);
    return;
};

/* Utility function to change the value in an pin location input. */
/* Returns a jQuery-wrapped element which is the pin on the map. */
stratics.f.atlas.changePinInput = function ($pin, amount) {
    var val = $pin.val();
    $pin.val(~~val + ~~amount);
    return $('#editlocation').data('selection');
};

/* Populate the Edit location tab when a location is selected. */
$('#editlocation').on('pinSelected', function (e) {
    let $pin = $(e.target).data('selection'),
        makehtml = stratics.f.util.makehtml;
    let info = $pin.attr('id').split(':'),
        arrowBtn = (value, id) => {
            return makehtml('input', {
                id: 'arrow' + id,
                type: 'button',
                style: "width:2em;height:2em;opacity:.8;",
                value
            });
    };

    $('#pinSelect').attr('value', 'Click to select another pin to edit')
        .hide();
    $('#pinEditDiv').empty()
        .append([
            br,
            $pin.html(),
            nbsp,
            $('<input>', {
                id: 'editName',
                style: 'width:30em;',
                type: 'text',
                value: $pin.attr('data-name')
            }).data('initial', $pin.attr('data-name')),
            $('<div>', {id:'arrowControls', style:'float:right;'}).append([
                arrowBtn('🡼','NW'),
                arrowBtn('🡹','N'),
                arrowBtn('🡽','NE'),
                br,
                arrowBtn('🡸','W'),
                $('<span>', {style:'vertical-align: bottom;display:inline-block;width:2em;height:2em;'}),
                arrowBtn('🡺','E'),
                br,
                arrowBtn('🡿','SW'),
                arrowBtn('🡻','S'),
                arrowBtn('🡾','SE')
            ]),
            br, br,
            'Location: ',
            '<label>x:' + makehtml('input', {
                id: 'editX',
                style: 'width:6em;margin-left:5px;margin-right:10px;',
                type: 'number',
                value: info[0]
            }) + '</label>',
            $('<label>y:' + makehtml('input', {
                id: 'editY',
                style: 'width:6em;margin-left:5px;margin-right:10px;',
                type: 'number',
                value: info[1]
            }) + '</label>').data('initial', info[2]),
            $('#getlocselect').clone()
            .attr('id', 'editType')
            .val(info[2].replace(/_/g, ' '))
            .data('initial', info[2]),
            br, br,
            '<label>Wikipage: ' + makehtml('input', {
                id: 'editPage',
                style: 'width:28em;',
                type: 'text',
                value: $pin.attr('data-page') || ''
            }) + '</label>',
        ])
        .find('img')
        .attr('id', 'editImage');

    $('#editX').data('initial', info[0]);
    $('#editY').data('initial', info[1]);
    $('#editPage').data('initial', $pin.attr('data-page'));

    /* Start: Move pin click handlers */
    let setArrowClick = (dir, x, y) => {
        $('#arrow' + dir).click(() => {
            if (!!x) stratics.f.atlas.changePinX(x);
            if (!!y) stratics.f.atlas.changePinY(y);
        });
    };
    setArrowClick('N', 0, -1);
    setArrowClick('W', -1, 0);
    setArrowClick('S', 0, 1);
    setArrowClick('E', 1, 0);
    setArrowClick('NW', -1, -1);
    setArrowClick('SW', -1, 1);
    setArrowClick('NE', 1, -1);
    setArrowClick('SE', 1, 1);
    /* End: Move pin click handlers */

    /* Handlers for changes directly to the x and y inputs */
    $('#editX, #editY').on('change, keyup input mousewheel', () => {
        let $pin = $('#editlocation').data('selection');
        let offset = {
            x: ~~$pin.attr('data-offsetX'),
            y: ~~$pin.attr('data-offsetY')
        };

        $pin.css({
            left: (~~$('#editX').val() - offset.x) + 'px',
            top: (~~$('#editY').val() - offset.y) + 'px'
        });
    });

    /* Pin-type change handler */
    $('#editType').change(() => {
        $('#editlocation').data('selection')
            .add('#editImage')
            .each(() => {
                let classes = $(this).attr('class'),
                    newClass = 'icon-' + $('#editType').val().replace(/\s/g, '_');
                $(this).attr('class', classes.replace(/icon\-\S+/, newClass));
            });
    });

    /* Enable keyboard movement of pins */
    $('body').on('keydown.pinselect', function (e) {
        if (!$(e.target).is('#editName, #editX, #editY, #editType')) {
            stratics.f.atlas.movePinByKey(e);
        }
    });

    $('#toolmenu').tabs('disable')
        .dialog('open');
});

/* Turns off pin-selection mode */
stratics.f.atlas.ui.disablePinSelectionMode = (e) => {
    e.preventDefault();
    $('.map-holder').css('cursor','default');
    $('body').off('keyup.checkEscape');
    $('span.pin').off('click.pinSelect');
    stratics.f.util.removeDirections();
    $('.menutrigger').show();
};

/* Handler for the "select a pin to edit" button */
$('#pinSelect').click(function () {
    /* Turns on pin-selection mode */
    $('#toolmenu').dialog('close');
    $('.map-holder').css('cursor','pointer');
    $('.menutrigger').hide();
    stratics.f.util.$makeDirections().html('Click on a pin on the map to select it.<br/>Hit the escape key to cancel.');

    $('body').trigger('focus');

    /* Handler for ESC being pressed */
    $('body').on('keyup.checkEscape', function (e) {
        if (e.which === 27) {
            stratics.f.atlas.ui.disablePinSelectionMode(e);
            stratics.f.atlas.ui.resetEditPin();
            $('#toolmenu').dialog('open');
        }
    });

    /* Handler for a click on a pin */
    $('span.pin').on('click.pinSelect', function (e) {
        stratics.f.atlas.ui.disablePinSelectionMode(e);
        $('.pinEditCancel, .pinEditStore', '#editlocation').vis();
        $('#editlocation').data('selection', $(this))
            .trigger('pinSelected');
    });
});

/* Handler for the "cancel editing pin" button */
$('#editlocation').on('click', '.pinEditCancel', () => {
    stratics.f.wiki.setPinCoordsOrig($('#editlocation').data('selection'));
    stratics.f.atlas.ui.resetEditPin();
});

/* Handler for the "store pin edit" button */
$('#editlocation').on('click', '.pinEditStore', () => {
    let wikiF = stratics.f.wiki;

    $('body').off('keydown.pinselect');
    stratics.wiki.openEdits.push([wikiF.getPinMarkupOrig(), wikiF.getPinMarkupEdit()]);
    $('.pinEditSubmit').vis();
    stratics.f.atlas.ui.resetEditPin();
});

/* Handler for the "submit edits" button */
$('.pinEditSubmit').click(() => {
    stratics.f.wiki.getMarkup(); // Asynch request
    $(this).invis();
});

if (/^UO\:Atlas\/(?:Fel|Tram)/.test(mw.config.values.wgPageName)) { /* Only run on Felucca and Trammel maps. */

    /* Populate the options tab for the gridlines option */
    $('#mapoptions').append('<br><br>' + makehtml('label', {}, makehtml('input', {
        'data-option': 'serverlines',
        type: 'checkbox',
    }, 'Show server lines')));

    /* Create server line overlays */
    addCanvas('serverlines', 2);
    $('.serverlines').hide().each(function() {
        var $this = $(this);
        var height = $this.height(),
            scale = $this.parent().parent().getScale();
        var linewidth = Math.ceil(5 / scale);
        var makeLine = function(x1, y1, x2, y2) {
            $this.drawLine({
                strokeStyle: 'red',
                strokeWidth: linewidth,
                x1: x1,
                y1: y1,
                x2: x2,
                y2: y2
            });
        };

        makeLine(0, 2030, 2678, 2030);
        makeLine(2678, 0, 2678, height);
        makeLine(0, 1281, 2679, 1281);
        makeLine(1536, 1281, 1536, 0);
        makeLine(3838, 0, 3838, height);
    });

}

})(jQuery);