您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Save, and load, restrictions from local storage.
当前为
- /* global $ */
- // ==UserScript==
- // @name Restriction Manager
- // @version 0.4
- // @description Save, and load, restrictions from local storage.
- // @namespace mailto:waze.kjg53@gmail.com
- // @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
- // @license GNU GPL v3
- // @author kjg53
- // ==/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;
- }
- // Convert list of saved restrictions into a string of HTML option elements.
- function allSavedRestrictionAsOptions() {
- var all = allSavedRestrictions();
- return all.length == 0 ? "" : "<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.restriction-validatation-region div.rtmgr").remove();
- }
- function addMessage(text) {
- var rvr = $("div.restriction-validation-region");
- var rvrul = $("ul", rvr);
- if (rvrul.length == 0) {
- rvr.append('<div><div class="restriction-validation-title">The Restrictions Manager encountered the following issue.</div><div class="collection-region"><ul></ul></div></div>');
- rvrul = $("ul", rvr);
- }
- rvrul.append('<li class="restriction-validation-error">' + text + '</li>');
- }
- var timeRegexp = /(\d\d?):(\d\d?)/
- function time2Int(time) {
- var m = timeRegexp.exec(time);
- return m == null ? 0 : (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) - time2Int(b.fromTime);
- if (c == 0) {
- c = time2Int(a.toTime) - time2Int(b.toTime);
- }
- return c;
- }
- }
- function compareRestrictions(a, b) {
- var c = compareTimeFrames(a.timeFrames, b.timeFrames);
- if (c == 0) {
- c = a.defaultType.localeCompare(b);
- }
- return c;
- }
- 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}');
- sheet.append('div.rtmgr-row {display: flex; flex-direction: row; justify-content: space-around}');
- sheet.append('div.rtmgr btn {margin-top: 5px}');
- 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}');
- // 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("h3.modal-title").first();
- var title = modalTitle.text().replace(/[\x00-\x1F\x7F-\x9F]/g, "");
- if ("Time based restrictions" == title) {
- if (modalTitle.data('rtmgr') === undefined) {
- // Flag this modal as having already augmented
- modalTitle.data('rtmgr', true);
- // Add the UI elements to the modal
- $("div.restriction-summary-group div.restriction-summary-title", restrictionsModal)
- .append (
- ""
- + "<div class='rtmgr rtmgr-column'>"
- + "<div class='name'>"
- + "<input type='text'/>"
- + "<select/>"
- + "</div>"
- + "<div class='rtmgr-row'>"
- + "<button class='btn save'>Save</button>"
- + "<button class='btn apply'>Apply</button>"
- + "<button class='btn delete'>Delete</button>"
- + "</div>"
- + "</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 text = tgt.options[tgt.selectedIndex].text;
- txt.val(text);
- });
- // 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 != "") {
- 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 != "") {
- var dir = direction(tgt);
- var attrs = si[0].model.getAttributes();
- 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.
- for(var i = W.model.actionManager.actions.length; i-- > 0;) {
- var action = W.model.actionManager.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);
- if (clipboardTarget == name) {
- input.val(restrictions).select();
- document.execCommand('copy');
- input.val(clipboardTarget).blur();
- } else {
- localStorage.setItem(lsPrefix + name, restrictions);
- }
- 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");
- });
- }
- // 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 != "") {
- var restrictions;
- 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();
- }
- }
- 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];
- $("div.add-drive-profile-item.do-add-item", restrictionsModal).click();
- for(j = 0; j < driveProfile.vehicleTypes.length; j++) {
- vehicleType = driveProfile.vehicleTypes[j];
- 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 + "']").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();
- }
- }
- });
- }
- }
- }
- }
- });
- });
- // 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);
- })();