Restriction Manager

Save, and load, restrictions from local storage.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

/* global $ */
/* global I18n */
/* global W */

// ==UserScript==
// @name        Restriction Manager
// @version     1.6
// @description Save, and load, restrictions from local storage.
// @namespace   mailto:[email protected]
// @include     https://www.waze.com/editor
// @include     https://www.waze.com/editor*
// @include     https://www.waze.com/*/editor*
// @include     https://beta.waze.com/*
// @exclude     https://www.waze.com/user/*
// @exclude     https://www.waze.com/*/user/*
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAjCAIAAABzZz93AAAABnRSTlMA/wD/AP83WBt9AAAACXBIWXMAAA7EAAAOxAGVKw4bAAABV0lEQVRIiWP8//8/A70ACxl6vm6JgjC4fZaRpJGRVJ/BbYID4q1kotAmkgBpllHoAhIso9BbJFiG3yYi3UFyarx39rRewx0IO6nBZ5IxL/F6ifIZisOffoAzrzzFrub/v7/kW0YqYGRiJtMy4tMFQZUELKM8BZJgGSZQkhbAI4vfcfgsI+gtHWksSRGPLpokEJItoyS2cOnFbhl10wUByygHWJ2LxTIC3pLiMyPXBeiWkRKAIupS+KQxjaJtakSzjwmPHA7AqwNlCKiSaDdKG4RGiRDeSEH4jEY2IQMmkm16fjOvdDmP73Ie391TnxOlA244yQnk3ulH825AmG/K1z0jSS8TA4kBeOfxG5IsgACIFST7TEVWhAzLIID0+sxULkkDwhTpDMKbqzEA4////+mQDhkYGLh9ljExkN4/IM8mhoGpPGnqObjh6F0m6sYfmh9I7p9RAgAFyXyizju5WQAAAABJRU5ErkJggg==
// @resource    jqUI_CSS  https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css
// @grant       none
// @copyright   2018, kjg53
// @author      kjg53
// @license     MIT
// ==/UserScript==


