// ==UserScript==
// @id             iitc-plugin-bookmarkUnderDraw
// @name           IITC plugin: Bookmark portals under draw or search result.
// @author         Jormund
// @category       Controls
// @version        1.0.0.20221113.2221
// @description    [2022-11-13-2221] Bookmark portals under draw or search result.
// @updateURL      https://raw.githubusercontent.com/Jormund/bookmark_under_draw/master/bookmark_under_draw.meta.js
// @downloadURL    https://raw.githubusercontent.com/Jormund/bookmark_under_draw/master/bookmark_under_draw.user.js
// @match          https://intel.ingress.com/*
// @match          https://intel-x.ingress.com/*
// @match          https://*.ingress.com/intel*
// @grant          none
// ==/UserScript==
//Changelog
//1.0.0: Activate on intel-x.ingress.com
//0.2.0: Fix error with IITC-CE, MultiPolygon doesn't exist in Leaflet 1.4
//0.1.15: activate on intel.ingress.com, changed download url to github
//0.1.14: remove bookmarks under draw or search
//0.1.13: sort by name
//0.1.12: buttons to clear draw and bookmarks
//0.1.11: sort new bookmarks
//0.1.10: preference dialog now opens with the star button (removed from portal detail)
//0.1.9: handle holes (can happen in search result)
//0.1.8: use same algorithm as layer-count (better approximation of "curved" edges), still not an exact solution for GeodesicPolygons
//0.1.7: handle ingress.com and www.ingress.com
//0.1.6: dropdownlist to choose folder