(function() {
    var initialized = false;
    var lsPrefix = "rtmgr:";

    // Map the css class used to identify the three restriction blocks to the direction constants used in the data.
    var classToDirection={"forward-restrictions-summary": "FWD",
                          "reverse-restrictions-summary": "REV",
                          "bidi-restrictions-summary": "BOTH"};

    // Convert the segment's default type to the driving modality that implied the type.  Creating a Toll Free restriction
    // implies that the segment is otherwise (i.e. defaults) to tolled which is what is then stored in the model.
    // Finding the tolled default thereby implies that the current restriction is specifying a toll free rule.
    var defaultType2drivingModality = {"TOLL": "DRIVING_TOLL_FREE",
                           "FREE":"DRIVING_BLOCKED",
                           "BLOCKED":"DRIVING_ALLOWED"};

    // Map the single bit constants used in the weekdays property to the integer numbers encoded in each week days HTML display.
    var weekdayBit2Idx = {
        1:1,
        2:2,
        4:3,
        8:4,
        16:5,
        32:6,
        64:0
    };

    var clipboardTarget = "*Clipboard*";

    // Get a sorted list of saved restrictions found in local storage.
    function allSavedRestrictions() {
        var all = [];
        for(var i = 0; i < localStorage.length; i++) {
            var key = localStorage.key(i);

            if (key.indexOf(lsPrefix) == 0) {
                key = key.substring(lsPrefix.length);

                all.push(key);
            }
        }
        all.sort();

        return all;
    }

    function extractRestrictions() {
        var extracted = {};
        for(var i = 0; i < localStorage.length; i++) {
            var key = localStorage.key(i);

            if (key.indexOf(lsPrefix) == 0) {
                var name = key.substring(lsPrefix.length);

                extracted[name] = localStorage.getItem(key);
            }
        }

        return extracted;
    }

    // Convert list of saved restrictions into a string of HTML option elements.
    function allSavedRestrictionAsOptions() {
        var all = allSavedRestrictions();
        return all.length == 0 ? "" : "<option selected></option><option>" + all.join("</option><option>") + "</option>";
    }

    // Update all restriction selectors to display the saved restrictions returned by allSavedRestrictions
    function updateSavedRestrictionSelectors(root) {
        $("div.rtmgr div.name select", root).html(allSavedRestrictionAsOptions()).each(resizeDivName);
    }

    // The content of the div.name element are positioned relative to its location.  As a result, the
    // div normally collapses to a point in the screen layout.  This function expands the div to enclose
    // its contents such that other elements are laid out around them.
    function resizeDivName(idx, child) {
        var div = $(child).parents("div.name").first();
        var height = 0;
        var width = 0;

        div.children().each(function(idx, child) {
            child = $(child);
            height = Math.max(height, child.height());
            width = Math.max(width, child.width());
        });

        div.width(width).height(height);
    }


    // Identify the direction of the restrictions associated with the specified button.
    function direction(btn) {
        var classes = btn.parents("div.restriction-summary-group").attr('class').split(' ');

        while(classes.length) {
            var cls = classes.pop();
            var dir = classToDirection[cls];

            if (dir) {
                return dir;
            }
        }
    }


    function setValue(selector, model, value) {
        if (value != null) {
            var sel = $(selector, model);
            var oldValue = sel.val();
            if (oldValue != value) {
                sel.val(value);
                sel.change();
            }
        }
    }

    function setCheck(selector, model, value) {
        if (value != null) {
            value = !!value;
            var sel = $(selector, model);
            var oldValue = sel.prop('checked');
            if (oldValue != value) {
                sel.prop('checked', value);
                sel.change();
            }
        }
    }

    function setSelector(name, model, value) {
        setValue('select[name="' + name + '"]', model, value);
    }

    function lastFaPlus(modal) {
        return $("i.fa-plus", modal).last();
    }

    function clearMessages() {
        $("div.modal-header-messages div.rtmgr").remove();
    }
    function addMessage(text, icon, color) {
        var rvr = $("div.modal-header-messages");
        if (icon) {
            icon = '<i class="fa fa-' + icon + '"/> ';
        } else {
            icon = "";
        }
        if (color) {
            color = ' style="color: ' + color + '"';
        } else {
            color = "";
        }
        rvr.append('<div class="modal-header-message"' + color + '>' + icon + text + '</div>');
    }

    var timeRegexp = /(\d\d?):(\d\d?)/

    function time2Int(time, ifNull) {
        var m = timeRegexp.exec(time);

        return m == null ? -1 : (m[1] * 60) + m[2];
    }

    function compareTimeFrames(a, b) {
        if (a == null) {
            return (b == null ? 0 : -1);
        } else if (b == null) {
            return 1;
        } else {
            a = a[0];
            b = b[0];

            var c = time2Int(a.fromTime, -1) - time2Int(b.fromTime, -1);
            if (c == 0) {
                c = time2Int(a.toTime, 1440) - time2Int(b.toTime, 1440);
            }

            return c;
        }
    }

    function compareRestrictions(a, b) {
        var c = compareTimeFrames(a.timeFrames, b.timeFrames);
        if (c == 0) {
            c = a.defaultType.localeCompare(b.defaultType);
        }
        return c;
    }

    function handleImportDragOver(evt) {
        evt.stopPropagation();
        evt.preventDefault();
        evt.dataTransfer.dropEffect = "copy";
    }

    function handleImportFile(file) {
        var reader = new FileReader();

        reader.onload = function(e) {
            var restrictions = JSON.parse(e.target.result);

            for(var restriction in restrictions) {
                localStorage.setItem(lsPrefix + restriction, restrictions[restriction]);
            }
        };

        reader.readAsText(file);
    }

    function handleImportDrop(evt) {
        evt.stopPropagation();
        evt.preventDefault();

        var i, f, item, seen = {};
        for(i = 0; item = evt.dataTransfer.items[i]; i++) {
            if (item.kind === 'file') {
                f = item.getAsFile();
                handleImportFile(f);
                seen[f.name] = true;
            }
        }
        for(i = 0; f = evt.dataTransfer.files[i]; i++) {
            if (seen[f.name] !== true) {
                handleImportFile(f);
            }
        }
    }

    function initializeRestrictionManager() {
        if (initialized) {
            return;
        }

        var observerTarget = document.getElementById("dialog-region");

        if (!observerTarget) {
            window.console.log("Restriction Manager: waiting for WME...");
            setTimeout(initializeRestrictionManager, 1015);
        }

        // Inject my stylesheet into the head
        var sheet = $('head').append('<style type="text/css"/>').children('style').last();
        sheet.append('div.rtmgr-column {display: flex; flex-direction: column; align-items: center}');
        sheet.append('div.rtmgr-row {display: flex; flex-direction: row; justify-content: space-around}');
        sheet.append('div.rtmgr button.btn {margin-top: 5px; border-radius: 40%}');
        sheet.append('div.rtmgr div.name input {width: 250px; position: absolute; left: 0px; top: 0px; z-index: 1}');
        sheet.append('div.rtmgr div.name select {width: 275px; position: absolute; left: 0px; top: 0px}');
        sheet.append('div.rtmgr div.name {width: 275px; position: relative; left: 0px; top: 0px}');
        sheet.append('div.modal-dialog div.modal-header .modal-title img.icon {float:right;height:20px;width:20px}');
        sheet.append('dialog.rtmgr span.cmd {text-decoration: underline}');
        sheet.append('dialog.rtmgr .import, dialog.rtmgr .export {width:30px; height: 30px}');

        // create an observer instance
        var observer = new MutationObserver(function(mutations) {
            var si = W.selectionManager.getSelectedFeatures();

            mutations.forEach(function(mutation) {
                if("childList" == mutation.type && mutation.addedNodes.length) {
                    var restrictionsModal = $("div.modal-dialog.restrictions-modal", observerTarget);

                    if (restrictionsModal) {
                        var modalTitle = $(restrictionsModal).find("div.modal-header .modal-title").first();
                        var title = modalTitle.text().trim();

                        if (I18n.translations[I18n.locale].restrictions.modal_headers.restriction_summary == title) {
                            if (modalTitle.data('rtmgr') === undefined) {
                                // Flag this modal as having already augmented
                                modalTitle.data('rtmgr', true);
                                modalTitle.append("<img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAjCAIAAABzZz93AAAABnRSTlMA/wD/AP83WBt9AAAACXBIWXMAAA7EAAAOxAGVKw4bAAABV0lEQVRIiWP8//8/A70ACxl6vm6JgjC4fZaRpJGRVJ/BbYID4q1kotAmkgBpllHoAhIso9BbJFiG3yYi3UFyarx39rRewx0IO6nBZ5IxL/F6ifIZisOffoAzrzzFrub/v7/kW0YqYGRiJtMy4tMFQZUELKM8BZJgGSZQkhbAI4vfcfgsI+gtHWksSRGPLpokEJItoyS2cOnFbhl10wUByygHWJ2LxTIC3pLiMyPXBeiWkRKAIupS+KQxjaJtakSzjwmPHA7AqwNlCKiSaDdKG4RGiRDeSEH4jEY2IQMmkm16fjOvdDmP73Ie391TnxOlA244yQnk3ulH825AmG/K1z0jSS8TA4kBeOfxG5IsgACIFST7TEVWhAzLIID0+sxULkkDwhTpDMKbqzEA4////+mQDhkYGLh9ljExkN4/IM8mhoGpPGnqObjh6F0m6sYfmh9I7p9RAgAFyXyizju5WQAAAABJRU5ErkJggg==' class='icon'>"
                                                  + "<dialog class='rtmgr'>"
                                                  + "<p style='text-align: center; font-weight: bold; font-size: 1.3em'>Restriction Manager</p>"
                                                  + "<p style='font-style: italic; font-size: .7em'>Stores restrictions in your browser's local storage so that they may be easily applied to other segments.</p>"
                                                  + "<p style='padding-left: 3em; text-indent: -3em;'>"
                                                  + "<span class='cmd'>Save</span>: Saves these restrictions to the selected key.<br>"
                                                  + "<span style='font-style: italic; font-size: .7em'>Note: Newly edited restrictions must be applied to the segment before the manager can save them.</span>"
                                                  + "</p>"
                                                  + "<p>"
                                                  + "<span class='cmd'>Apply</span>: Replaces the current restrictions with the restrictions associated with the selected key.</p>"
                                                  + "<p>"
                                                  + "<span class='cmd'>Delete</span>: Delete the selected key from your browser's local storage.</p>"
                                                  + "</dialog>");
                                if (window.File && window.FileReader && window.FileList && window.Blob) {
                                    $('dialog.rtmgr', modalTitle)
                                        .append("<p>Offline Storage: "
                                                + "<a download='restrictions.txt'><img title='Click here to export all saved restrictions' src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEwAACxMBAJqcGAAABBVJREFUeJzt3btqVFEUh/FPDXiLJIhooYENKUQrqxQitrYiGgufQfENBBG08AnEWkGwsUojFhbeQFBUsBNBRBGJsZBg4lgMBzRkkrPO7NvM+v/gdJl9lrM+MqOFB/zZB9wA3gO/gSXgITBfcijJYw74AvQGXPeB7cWmk6QOAt8YvPzmul1qQEnrFpsvv7mOFZpREtkKLNI+gOtlxsxva+kBMjkATBl+/kiqQWrjJQDrF7sdSaaokJcAZAAF4JwCcE4BOKcAnFMAzikA5xSAcwrAOQXgnAJwTgE4pwCcUwDOKQDnFIBzCsA5BeCcAnBOATinAJxTAM4pAOcUgHMKwDkF4JwCcE4BOKcAnFMAzikA5xSAcwrAOQXgnAJwTgE4pwCcUwDOKQDnFIBzCsA5BeCcAnBOATg3kfj8WWAG+Am8AZYT328c7ALOAseBSeAj8AB4WnIoqzPAO/7/P/iXgJvA7gLzBNo/K6AHLBSYEeAU8HnATA+AvYXmMrnKxm/ua2B/5pnCJjPVEMA5+g+x2miul8DOArO1dpp2b/Bb8kYQWs5VKoA2y2+ua5lnM3lF+zc5ZwTBMFfuACzL79F/8FWVX94PYXuTc0YQjHPlCsC6/OY6HGuAmCXNdHjNUeAR+b8T1OAccIdufxObjDVEzAB+dHydxwiGWT7Ah3ijxLMN+Ir911mOj4NgnCXlR0DXX/vN9TjhbEO7Qvc/WMoIgnGOVAHMM9zyV4GTiWaLYjvwhPoiCMYZUgQw7PJ7wMUEc0U3DbygrgiC8f6xA4ix/MuRZ0qqtgiC8d4xA3C3/EZNEQTjfWMF4Hb5jVoiCMZ7xgjA/fIbNUQQjPcbNgAtf43SEQTjvYYJQMsfoGQEwXifrgHMAyvGe7lYfqNUBMF4jy4BaPktlYggGM+3BqDlG00Dz8kXQTCebQlAy+8oZwTBeG7bALT8IeWKIBjPbBOAlh9JjgiC8bzNAtDyI0sdQTCetVEA59Hyk0gZQTCeMygALT+xVBEE4xnrBaDlZ5IigmB8/doAtPzMYkcQjK/9NwAtv5CYEQTj65oAtPzCpogTwZzxNQto+dWIEcEn489/QcuvSowIcl5afgKjEoGWn1DtEWj5GdQagZafUW0RaPkF1BKBll9Q6Qi0/AqUikDLr8gU8Awt37VcEWj5FUsdgZY/AlJFoOWPkNgRaPkjKFYEWv4IGzYCLX8MdI1Ayx8j1gi0/DE0Rf+hCxst/g9wqdSAkt4e4B7rL38RuFButDK2lB6gkDn6TzWZBX7R/3i4C3wvOZSIiIiIiIiIiIhIGjH/KfgElT/RakwsAztiHVblI0glHwXgnAJwTgE4pwCcUwDOKQDnFIBzCsA5BeCcAnBOATinAJybiHhWD1iNeJ6sbyXmYX8BhFf7gV3QQHIAAAAASUVORK5CYII=' class='export' download='restrictions.txt'></a>"
                                                + "<img title='Drag file here to import restrictions' src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEwAACxMBAJqcGAAAA61JREFUeJzt3U+LjWEcxvHvDEJKhIVslNlYSZGsiKJmUPZ2lpaaF8DGglfA5uQNKOIVyChRLCkLf2qyUEaTGSkWx6TJzJnzPM9937/7nOv61NmdGb9xfZmaOmcgrSngtx9ZH1eGXmMIkyk/mY0eByDOAYhzAOIcgDgHIM4BiHMA4hyAOAcgzgGIcwDiHIC4zYk/3yLwOPHntNU+RR9gZmZmZmZmZmYjaFP0AYEmgIPAfvo/wfwVeo0Vsx24Cczz78UWP4EHwOHAu6yAvcAr1n/VzSIwHXadZbUPeMPGL71aAmaCbrRMhh3fEYyhpuM7gjHSdnxHMAa6ju8IRliq8R3BCEo9viMYIbnGdwQjIPf4jqBipcZfeSzjCKpRenxHUJGo8R1BBaLHdwSBahnfEQSobXxHUFCt4zuCAmof3xFklGL8hczPdwSZpBj/NtBr+DEn6R7BhfR/HVpSjQ/NAwBHECrl+NAuAHAEIVKPD+0DAEdQVI7xoVsA4AiKyDU+dA8AHEFWOceHNAGAI8gi9/iQLgBwBEmVGB/SBgCOIIlS40P6AMARdFJyfMgTAKSJ4GKDP28slB4f8gUAjqCRiPEhbwDgCIYSNT7kDwAcwUCR40OZAMARrCl6fCgXADiCVWoYH8oGAI4AqGd8KB8AiEdQ0/gQEwCIRlDb+BAXAIhFsBN4TV3jQ2wA0D2CJeBYhruSu0t940N8ANA9grlMdyWzh/67btY2PtQRAHSPYCrjbZ3NUOf4UE8A0C2CyykPSf17A3e3/Lg7wGzKQyo3B5wHvrf42NxxdnKa+v7lr+g1vKuENv8THCp0Wytbga/UNz7UGQA0i+Bpwbtam6W+8aHeAGC4CH4ARwvf1cokcJ/BX8ytgLt6G9wUGQDAceDDOrd8Ac4G3NTJVeAtq7+Ql8CloHt61B0AwA7gGvAQeAE8Aa4Du4LuSeIAcIT+j4cj9ag/gOJS//bwtXz++7AKpf45gI0YByDOAYhzAOIcgDgHIM4BiHMA4hyAOAcgzgGIcwDiHIA4ByDOAYhzAOIcgDgHIM4BiHMA4hyAOAcgzgGIcwDiHIA4ByDOAYhzAOIcgDgHIM4BiHMA4hyAOAcgzgGIcwDiHIA4ByDOAYhzAOIcgDgHIE4pAJn3/21CKYCFBs/9lu2KyigF8LzBc59lu8LCbAM+MtxbxU8H3WiZnaH/a1gHjX8v7Dor4hTwnv+HXwZuoPVtkYnoA4JsAc4BJ+h/a3gHPALmI48yMyvrD75JO5BlUd2tAAAAAElFTkSuQmCC' class='import'>"
                                                + "</p>");
                                }
                                $('dialog.rtmgr', modalTitle).append("<p style='text-align: right; font-style: italic; font-size: .5em'>Click anywhere to close.</p>");
                                $("img.icon", modalTitle).click(function(evt) {
                                    $("dialog.rtmgr", modalTitle)[0].showModal();
                                });
                                $("dialog.rtmgr", modalTitle).click(function(evt) {
                                    evt.currentTarget.close();
                                });
                                if (window.File && window.FileReader && window.FileList && window.Blob) {
                                    $("img.export", modalTitle).click(function(evt) {
                                        var data = JSON.stringify(extractRestrictions());
                                        evt.target.parentNode.href = "data:text/plain;base64," + btoa(data);
                                    });

                                    var imgImport = $("img.import", modalTitle)[0];

                                    imgImport.addEventListener('dragover', handleImportDragOver, false);
                                    imgImport.addEventListener('drop', handleImportDrop, false);
                                }

                                // Add the UI elements to the modal
                                // Original, single, jquery statement split up after it stopped working.
                                var restrictionSummaryGroups = $("div.restriction-summary-group div.restriction-summary-title", restrictionsModal);
                                restrictionSummaryGroups.before ("<div class='rtmgr rtmgr-column'>"
                                            +   "<div class='name'>"
                                            +     "<input type='text'/>"
                                            +     "<select/>"
                                            +   "</div>"
                                            + "</div>");
                                var rtmgrColumns = restrictionSummaryGroups.parent().children("div.rtmgr-column");
                                rtmgrColumns.append ("<div class='rtmgr-row'>"
                                            +     "<button class='btn save'>Save</button>"
                                            +     "<button class='btn apply'>Apply</button>"
                                            +     "<button class='btn delete'>Delete</button>"
                                            + "</div>");

                                // Initialize the saved restriction selectors
                                updateSavedRestrictionSelectors(restrictionsModal);

                                // When a selection is made copy it to the overlapping input element to make it visible.
                                $("div.rtmgr select").change(function(evt) {
                                    var tgt = evt.target;
                                    var txt = $(tgt).parent().children("input");
                                    var opt = tgt.options[tgt.selectedIndex];
                                    txt.val(opt.text);

                                    $(opt).prop('selected', false);
                                    $(opt).parent().children("option:first").prop("selected", "selected");
                                });

                                // Delete action
                                $("div.rtmgr button.delete", restrictionsModal).click(function(evt) {
                                    var tgt = $(evt.target);
                                    var inp = tgt.parents('div.rtmgr').find("input");
                                    var name = inp.val();
                                    if (name == "") {
                                        addMessage("Specify the name of the restrictions being deleted.", 'ban', 'red');
                                    } else {
                                        localStorage.removeItem(lsPrefix + name);
                                        updateSavedRestrictionSelectors(restrictionsModal);
                                        inp.val("");
                                    }
                                });

                                // Save action (only one segment currently selected)
                                if (si.length == 1) {
                                    $("div.rtmgr button.save", restrictionsModal).click(function(evt) {
                                        var tgt = $(evt.target);
                                        var input = tgt.parents('div.rtmgr').find("input");
                                        var name = input.val();
                                        if (name == "") {
                                            addMessage("The restrictions require a name before they can be saved.", 'ban', 'red');
                                        } else {
                                            var dir = direction(tgt);
                                            var attrs = si[0].model.attributes;
                                            var src = attrs.restrictions;

                                            // Checking for pending updates to the selected segment's restrictions.  If found, save a copy of them.
                                            // This is a convenience feature that enables an editor to Apply a restriction change to a segment and then store it for re-use without first having to save it on the original segment.
                                            var actions = W.model.actionManager.getActions();
                                            for(var i = actions.length; i-- > 0;) {
                                                var action = actions[i];
                                                if (action.model.hasOwnProperty('subActions') && action.subActions[0].attributes.id == si[0].model.attributes.id && action.subActions[0].newAttributes.hasOwnProperty('restrictions')) {
                                                    src = action.subActions[0].newAttributes.restrictions;
                                                    break;
                                                }
                                            }

                                            var restrictions = [];
                                            for (i = 0;  i< src.length; i++) {
                                                var restriction = src[i];
                                                if (restriction._direction == dir) {
                                                    restrictions.push(restriction);
                                                }
                                            }

                                            restrictions = JSON.stringify(restrictions);

                                            clearMessages();
                                            if (clipboardTarget == name) {
                                                input.val(restrictions).select();
                                                document.execCommand('copy');
                                                input.val(clipboardTarget).blur();
                                                addMessage("Restrictions copied to clipboard");
                                            } else {
                                                localStorage.setItem(lsPrefix + name, restrictions);
                                                addMessage("Restrictions saved to " + name);
                                            }
                                            input.val("");

                                            updateSavedRestrictionSelectors(restrictionsModal);
                                        }
                                    });
                                } else {
                                    $("div.rtmgr button.save", restrictionsModal).click(function(evt) {
                                        clearMessages();
                                        addMessage("Save is only enabled when displaying the restrictions for a SINGLE segment", 'ban', 'red');
                                    });
                                }

                                // Apply saved restrictions to the current segment
                                $("div.rtmgr button.apply", restrictionsModal).click(function(evt) {
                                    var tgt = $(evt.target);
                                    var input = tgt.parents('div.rtmgr').find("input");
                                    var name = input.val().trim();
                                    if (name == "") {
                                        addMessage("Specify the name of the restrictions being applied.", 'ban', 'red');
                                    } else {
                                        var restrictions;

                                        input.val("");

                                        if (name.startsWith('[') & name.endsWith(']')) {
                                            restrictions = name;
                                        } else {
                                            restrictions = localStorage.getItem(lsPrefix + name);
                                        }
                                        restrictions = JSON.parse(restrictions).sort(compareRestrictions);

                                        var rsg = $(evt.target).parents("div.restriction-summary-group").first();
                                        var classes = rsg.attr('class').split(' ');
                                        classes.splice(classes.indexOf('restriction-summary-group'), 1);

                                        // Delete all current restrictions associated with the action's direction
                                        while (true) {
                                            var doDelete = "." + classes[0] + " .restriction-editing-actions i.do-delete";
                                            var deleteRestrictions = $(doDelete, restrictionsModal);

                                            if (deleteRestrictions.length == 0) {
                                                break;
                                            }

                                            deleteRestrictions.eq(0).click();
                                        }

                                        // Create new restrictions
                                        while (restrictions.length) {
                                            var restriction = restrictions.shift();

                                            $("." + classes[0] + " button.do-create", restrictionsModal).click();

                                            setSelector('disposition', restrictionsModal, restriction.disposition);
                                            setSelector('laneType', restrictionsModal, restriction.laneType);
                                            setValue('textarea[name="description"]', restrictionsModal, restriction.description);

                                            if (restriction.timeFrames != null && restriction.timeFrames.length != 0) {
                                                var weekdays = restriction.timeFrames[0].weekdays;

                                                var bit = 1;
                                                for(var idx = 0; idx < 7; idx++) {
                                                    var set = weekdays & bit;
                                                    set = (set != 0);
                                                    setCheck('input#day-ordinal-' + weekdayBit2Idx[bit] + '-checkbox', restrictionsModal, set);
                                                    bit <<= 1;
                                                }

                                                if (restriction.timeFrames[0].fromTime && restriction.timeFrames[0].toTime) {
                                                    setCheck("input#is-all-day-checkbox", restrictionsModal, false);
                                                    setValue("input.timepicker-from-time", restrictionsModal, restriction.timeFrames[0].fromTime);
                                                    setValue("input.timepicker-to-time", restrictionsModal, restriction.timeFrames[0].toTime);
                                                }

                                                if (restriction.timeFrames[0].startDate && restriction.timeFrames[0].endDate) {
                                                    setCheck("input#is-during-dates-on-radio", restrictionsModal, true);

                                                    // Ref: http://www.daterangepicker.com/
                                                    var drp = $('input.btn.datepicker', restrictionsModal).data('daterangepicker');

                                                    var re = /(\d{4})-(\d{2})-(\d{2})/;
                                                    var match = re.exec(restriction.timeFrames[0].startDate);
                                                    var startDate = match[2] + "/" + match[3] + "/" + match[1];

                                                    match = re.exec(restriction.timeFrames[0].endDate);
                                                    var endDate = match[2] + "/" + match[3] + "/" + match[1];

                                                    // WME's callback is fired by drp.hide().
                                                    drp.show();
                                                    drp.setStartDate(startDate);
                                                    drp.setEndDate(endDate);
                                                    drp.hide();

                                                    setCheck("input#repeat-yearly-checkbox", restrictionsModal, restriction.timeFrames[0].repeatYearly);
                                                }
                                            }

                                            var drivingModality;
                                            // if ALL vehicles are blocked then the default type is simply BLOCKED and the modality is blocked.
                                            if ("BLOCKED" == restriction.defaultType && !restriction.driveProfiles.hasOwnProperty("FREE") && !restriction.driveProfiles.hasOwnProperty("BLOCKED")) {
                                                drivingModality = "DRIVING_BLOCKED";
                                            } else {
                                                drivingModality = defaultType2drivingModality[restriction.defaultType];
                                            }

                                            setValue("select.do-change-driving-modality", restrictionsModal, drivingModality);

                                            var driveProfiles, driveProfile, i, j, vehicleType, plus, driveProfileItem, subscription;
                                            if (restriction.driveProfiles.hasOwnProperty("FREE")) {
                                                driveProfiles = restriction.driveProfiles.FREE;
                                                for(i = 0; i < driveProfiles.length; i++) {
                                                    driveProfile = driveProfiles[i];
                                                    var numDriveProfileItems = Math.max(1, driveProfile.vehicleTypes.length);

                                                    for(j = 0; j < numDriveProfileItems; j++) {
                                                        $("div.add-drive-profile-item.do-add-item", restrictionsModal).click();

                                                        vehicleType = driveProfile.vehicleTypes[j];

                                                        if (vehicleType !== undefined) {
                                                            plus = lastFaPlus(restrictionsModal);
                                                            plus.click();
                                                            driveProfileItem = plus.parents("div.drive-profile-item");
                                                            $("div.btn-group.open a.do-init-vehicle-type", driveProfileItem).click();

                                                            $("div.vehicle-type span.restriction-chip-content", driveProfileItem).click();

                                                            $('a.do-set-vehicle-type[data-value="' + vehicleType + '"]', driveProfileItem).click();
                                                        }

                                                        if (driveProfile.numPassengers > 0) {
                                                            plus = lastFaPlus(restrictionsModal);
                                                            plus.click();
                                                            driveProfileItem = plus.parents("div.drive-profile-item");
                                                            $("div.btn-group.open a.do-init-num-passengers", driveProfileItem).click();

                                                            if (driveProfile.numPassengers > 2) {
                                                                $("a.do-set-num-passengers[data-value='" + driveProfile.numPassengers + "']", driveProfileItem).click();
                                                            }
                                                        }

                                                        for(var k = 0; k < driveProfile.subscriptions.length; k++) {
                                                            subscription = driveProfile.subscriptions[k];

                                                            plus = lastFaPlus(restrictionsModal);
                                                            plus.click();
                                                            driveProfileItem = plus.parents("div.drive-profile-item");
                                                            $("div.btn-group.open a.do-init-subscription", driveProfileItem).click();


                                                            $("div.subscription span.restriction-chip-content", driveProfileItem).click();

                                                            $('a.do-set-subscription[data-value="' + subscription + '"]', driveProfileItem).click();
                                                        }
                                                    }
                                                }
                                            } else if (restriction.driveProfiles.hasOwnProperty("BLOCKED")) {
                                                driveProfiles = restriction.driveProfiles.BLOCKED;

                                                for(i = 0; i < driveProfiles.length; i++) {
                                                    driveProfile = driveProfiles[i];

                                                    if (driveProfile.vehicleTypes.length > 0) {
                                                        setCheck('input#all-vehicles-off-radio', restrictionsModal, true);

                                                        for(j = 0; j < driveProfile.vehicleTypes.length; j++) {
                                                            vehicleType = driveProfile.vehicleTypes[j];

                                                            setCheck('input#vehicle-type-' + vehicleType + '-checkbox', restrictionsModal, true);
                                                        }
                                                    }
                                                }
                                            }

                                            $("div.modal-footer button.do-create", restrictionsModal).click();
                                        }
                                        clearMessages();
                                        addMessage("Restrictions from " + name + " applied to current selection.");
                                    }
                                });
                            }
                        }
                    }
                }
            });
        });

        // configuration of the observer:
        var config = { attributes: false, childList: true, characterData: false, subtree: true };

        // pass in the target node, as well as the observer options
        observer.observe(observerTarget, config);

        initialized = true;
    }

     setTimeout(initializeRestrictionManager, 1000);
 })();