function wrapper(plugin_info) {
    if (typeof window.plugin !== 'function') window.plugin = function () { };

    // PLUGIN START ////////////////////////////////////////////////////////
    window.plugin.bookmarkUnderDraw = function () { };
    window.plugin.bookmarkUnderDraw.work = {};


    window.plugin.bookmarkUnderDraw.KEY_STORAGE = 'bookmarkUnderDraw-storage';
    window.plugin.bookmarkUnderDraw.DRAW_TOOLS_KEY_STORAGE = 'plugin-draw-tools-layer';

    window.plugin.bookmarkUnderDraw.actions = { ADD: 'ADD', REMOVE: 'REMOVE' };
    window.plugin.bookmarkUnderDraw.DEFAULT_FOLDER_ID = 'idOthers'; //using string constant because bookmark plugin might not be loaded yet
    window.plugin.bookmarkUnderDraw.DEFAULT_NOT_LOADED_PORTAL_NAME = 'UNKNOWN PORTAL NAME';
    window.plugin.bookmarkUnderDraw.DEFAULT_BOOKMARK_UNKNOWN_PORTAL = false;
    window.plugin.bookmarkUnderDraw.DEFAULT_BOOKMARK_HIDDEN_PORTAL = true;
    window.plugin.bookmarkUnderDraw.sorts = {
        NONE: {
            code: "NONE", name: "No sort", compareFunction: function (a, b) { return 0 }
        },
        EAST_TO_WEST: {
            code: "EAST_TO_WEST", name: "East to west",
            compareFunction: function (a, b) {
                if (a.lng > b.lng) return -1;
                if (a.lng < b.lng) return 1;
                //when lng are equal, sort SOUTH_TO_NORTH to make the sort stable
                if (a.lat > b.lat) return 1;
                if (a.lat < b.lat) return -1;
                return 0;
            }
        },
        WEST_TO_EAST: {
            code: "WEST_TO_EAST", name: "West to east",
            compareFunction: function (a, b) {
                if (a.lng > b.lng) return 1;
                if (a.lng < b.lng) return -1;
                //when lng are equal, sort SOUTH_TO_NORTH to make the sort stable
                if (a.lat > b.lat) return 1;
                if (a.lat < b.lat) return -1;
                return 0;
            }
        },
        NORTH_TO_SOUTH: {
            code: "NORTH_TO_SOUTH", name: "North to south",
            compareFunction: function (a, b) {
                if (a.lat > b.lat) return -1;
                if (a.lat < b.lat) return 1;
                //when lat are equal, sort WEST_TO_EAST to make the sort stable
                if (a.lng > b.lng) return 1;
                if (a.lng < b.lng) return -1;
                return 0;
            }
        },
        SOUTH_TO_NORTH: {
            code: "SOUTH_TO_NORTH", name: "South to north",
            compareFunction: function (a, b) {
                if (a.lat > b.lat) return 1;
                if (a.lat < b.lat) return -1;
                //when lat are equal, sort WEST_TO_EAST to make the sort stable
                if (a.lng > b.lng) return 1;
                if (a.lng < b.lng) return -1;
                return 0;
            }
        },
        PORTAL_NAME: {
            code: "PORTAL_NAME", name: "Portal name",
            compareFunction: function (a, b) {
                var comp = a.label.localeCompare(b.label, "en-GB"); //TODO:let user choose locale ?
                if (comp != 0) return comp;
                //if (a.label > b.label) return 1;
                //if (a.label < b.label) return -1;
                //when equal, use lat&lng to make the sort stable
                //WEST_TO_EAST
                if (a.lng > b.lng) return 1;
                if (a.lng < b.lng) return -1;
                //SOUTH_TO_NORTH
                if (a.lat > b.lat) return 1;
                if (a.lat < b.lat) return -1;
                return 0;
            }
        }
    };
    window.plugin.bookmarkUnderDraw.sortedSorts = [
        window.plugin.bookmarkUnderDraw.sorts.NONE,
        window.plugin.bookmarkUnderDraw.sorts.WEST_TO_EAST,
        window.plugin.bookmarkUnderDraw.sorts.EAST_TO_WEST,
        window.plugin.bookmarkUnderDraw.sorts.NORTH_TO_SOUTH,
        window.plugin.bookmarkUnderDraw.sorts.SOUTH_TO_NORTH,
        window.plugin.bookmarkUnderDraw.sorts.PORTAL_NAME
    ];
    //window.plugin.bookmarkUnderDraw.DEFAULT_SORT = window.plugin.bookmarkUnderDraw.sorts.NONE;
    window.plugin.bookmarkUnderDraw.DEFAULT_SORT_CODE = window.plugin.bookmarkUnderDraw.sorts.NONE.code;

    window.plugin.bookmarkUnderDraw.storage = {
        folderId: window.plugin.bookmarkUnderDraw.DEFAULT_FOLDER_ID,
        sortCode: window.plugin.bookmarkUnderDraw.DEFAULT_SORT_CODE,
        notLoadedPortalName: window.plugin.bookmarkUnderDraw.DEFAULT_NOT_LOADED_PORTAL_NAME,
        bookmarkUnknownPortal: window.plugin.bookmarkUnderDraw.DEFAULT_BOOKMARK_UNKNOWN_PORTAL,
        bookmarkHiddenPortal: window.plugin.bookmarkUnderDraw.DEFAULT_BOOKMARK_HIDDEN_PORTAL
    };

    //star icon
    //    window.plugin.bookmarkUnderDraw.ico = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30">'
    //	                                            + '<g style="fill: #FACA00; fill-opacity: 1; stroke: none;">'
    //                                                    + '<path d="M 15,1 18,12 29,12 20,18 24,28 15,21 6,28 10,18 1,12 12,12 Z" />'
    //	                                            + '</g>'
    //                                            + '</svg>';//utf-8 svg not working for reason unknown, using base64 instead
    window.plugin.bookmarkUnderDraw.bookmarkIcon = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCI+DQoJPGcgc3R5bGU9ImZpbGw6ICNGQUNBMDA7IGZpbGwtb3BhY2l0eTogMTsgc3Ryb2tlOiBub25lOyI+DQogICAgPHBhdGggZD0iTSAxNSwxIDE4LDEyIDI5LDEyIDIwLDE4IDI0LDI4IDE1LDIxIDYsMjggMTAsMTggMSwxMiAxMiwxMiBaIiAvPg0KCTwvZz4NCjwvc3ZnPg==";

    // STORAGE //////////////////////////////////////////////////////////
    // update the localStorage with preferences
    window.plugin.bookmarkUnderDraw.saveStorage = function () {
        localStorage[window.plugin.bookmarkUnderDraw.KEY_STORAGE] = JSON.stringify(window.plugin.bookmarkUnderDraw.storage);
    };

    // load the localStorage datas
    window.plugin.bookmarkUnderDraw.loadStorage = function () {
        if (typeof localStorage[window.plugin.bookmarkUnderDraw.KEY_STORAGE] != "undefined") {
            window.plugin.bookmarkUnderDraw.storage = JSON.parse(localStorage[window.plugin.bookmarkUnderDraw.KEY_STORAGE]);
        }

        //ensure default values are always set
        if (typeof window.plugin.bookmarkUnderDraw.storage.folderId == "undefined") {
            window.plugin.bookmarkUnderDraw.storage.folderId = window.plugin.bookmarkUnderDraw.DEFAULT_FOLDER_ID;
        }
        if (typeof window.plugin.bookmarkUnderDraw.storage.sortCode == "undefined") {
            window.plugin.bookmarkUnderDraw.storage.sortCode = window.plugin.bookmarkUnderDraw.DEFAULT_SORT_CODE;
        }
        if (typeof window.plugin.bookmarkUnderDraw.storage.notLoadedPortalName == "undefined") {
            window.plugin.bookmarkUnderDraw.storage.notLoadedPortalName = window.plugin.bookmarkUnderDraw.DEFAULT_NOT_LOADED_PORTAL_NAME;
        }
        if (typeof window.plugin.bookmarkUnderDraw.storage.bookmarkUnknownPortal == "undefined") {
            window.plugin.bookmarkUnderDraw.storage.bookmarkUnknownPortal = window.plugin.bookmarkUnderDraw.DEFAULT_BOOKMARK_UNKNOWN_PORTAL;
        }
        if (typeof window.plugin.bookmarkUnderDraw.storage.bookmarkHiddenPortal == "undefined") {
            window.plugin.bookmarkUnderDraw.storage.bookmarkHiddenPortal = window.plugin.bookmarkUnderDraw.DEFAULT_BOOKMARK_HIDDEN_PORTAL;
        }
    };

    // FUNCTIONS ////////////////////////////////////////////////////////
    /*
    pnpoly Copyright (c) 1970-2003, Wm. Randolph Franklin

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
    documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
    rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
    persons to whom the Software is furnished to do so, subject to the following conditions:

    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
    disclaimers.
    2. Redistributions in binary form must reproduce the above copyright notice in the documentation and/or other
    materials provided with the distribution.
    3. The name of W. Randolph Franklin may not be used to endorse or promote products derived from this Software without
    specific prior written permission.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
    OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
    window.plugin.bookmarkUnderDraw.pnpoly = function (latlngs, point) {
        var length = latlngs.length, c = false;

        for (var i = 0, j = length - 1; i < length; j = i++) {
            if (((latlngs[i].lat > point.lat) != (latlngs[j].lat > point.lat)) &&
                (point.lng < latlngs[i].lng + (latlngs[j].lng - latlngs[i].lng) * (point.lat - latlngs[i].lat) / (latlngs[j].lat - latlngs[i].lat))) {
                c = !c;
            }
        }

        return c;
    }

    window.plugin.bookmarkUnderDraw.circleToSearchCircle = function (drawnItem) {
        var circleCenter = drawnItem.getLatLng();
        var result = { type: 'circle', radius: drawnItem.getRadius(), center: new L.LatLng(circleCenter.lat, circleCenter.lng) };
        return result;
    };

    //drawnItem can be multipolygon or polygon
    window.plugin.bookmarkUnderDraw.multiPolygonToSearchPolygons = function (drawnItem) {
        var result = [];
        var polygonArr = [];
        if (drawnItem instanceof L.GeodesicPolygon) {
            //_latlngs contains the Polygon path used to approximate the GeodesicPolygon
            //we use this because the pnpoly algorithm is not suited for GeodesicPolygon and the approximation works better
            if (typeof drawnItem._latlngs != "undefined" && drawnItem._latlngs.length > 0) {
                if (typeof drawnItem._latlngs[0].lng == "number") {
                    polygonArr = drawnItem._latlngs.map(function (item) { return [item.lng, item.lat] });
                    polygonArr = [polygonArr]; //handle simple polygon like a multipolygon of one polygon only
                } else if (typeof drawnItem._latlngs[0][0].lng == "number") {
                    $.each(drawnItem._latlngs, function (i, latLngs) {//each latLngs is a polygon of a multipolygon
                        var innerPolygonArr = latLngs.map(function (item) { return [item.lng, item.lat] });
                        polygonArr.push(innerPolygonArr);
                    });
                }
            }
        }
        else {
            //console.log("Not a GeodesicPolygon");
            polygonArr = drawnItem.toGeoJSON().geometry.coordinates;
            if (polygonArr[0].length == 2 && typeof polygonArr[0][0] == "number") {
                //console.log("Not a MultiPolygon");
                polygonArr = [polygonArr]; //handle simple polygon like a multipolygon of one polygon only
            }
        }
        //console.log("polygonArr:"+polygonArr.length);
        $.each(polygonArr, function (i, polygonCoords) {//each polygonCoords is a polygon of a multipolygon
            var searchPolygon = {
                type: 'polygon',
                outerRing: [],
                holes: []
            };
            if (polygonCoords[0].length == 2 && typeof polygonCoords[0][0] == "number") {
                //polygon has no hole, we wrap it in an array
                polygonCoords = [polygonCoords];
            }
            //console.log(i+" polygonCoords:"+polygonCoords.length);
            $.each(polygonCoords, function (j, linearRing) {//in a polygon, the first set of coords is the outside bound, the others are holes
                var latLngArr = [];
                //console.log(j+" linearRing:"+linearRing.length);
                $.each(linearRing, function (k, latlng) {
                    var obj = { lng: latlng[0], lat: latlng[1] };
                    latLngArr.push(obj);
                    //console.log(k+" latLngArr:" + latLngArr.length);
                });
                if (j == 0) {
                    searchPolygon.outerRing = latLngArr;
                }
                else {
                    searchPolygon.holes.push(latLngArr);
                }
                //console.log("searchPolygon.outerRing:"+searchPolygon.outerRing.length);
                //console.log("searchPolygon.holes:"+searchPolygon.holes.length);
            });
            result.push(searchPolygon);
            //console.log("result:"+result.length);
        });
        return result;
    };

    window.plugin.bookmarkUnderDraw.doTheJob = function (options) {
        if (!window.plugin.bookmarks) {
            console.log('Bookmarks Under Draw ERROR : Bookmark plugin required');
            alert('Bookmarks plugin required');
            return false;
        }

        window.plugin.bookmarkUnderDraw.work.searchItems = [];
        if (!!window.plugin.drawTools && !!window.plugin.drawTools.drawnItems) {
            window.plugin.drawTools.drawnItems.eachLayer(function (drawnItem) {
                if (drawnItem instanceof L.GeodesicCircle) {//must be tested first because GeodesicCircle inherit from Polygon
                    var searchCircle = window.plugin.bookmarkUnderDraw.circleToSearchCircle(drawnItem);
                    window.plugin.bookmarkUnderDraw.work.searchItems.push(searchCircle);
                }
                else if (drawnItem instanceof L.GeodesicPolygon) {
                    var searchPolygons = window.plugin.bookmarkUnderDraw.multiPolygonToSearchPolygons(drawnItem);
                    //console.log("searchPolygons:"+searchPolygons.length);
                    $.each(searchPolygons, function (index, searchItem) {
                        window.plugin.bookmarkUnderDraw.work.searchItems.push(searchItem);
                    });
                }
                else if (drawnItem instanceof L.GeodesicPolyline || drawnItem instanceof L.Marker) {
                    //ignored, nothing to do
                }
                else {
                    //should not happen
                    console.log('Bookmark Under Draw : unknown drawn item type');
                }
            });
        }

        //if search, add it to job
        if (window.search.lastSearch &&
            window.search.lastSearch.selectedResult &&
            window.search.lastSearch.selectedResult.layer) {

            window.search.lastSearch.selectedResult.layer.eachLayer(function (drawnItem) {
                if (drawnItem instanceof L.Polygon || (typeof L.MultiPolygon == "function" && drawnItem instanceof L.MultiPolygon)) {
                    var searchPolygons = window.plugin.bookmarkUnderDraw.multiPolygonToSearchPolygons(drawnItem);
                    $.each(searchPolygons, function (index, searchItem) {
                        window.plugin.bookmarkUnderDraw.work.searchItems.push(searchItem);
                    });
                }
            });
        }

        if (window.plugin.bookmarkUnderDraw.work.searchItems.length == 0) {
            //warning icon
            var img = '<img style="vertical-align:middle;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAGE0lEQVR4nMWXbWxUWRnHf885985Mh2lnpm8zLeVlS3mp1qGgFigLUyltpcTZalsENST4icSYXXenbDZt4uqaGDG4H0gaNVk0uyvuayK4omY1K1kN8SVGDYlb1oSlVGophYUy05nOzD1+mBkKkgWKLJzkn5Obe+7z/93nec7cOWKM4UEO9UDdAetuHhoaHKo1xpwpLS21EonE7NTFqbbh4eET9w1AKXV0W/c2a8OGDYyPj7sOHjz4uojUGWOcecea7wPxeDwWrgl/fGHFAiYPfZqyEs3SJUtruru7H5tvrLsC8Lg9z2/fvp333/o2iCIzcpSdu3ayds3a/SIS+lAB4k/En2pqavInT5+g1n0FEUX2naNYuSTRaFTv3r37BRHRHxqAz+d7ZmtHO653XwZRoDQoReZPzxKNbmZ5w/IO4KH5xLzjJhyID7wZi8X0xJ9fowTDuasaARBQyXdZ/N5x+vr7GBsb+62IrDLGzNwzgKHBodpgMNgeaVpF5o0DfP2tJD97+51r9+vq6vjFosMs7/why5YtWxyNRvuAF+4k9h2VIJ1On4g9EpN//+YAyhKS6dmb1vjKpph57zB7vryHdevWfV9EgvcEIB6Px+rr6xeXWWnCzgjKY1jguXmdLgFP4nUCfi+RSMTb39//XRGR/xvA4/a8snPXTsxf9qM9oD1QEbBvBijcc8a+SW9vL5GPRfYAdbeLf8se2Dew78CmTZvcF08dp7I0g3KDduWbvziMMWAMyg2I4DIjJNOjdG/frkZHR4+KSIsxJvNBHh+YgaHBoVqfz/fo+vXrKD33E3TR3A1ocByH9MwM01cuk5qZATW3Lb3T3yMSaaKxsbG5vb29964ykEqlXuvq6tJXTh6musRBuUEVAMoDLhJXp0kmk2AMOSeXNxfJ/z5wicTkr9jx+R2Mj48Pi8gbxpird5yBocGhT4bD4fVrm5uonv0D2g3KLSiPoN1QVeUhmUhgWxZ+v59PNJWBMwVMgboCtsZvvUQoVElDQ0Owq6vrK/PKQCaT+XV/f79MvP0M1XIe0pchO4lk00jmEjK7Cq01Pp+P8vJyFoaA2eP5aE5BGlKTAb74pf2cPXv2WyJyyBgzeVuA+BPxp1asWBH0qgT25I8x3ixSkkNZWbTOoWyHRaEslmXh9XqpqKhgVeMl8M0ZY+VnnxpmOvc40WjUGj07+qKIdBtjcrcsQYm35Bv9O/p4/8hWlMtBuxyUO4d252cEFoWyiAgiglIKEcmbuv5Hbih1tfGpLW00LGvoBJbcMgMD8YE3Ozo77MmTR6j2p1Au5wYIFCDgURM8950l+Wt9iY0bz4AUohWlC1JjXJx6hf4d/YyNjf1SRFYbY1I3ARS2Xfvmh1uZer4e5XdQdkEuB9EmbyJQVZmgd9M/uei2qK7KkMteZ2wXAQSUDXioqn6UQHCMNWvWrGhra+sDXrypBLOzs7/r+WyP/OvIV1lQkr3R3HaumaPgzGUPnY+1saV3JRs/s5H/XPTljV1FAAtUGVAJVAEhksmn2fWFXbS0tPxARAI3AMTj8ViwPLi84aE6aqd/jrKcvOz8LGru7dFw8nQZExPnKS8vp6YmzB//Wps3tgHLBRIAKgrm1UAIv/9vaJ2idUOrt6enZ/AGANuyf7p3717GXvocYhnEcuZ0/dsXAFY3JggEAgQCfmpra1kTuVxIuw34gfICQOU1AAij9XN0dnXSvLr5cRFZCKCTieSB5ubmzUsrNd5Tz6ILHX+9is1XrHFZaYbGlWkqq+Ejjf/go5FzuBcoED8QLAAUFSwogG3nuHAhRP2yiGRmM1tij8QOWbZtf62jcyszr27Dqw1SkLLyovhBLdQ/3/nQ2nKe1ofPz2058QGlhQwErpnmr0uBEsCmpubvhMO7CYfDzUCDpbUW5aQ5PXEF7SnDTuXQiRx6Oov25JAihGKuzkW5illRQNl1hgsAT2FR8dNpCppm5coMLreLUCjUYWUymawvUG1tfvoU92skEkkAZ2JiwifGGJ7c9+R5y7KCxpjb/oO5FyOVTs0cO3bsRyMjI7+X4ulYRFzAQvLFul9H5gvyoI/n/wXy2/DuK2rGkQAAAABJRU5ErkJggg=="/>';
            alert(img + ' A polygon or circle must be drawn or a search result selected');
            return false;
        }
        else {
            console.log('Bookmark Under Draw :' + window.plugin.bookmarkUnderDraw.work.searchItems.length + ' shapes found');
        }

        var bookmarkUnderDraw = function (options) {
            if (typeof options == 'undefined') options = {};
            if (typeof options.folderId == 'undefined') options.folderId = window.plugin.bookmarkUnderDraw.DEFAULT_FOLDER_ID;
            if (typeof options.notLoadedPortalName == 'undefined') options.notLoadedPortalName = window.plugin.bookmarkUnderDraw.DEFAULT_NOT_LOADED_PORTAL_NAME;
            if (typeof options.bookmarkUnknownPortal == 'undefined') options.bookmarkUnknownPortal = window.plugin.bookmarkUnderDraw.DEFAULT_BOOKMARK_UNKNOWN_PORTAL;
            if (typeof options.bookmarkHiddenPortal == 'undefined') options.bookmarkHiddenPortal = window.plugin.bookmarkUnderDraw.DEFAULT_BOOKMARK_HIDDEN_PORTAL;
            if (typeof options.sort == 'undefined' || typeof options.sortCode == 'undefined') {
                options.sortCode = window.plugin.bookmarkUnderDraw.DEFAULT_SORT_CODE;
                options.sort = window.plugin.bookmarkUnderDraw.sorts[options.sortCode];
            }

            var t = this;
            this._w_ = {}; //work variables
            this.bookmarkedPortals = {}; //bookmarks guids
            this.distinctPortalUnderDrawCount = 0;
            this.bookmarkAddCount = 0;
            this.bookmarkRemoveCount = 0;
            this.wrongDataCount = 0;
            this.alreadyExistCount = 0;
            //this.foundTwiceCount = 0;//obsolete
            this.options = options;
            return t;
        };
        bookmarkUnderDraw.prototype.initialize = function (searchItems) {
            var t = this;
            //t._w_ = window.plugin.bookmarkUnderDraw.work;
            t._w_.searchItems = searchItems;
            t._w_.portalsUnderDraw = []; //guids of portals under draw
        };

        bookmarkUnderDraw.prototype.run = function () {
            var t = this;

            var input = window.portals;
            //when removing bookmarks, we can restrict ourselves to bookmarks
            if (t.options.action == window.plugin.bookmarkUnderDraw.actions.REMOVE) {
                input = {};
                var list = window.plugin.bookmarks.bkmrksObj['portals'];
                for (var idFolders in list) {
                    for (var idBkmrk in list[idFolders]['bkmrk']) {
                        var portalGuid = list[idFolders]['bkmrk'][idBkmrk]['guid'];
                        var portal = window.portals[portalGuid];
                        if (portal) {
                            input[portalGuid] = portal;
                        }
                    }
                }
            }
            console.log("portals:" + input.length);
            $.each(input, function (guid, portal) {
                var point = portal.getLatLng();
                var found = false;
                $.each(t._w_.searchItems, function (index, searchItem) {
                    switch (searchItem.type) {
                        case 'circle':
                            if (t.pointIsInCircle(point, searchItem)) {
                                found = true;
                                console.log("in circle:" + point.lat + "," + point.lng);
                                return false; //breaks the $.each
                            }
                            break;
                        case 'polygon':
                            if (t.pointIsInPolygon(point, searchItem)) {
                                found = true;
                                console.log("in polygon:" + point.lat + "," + point.lng);
                                return false; //breaks the $.each
                            }
                            break;
                        default:
                            console.log('Bookmark Under Draw ERROR : invalid draw type (' + searchItem.type + ')');
                            return true; //continue the $.each
                            break;
                    };
                });
                //console.log("found"+found);
                if (found) {
                    t._w_.portalsUnderDraw.push(guid);
                }
            });
            if (t.options.action == window.plugin.bookmarkUnderDraw.actions.ADD) {
                t.bookmarkPortals();
            }
            else if (t.options.action == window.plugin.bookmarkUnderDraw.actions.REMOVE) {
                t.removeBookmarks();
            }
            else {
                //should not happen
            }
            t.render();
        };

        bookmarkUnderDraw.prototype.pointIsInPolygon = function (point, searchItem) {
            var nodeIn = window.plugin.bookmarkUnderDraw.pnpoly(searchItem.outerRing, point);
            $.each(searchItem.holes, function (index, hole) {
                var inHole = window.plugin.bookmarkUnderDraw.pnpoly(hole, point);
                if (inHole) {
                    nodeIn = false; //portal is in the hole so not in the polygon
                    return false; //breaks the loop
                }
            });
            return nodeIn;
        };
        bookmarkUnderDraw.prototype.pointIsInCircle = function (point, searchItem) {
            //var t = this;
            var found = false;
            if (searchItem.center.distanceTo(point) <= searchItem.radius) {
                found = true;
            }
            return found;
        };


        bookmarkUnderDraw.prototype.removeBookmarks = function () {
            var t = this;

            $.each(t._w_.portalsUnderDraw, function (index, guid) {
                var list = window.plugin.bookmarks.bkmrksObj['portals'];
                var bkmrkData = window.plugin.bookmarks.findByGuid(guid);
                if (bkmrkData) {
                    //bookmark exists
                    t.bookmarkRemoveCount++;

                    delete list[bkmrkData['id_folder']]['bkmrk'][bkmrkData['id_bookmark']]; //inspired by window.plugin.bookmarks.switchStarPortal
                    //$('.bkmrk#' + bkmrkData['id_bookmark'] + '').remove();//handled by the refresh below
                }
                else {
                    //do nothing
                }
            });

            window.plugin.bookmarks.saveStorage();
            window.plugin.bookmarks.refreshBkmrks();
            window.runHooks('pluginBkmrksEdit', { "target": "all", "action": "import" });
            console.log('bookmarkUnderDraw: refreshed bookmarks');
        }
        //used instead of bookmark plugin because the original code makes only 99 different IDs in the same millisecond
        bookmarkUnderDraw.prototype.generateID = function () {
            var d = new Date();
            var ID = Math.floor(Math.random() * 1e10) + 1;
            //var ID = d.getTime()+(Math.floor(Math.random()*99)+1);//id1 472 936 881 241
            ID = 'id' + ID.toString();
            return ID;
        }
        bookmarkUnderDraw.prototype.bookmarkPortals = function () {
            var t = this;

            var folderBkmrks = window.plugin.bookmarks.bkmrksObj['portals'][t.options.folderId]['bkmrk'];
            var newBookmarks = [];
            $.each(t._w_.portalsUnderDraw, function (index, guid) {
                //if (typeof t.bookmarkedPortals[guid] == 'undefined') {//we check if portal was added by our loop, can happen if draws overlap//28/05/2018: cannot happen since we loop on portals only once
                var bkmrkData = window.plugin.bookmarks.findByGuid(guid);
                if (bkmrkData) {
                    //bookmark exists
                    t.alreadyExistCount++;
                }
                else {
                    var portal = window.portals[guid];
                    var portalName = portal.options.data.title;
                    if (typeof portalName == 'string' || t.options.bookmarkUnknownPortal) {//add portal only if name is loaded or override is chosen
                        if (t.options.bookmarkHiddenPortal || window.map.hasLayer(portal)) {//add portal only if it is not filtered out or override is chosen
                            var ll = portal.getLatLng();
                            //window.plugin.bookmarks.addPortalBookmark(guid, ll.lat + ',' + ll.lng, portalName); //actually bookmarks the portal
                            //02/09/2016: only add the bookmark to JS obj to make it faster
                            var bookmarkID = t.generateID();
                            // Add bookmark in the localStorage
                            var latlng = ll.lat + ',' + ll.lng;
                            var label = portalName || t.options.notLoadedPortalName;
                            var newBookmark = {
                                "guid": guid, "latlng": latlng, "label": label,
                                "id": bookmarkID, lat: ll.lat, lng: ll.lng //used for sort, will be deleted before adding to the bookmark plugin
                            };
                            newBookmarks.push(newBookmark);
                            //console.log('bookmarkUnderDraw: added portal ' + ID);
                            //window.runHooks('pluginBkmrksEdit', { "target": "portal", "action": "add", "id": ID, "guid": guid });//02/09/2016: only refresh once
                            t.bookmarkAddCount++;
                        }
                    }
                    else {
                        t.wrongDataCount++;
                    }
                }
                t.bookmarkedPortals[guid] = {}; //keep result for the count and the next checks
                t.distinctPortalUnderDrawCount++;
                //                }
                //                else {
                //                    //bookmark exists
                //                    t.foundTwiceCount++;
                //                }
            });
            //sort result if a sort is selected
            if (t.options.sort.code != window.plugin.bookmarkUnderDraw.sorts.NONE.code) {
                newBookmarks.sort(t.options.sort.compareFunction);
            }

            //create the bookmarks
            $.each(newBookmarks, function (index, newBookmark) {
                var bookmarkID = newBookmark.id;
                delete newBookmark.id;
                delete newBookmark.lat;
                delete newBookmark.lng;
                folderBkmrks[bookmarkID] = newBookmark;
            });

            //02/09/2016: only refresh once
            window.plugin.bookmarks.saveStorage();
            window.plugin.bookmarks.refreshBkmrks();
            window.runHooks('pluginBkmrksEdit', { "target": "all", "action": "import" });
            console.log('bookmarkUnderDraw: refreshed bookmarks');
        };

        bookmarkUnderDraw.prototype.setMessage = function (message, makeVisible) {
            if (makeVisible) {
                setTimeout(function () {
                    window.plugin.bookmarkUnderDraw.button.classList.add("active");
                }, 1);
            }
            setTimeout(function () {
                window.plugin.bookmarkUnderDraw.messageBox.innerHTML = message;
            }, 1);
        };
        bookmarkUnderDraw.prototype.render = function () {
            var t = this;

            var message = '';
            if (t.options.action == window.plugin.bookmarkUnderDraw.actions.ADD) {
                var bookmarkedPortalCount = t.bookmarkAddCount; // Object.keys(t.bookmarkedPortals).length;
                var totalPortalCount = t.distinctPortalUnderDrawCount;
                if (totalPortalCount > 0) {
                    if (totalPortalCount == 1)
                        message += totalPortalCount + ' portal found';
                    else
                        message += totalPortalCount + ' portals found';
                }
                else {
                    message += 'No portal found';
                }
                message += ', ' + bookmarkedPortalCount + ' new';
                if (t.alreadyExistCount > 0)
                    message += ', ' + t.alreadyExistCount + ' old';
                if (t.wrongDataCount > 0)
                    message += ', ' + t.wrongDataCount + ' not loaded';
            } else if (t.options.action == window.plugin.bookmarkUnderDraw.actions.REMOVE) {
                if (t.bookmarkRemoveCount > 0) {
                    if (t.bookmarkRemoveCount == 1)
                        message += t.bookmarkRemoveCount + ' bookmark removed';
                    else
                        message += t.bookmarkRemoveCount + ' bookmarks removed';
                }
                else {
                    message += 'No bookmark removed';
                }
            }


            var zoomLevel = $('#loadlevel').html();
            if (zoomLevel != 'all') {
                message += ' <span style="color:orange">Portals might be missing. Zoom level :<b>' + zoomLevel + '<b></span>';
            }

            t.setMessage(message, true); //setTimeout copied from layer-count, don't know why
        };

        //doTheJob
        //var options = window.plugin.bookmarkUnderDraw.storage;
        var worker = new bookmarkUnderDraw(options);
        window.plugin.bookmarkUnderDraw.work.lastWorker = worker; //debug
        worker.initialize(window.plugin.bookmarkUnderDraw.work.searchItems);
        worker.run();
    };

    window.plugin.bookmarkUnderDraw.messageBoxClicked = function (evt) {
        //console.log('window.plugin.bookmarkUnderDraw.messageBoxClicked');
        var btn = window.plugin.bookmarkUnderDraw.button;
        var messageBox = window.plugin.bookmarkUnderDraw.messageBox;
        btn.classList.remove("active");
        messageBox.textContent = "";
        evt.stopPropagation();
    };

    //inspired by window.plugin.drawTools.optReset
    window.plugin.bookmarkUnderDraw.resetDraw = function () {
        if (window.plugin.drawTools) {
            delete localStorage[window.plugin.bookmarkUnderDraw.DRAW_TOOLS_KEY_STORAGE];
            window.plugin.drawTools.drawnItems.clearLayers();
            window.plugin.drawTools.load();
            runHooks('pluginDrawTools', { event: 'clear' });
        }
    };
    //inspired by window.plugin.bookmarks.optReset
    window.plugin.bookmarkUnderDraw.resetBookmarks = function () {
        delete localStorage[window.plugin.bookmarks.KEY_STORAGE];
        window.plugin.bookmarks.createStorage();
        window.plugin.bookmarks.loadStorage();
        window.plugin.bookmarks.refreshBkmrks();
        window.runHooks('pluginBkmrksEdit', { "target": "all", "action": "reset" });
    };
    window.plugin.bookmarkUnderDraw.resetOpt = function () {
        window.plugin.bookmarkUnderDraw.storage.folderId = window.plugin.bookmarkUnderDraw.DEFAULT_FOLDER_ID;
        window.plugin.bookmarkUnderDraw.storage.sortCode = window.plugin.bookmarkUnderDraw.DEFAULT_SORT_CODE;
        window.plugin.bookmarkUnderDraw.storage.notLoadedPortalName = window.plugin.bookmarkUnderDraw.DEFAULT_NOT_LOADED_PORTAL_NAME;
        window.plugin.bookmarkUnderDraw.storage.bookmarkUnknownPortal = window.plugin.bookmarkUnderDraw.DEFAULT_BOOKMARK_UNKNOWN_PORTAL;
        window.plugin.bookmarkUnderDraw.storage.bookmarkHiddenPortal = window.plugin.bookmarkUnderDraw.DEFAULT_BOOKMARK_HIDDEN_PORTAL;

        window.plugin.bookmarkUnderDraw.saveStorage();
        window.plugin.bookmarkUnderDraw.openOptDialog();
    }

    window.plugin.bookmarkUnderDraw.saveOpt = function () {
        var options = {};
        options.folderId = $('#bookmarkUnderDraw-folderId').val();
        options.notLoadedPortalName = $('#bookmarkUnderDraw-notLoadedPortalName').val();
        options.bookmarkUnknownPortal = $('#bookmarkUnderDraw-bookmarkUnknownPortal').is(":checked");
        options.bookmarkHiddenPortal = $('#bookmarkUnderDraw-bookmarkHiddenPortal').is(":checked");
        options.sortCode = $('#bookmarkUnderDraw-sortCode').val();
        var sort = window.plugin.bookmarkUnderDraw.sorts[options.sortCode];
        if (typeof sort == 'undefined') {
            options.sortCode = window.plugin.bookmarkUnderDraw.DEFAULT_SORT_CODE;
            sort = window.plugin.bookmarkUnderDraw.sorts[options.sortCode];
        }
        options.sort = sort;

        window.plugin.bookmarkUnderDraw.storage.folderId = options.folderId;
        window.plugin.bookmarkUnderDraw.storage.sortCode = options.sortCode;
        window.plugin.bookmarkUnderDraw.storage.notLoadedPortalName = options.notLoadedPortalName;
        window.plugin.bookmarkUnderDraw.storage.bookmarkUnknownPortal = options.bookmarkUnknownPortal;
        window.plugin.bookmarkUnderDraw.storage.bookmarkHiddenPortal = options.bookmarkHiddenPortal;
        window.plugin.bookmarkUnderDraw.saveStorage();

        return options;
    }
    window.plugin.bookmarkUnderDraw.saveOptAndRemoveBookmarks = function () {
        var options = window.plugin.bookmarkUnderDraw.saveOpt();
        options.action = window.plugin.bookmarkUnderDraw.actions.REMOVE;

        window.plugin.bookmarkUnderDraw.doTheJob(options);
    }
    window.plugin.bookmarkUnderDraw.saveOptAndBookmarkPortals = function () {
        var options = window.plugin.bookmarkUnderDraw.saveOpt();
        options.action = window.plugin.bookmarkUnderDraw.actions.ADD;

        window.plugin.bookmarkUnderDraw.doTheJob(options);
    }

    window.plugin.bookmarkUnderDraw.openOptDialog = function () {
        var html =
            '<div>' +
            '<table>';
        html +=
            '<tr>' +
            '<td>' +
            'Send alert even if portal name is not loaded' +
            '</td>' +
            '<td>' +
            '<input id="bookmarkUnderDraw-bookmarkUnknownPortal" type="checkbox" ' +
            (window.plugin.bookmarkUnderDraw.storage.bookmarkUnknownPortal ? 'checked="checked" ' : '') +
            '/>' +
            '</td>' +
            '</tr>';
        html +=
            '<tr>' +
            '<td>' +
            'Send alert even if portal is hidden' +
            '</td>' +
            '<td>' +
            '<input id="bookmarkUnderDraw-bookmarkHiddenPortal" type="checkbox" ' +
            (window.plugin.bookmarkUnderDraw.storage.bookmarkHiddenPortal ? 'checked="checked" ' : '') +
            '/>' +
            '</td>' +
            '</tr>';
        html +=
            '<tr>' +
            '<td>' +
            'Folder :' +
            '</td>' +
            '<td>';
        html += '<select id="bookmarkUnderDraw-folderId">';
        //loop on bookmark folders
        for (folderId in window.plugin.bookmarks.bkmrksObj['portals']) {
            var folder = window.plugin.bookmarks.bkmrksObj['portals'][folderId];
            var folderName = folderId == window.plugin.bookmarks.KEY_OTHER_BKMRK ? 'Root' : folder.label;
            html += '<option value="' + folderId +
                '" ' + (window.plugin.bookmarkUnderDraw.storage.folderId == folderId ? 'selected="selected"' : '') +
                '>' + folderName + '</option>';
        }
        html += '</select>';
        html += '</td>' +
            '</tr>';
        html +=
            '<tr>' +
            '<td>' +
            'Sort :' +
            '</td>' +
            '<td>';
        html += '<select id="bookmarkUnderDraw-sortCode">';
        for (var i = 0; i < window.plugin.bookmarkUnderDraw.sortedSorts.length; i++) {
            var sort = window.plugin.bookmarkUnderDraw.sortedSorts[i];
            html += '<option value="' + sort.code +
                '" ' + (window.plugin.bookmarkUnderDraw.storage.sortCode == sort.code ? 'selected="selected"' : '') +
                '>' + sort.name + '</option>';
        }
        html += '</select>';
        html += '</td>' +
            '</tr>';
        html +=
            '<tr>' +
            '<td>' +
            'Missing portal name :' +
            '</td>' +
            '<td>' +
            '<input id="bookmarkUnderDraw-notLoadedPortalName" type="text" ' +
            'value="' + window.plugin.bookmarkUnderDraw.storage.notLoadedPortalName + '" />' +
            '</td>' +
            '</tr>';

        html +=
            '</table>' +
            '</div>'
            ;
        var d = dialog({
            html: html,
            id: 'bookmarkUnderDraw_opt',
            title: 'Bookmark under draw',
            width: 'auto',
            buttons: [
                {
                    text: 'Reset options',
                    title: 'Reset Bookmark under draw preferences (this dialog)',
                    click: function () {
                        window.plugin.bookmarkUnderDraw.resetOpt();
                    }
                },
                {
                    text: 'Clear drawn items',
                    click: function () {
                        window.plugin.bookmarkUnderDraw.resetDraw();
                    }
                },
                {
                    text: 'Clear all bookmarks',
                    click: function () {
                        window.plugin.bookmarkUnderDraw.resetBookmarks();
                    }
                },
                {
                    text: 'Remove under draw',
                    title: 'Remove bookmarks found under draw or search result',
                    click: function () {
                        window.plugin.bookmarkUnderDraw.saveOptAndRemoveBookmarks();
                    }
                },
                {
                    text: 'Add under draw',
                    title: 'Add bookmarks found under draw or search result',
                    click: function () {
                        window.plugin.bookmarkUnderDraw.saveOptAndBookmarkPortals();
                    }
                }
            ]
        });
        var dialogId = d.data('id'); //dialog-bookmarkUnderDraw_opt
        $("#" + dialogId + "").parent().find(".ui-dialog-buttonpane .ui-button .ui-button-text:contains('OK')").parent().hide(); //remove the default OK button
    }

    window.plugin.bookmarkUnderDraw.optClicked = function () {
        window.plugin.bookmarkUnderDraw.openOptDialog();
    };

    // init setup
    window.plugin.bookmarkUnderDraw.setup = function () {
        console.log('Bookmarks Under Draw loading.');
        //        if (!window.plugin.bookmarks) {
        //            console.log('ERROR : Bookmarks plugin required');
        //            return false;
        //        }
        //        if (!window.plugin.drawTools) {
        //            console.log('ERROR : Draw tools plugin required');
        //            return false;
        //        }
        if (window.plugin.bookmarks) {
            window.plugin.bookmarkUnderDraw.DEFAULT_FOLDER_ID = window.plugin.bookmarks.KEY_OTHER_BKMRK;
        }

        window.plugin.bookmarkUnderDraw.loadStorage();

        window.plugin.bookmarkUnderDraw.addButtons();
        console.log('Bookmarks Under Draw loaded.');
    };

    // toolbox menu
    window.plugin.bookmarkUnderDraw.addButtons = function () {
        var css = '.leaflet-control-bookmark-under-draw-bookmark{background-image:url(' + window.plugin.bookmarkUnderDraw.bookmarkIcon + ')!important; background-repeat:no-repeat;}'
            + '.leaflet-control-bookmark-under-draw-messageBox{background-color: rgba(255, 255, 255, 0.6); display: none; height: 24px; left: 30px; line-height: 24px; margin-left: 15px; margin-top: -12px; padding: 0 10px; position: absolute; top: 50%; white-space: nowrap; width: auto; }'
            + '.leaflet-control-bookmark-under-draw a.active .leaflet-control-bookmark-under-draw-messageBox{ display: block;} '
            + '.leaflet-control-bookmark-under-draw-messageBox:before { border-color: transparent rgba(255, 255, 255, 0.6); border-style: solid; border-width: 12px 12px 12px 0; content: ""; display: block; height: 0; left: -12px; position: absolute; width: 0; } ';

        $('head').append('<style>' + css + '</style>');

        var leafletLeft = $(".leaflet-top.leaflet-left", window.map.getContainer());

        var container = document.createElement("div");
        container.className = "leaflet-control-bookmark-under-draw leaflet-bar leaflet-control";
        leafletLeft.append(container);

        var button = document.createElement("a");
        button.className = "leaflet-bar-part leaflet-control-bookmark-under-draw-bookmark";
        //button.addEventListener("click", window.plugin.bookmarkUnderDraw.doTheJob, false);
        button.addEventListener("click", window.plugin.bookmarkUnderDraw.optClicked, false);
        button.title = 'Bookmark portals';
        container.appendChild(button);

        var messageBox = document.createElement("div");
        messageBox.className = "leaflet-control-bookmark-under-draw-messageBox";
        messageBox.addEventListener("click", window.plugin.bookmarkUnderDraw.messageBoxClicked, false);
        button.appendChild(messageBox);

        plugin.bookmarkUnderDraw.button = button;
        plugin.bookmarkUnderDraw.messageBox = messageBox;
        plugin.bookmarkUnderDraw.container = container;

        //0.1.10: removed options menu, replaced by dialog before bookmarking
        //add options menu
        //$('#toolbox').append('<a onclick="window.plugin.bookmarkUnderDraw.optClicked();return false;">Bookmark under draw Opt</a>');
    };

    // runrun
    var setup = window.plugin.bookmarkUnderDraw.setup;

    setup.info = plugin_info; //add the script info data to the function as a property
    if (!window.bootPlugins) window.bootPlugins = [];
    window.bootPlugins.push(setup);
    // if IITC has already booted, immediately run the 'setup' function
    if (window.iitcLoaded && typeof setup === 'function') {
        setup();
    }

    // PLUGIN END ////////////////////////////////////////////////////////    
} // WRAPPER END ////////////////////////////////////////////////////////    

var script = document.createElement('script');
var info = {};
if (typeof GM_info !== 'undefined' && GM_info && GM_info.script) info.script = { version: GM_info.script.version, name: GM_info.script.name, description: GM_info.script.description };
script.appendChild(document.createTextNode('(' + wrapper + ')(' + JSON.stringify(info) + ');'));
(document.body || document.head || document.documentElement).appendChild(script);