Просмотр исходного кода

feat: customize @inject-into in script settings (#1760)

tophf 2 лет назад
Родитель
Сommit
05d8829124

+ 4 - 2
src/_locales/en/messages.yml

@@ -382,8 +382,10 @@ labelInclude:
   description: Label of @include rules.
   message: '@include rules'
 labelInjectionMode:
-  description: Label for default option to inject scripts.
-  message: 'Default injection mode: '
+  description: >-
+    Label for option in advanced settings and in script settings.
+    Don't forget the space after ":" if the translated language separates words with spaces.
+  message: 'Injection mode: '
 labelInstall:
   description: Shown in the title of the confirm page while trying to install a script.
   message: Installing script

+ 11 - 15
src/background/utils/db.js

@@ -586,21 +586,17 @@ export async function parseScript(src) {
     result.isNew = true;
     result.update.message = i18n('msgInstalled');
   }
-  script.config = {
-    ...script.config,
-    ...src.config,
-    removed: 0, // force reset `removed` since this is an installation
-  };
-  script.custom = {
-    ...script.custom,
-    ...src.custom,
-  };
-  script.props = {
-    ...script.props,
-    lastModified: Date.now(),
-    lastUpdated: Date.now(),
-    ...src.props,
-  };
+  // Overwriting inner data by `src`, deleting keys for which `src` specifies `null`
+  for (const key of ['config', 'custom', 'props']) {
+    let dst = script[key];
+    if (!isObject(dst)) dst = script[key] = {};
+    if (key === 'props') dst.lastModified = dst.lastUpdated = Date.now();
+    src[key]::forEachEntry(([srcKey, srcVal]) => {
+      if (srcVal == null) delete dst[srcKey];
+      else dst[srcKey] = srcVal;
+    });
+  }
+  script.config.removed = 0; // force-resetting `removed` since this is an installation
   script.meta = meta;
   if (!getScriptHome(script) && isRemote(src.from)) {
     script.custom.homepageURL = src.from;

+ 3 - 6
src/background/utils/preinject.js

@@ -1,5 +1,7 @@
 import { getScriptName, getScriptPrettyUrl, getUniqId, sendTabCmd } from '@/common';
-import { BLACKLIST, HOMEPAGE_URL, META_STR, METABLOCK_RE, NEWLINE_END_RE } from '@/common/consts';
+import {
+  BLACKLIST, HOMEPAGE_URL, KNOWN_INJECT_INTO, META_STR, METABLOCK_RE, NEWLINE_END_RE,
+} from '@/common/consts';
 import initCache from '@/common/cache';
 import { forEachEntry, forEachKey, forEachValue, mapEntry, objectSet } from '@/common/object';
 import ua from '@/common/ua';
@@ -56,11 +58,6 @@ const META_KEYS_TO_PLURALIZE_RE = /^(?:(m|excludeM)atch|(ex|in)clude)$/;
 const pluralizeMetaKey = (s, consonant) => s + (consonant ? 'es' : 's');
 const pluralizeMeta = key => key.replace(META_KEYS_TO_PLURALIZE_RE, pluralizeMetaKey);
 const UNWRAP = 'unwrap';
-const KNOWN_INJECT_INTO = {
-  [AUTO]: 1,
-  [CONTENT]: 1,
-  [PAGE]: 1,
-};
 const propsToClear = {
   [S_CACHE_PRE]: CACHE_KEYS,
   [S_CODE_PRE]: true,

+ 6 - 0
src/common/consts.js

@@ -29,3 +29,9 @@ export const TIMEOUT_WEEK = 7 * 24 * 60 * 60 * 1000;
 export const BLACKLIST = 'blacklist';
 export const BLACKLIST_ERRORS = `${BLACKLIST}Errors`;
 export const RUN_AT_RE = /^document-(start|body|end|idle)$/;
+export const KNOWN_INJECT_INTO = {
+  // Using the default injection order: auto, page, content
+  [AUTO]: 1,
+  [PAGE]: 1,
+  [CONTENT]: 1,
+};

+ 15 - 8
src/options/views/edit/index.vue

@@ -92,7 +92,6 @@ import VmHelp from './help';
 
 const CUSTOM_PROPS = {
   name: '',
-  [RUN_AT]: '',
   homepageURL: '',
   updateURL: '',
   downloadURL: '',
@@ -101,6 +100,8 @@ const CUSTOM_PROPS = {
   origMatch: true,
   origExcludeMatch: true,
 };
+const fromProp = (val, key) => val ?? CUSTOM_PROPS[key];
+const toProp = val => val !== '' ? val : null; // `null` removes the prop from script object
 const CUSTOM_LISTS = [
   'include',
   'match',
@@ -114,10 +115,16 @@ const fromList = list => (
     : ''
 );
 const toList = text => (
-  text.split('\n')
-  .map(line => line.trim())
-  .filter(Boolean)
+  text.trim()
+    ? text.split('\n').map(line => line.trim()).filter(Boolean)
+    : null // `null` removes the prop from script object
 );
+const CUSTOM_ENUM = [
+  INJECT_INTO,
+  RUN_AT,
+];
+const fromEnum = val => val || '';
+const toEnum = val => val || null; // `null` removes the prop from script object
 let savedSettings;
 
 let shouldSavePositionOnSave;
@@ -252,10 +259,9 @@ export default {
       },
       custom: {
         // Adding placeholders for any missing values so deepEqual can work properly
-        ...CUSTOM_PROPS,
-        ...objectPick(custom, Object.keys(CUSTOM_PROPS)),
+        ...objectPick(custom, Object.keys(CUSTOM_PROPS), fromProp),
+        ...objectPick(custom, CUSTOM_ENUM, fromEnum),
         ...objectPick(custom, CUSTOM_LISTS, fromList),
-        [RUN_AT]: custom[RUN_AT] || '',
         noframes: noframes == null ? '' : +noframes, // it was boolean in old VM
       },
     };
@@ -307,8 +313,9 @@ export default {
             notifyUpdates: notifyUpdates ? +notifyUpdates : null,
           },
           custom: {
-            ...objectPick(custom, Object.keys(CUSTOM_PROPS)),
+            ...objectPick(custom, Object.keys(CUSTOM_PROPS), toProp),
             ...objectPick(custom, CUSTOM_LISTS, toList),
+            ...objectPick(custom, CUSTOM_ENUM, toEnum),
             noframes: noframes ? +noframes : null,
           },
           // User created scripts MUST be marked `isNew` so that

+ 27 - 1
src/options/views/edit/settings.vue

@@ -50,6 +50,20 @@
           </select>
         </td>
       </tr>
+      <tr>
+        <td>
+          <code>@inject-into</code>
+        </td>
+        <td>
+          <p v-text="i18n('labelInjectionMode')"/>
+        </td>
+        <td>
+          <select v-model="custom.injectInto" :disabled="readOnly">
+            <option value="" v-text="i18n('labelRunAtDefault')"/>
+            <option v-for="(_, mode) in KII" :key="mode" v-text="mode" />
+          </select>
+        </td>
+      </tr>
       <tr v-for="([ name, label ]) in textInputs" :key="name">
         <td>
           <code v-text="`@${name}`"/>
@@ -85,12 +99,19 @@
 
 <script>
 import { getScriptHome, i18n } from '@/common';
+import { KNOWN_INJECT_INTO } from '@/common/consts';
 import { objectGet } from '@/common/object';
+import { focusMe } from '@/common/ui';
 
 const highlightMetaKeys = str => str.match(/^(.*?)(@[-a-z]+)(.*)/)?.slice(1) || [str, '', ''];
 
 export default {
   props: ['active', 'settings', 'value', 'readOnly'],
+  data() {
+    return {
+      KII: KNOWN_INJECT_INTO,
+    };
+  },
   computed: {
     custom() {
       return this.settings.custom || {};
@@ -127,7 +148,7 @@ export default {
   watch: {
     active(val) {
       if (val) {
-        this.$refs.container.querySelector('input').focus();
+        focusMe(this.$el);
       }
     },
   },
@@ -179,5 +200,10 @@ $leftColWidth: 12rem;
     background: none;
     font-weight: bold;
   }
+  svg {
+    width: 16px;
+    height: 16px;
+    vertical-align: text-bottom;
+  }
 }
 </style>

+ 4 - 8
src/options/views/tab-settings/index.vue

@@ -91,7 +91,7 @@
           <label>
             <locale-group i18n-key="optionUiTheme">
               <select v-for="opt in ['uiTheme']" v-model="settings[opt]" :key="opt">
-                <option v-for="(title, value) in items[opt].enum" :key="`${opt}:${value}`"
+                <option v-for="(title, value) in items[opt].enum" :key="value"
                         :value="value" v-text="title" />
               </select>
             </locale-group>
@@ -101,8 +101,7 @@
           <label>
             <span v-text="i18n('labelInjectionMode')"></span>
             <select v-for="opt in ['defaultInjectInto']" v-model="settings[opt]" :key="opt">
-              <option v-for="(_, mode) in items[opt].enum" :key="`${opt}:${mode}`"
-                      :value="mode" v-text="mode" />
+              <option v-for="(_, mode) in items[opt].enum" :key="mode" v-text="mode" />
             </select>
             <a class="ml-1" href="https://violentmonkey.github.io/posts/inject-into-context/" target="_blank" rel="noopener noreferrer" v-text="i18n('learnInjectionMode')"></a>
           </label>
@@ -145,6 +144,7 @@
 import { reactive } from 'vue';
 import Tooltip from 'vueleton/lib/tooltip';
 import { debounce, i18n } from '@/common';
+import { KNOWN_INJECT_INTO } from '@/common/consts';
 import SettingCheck from '@/common/ui/setting-check';
 import { forEachEntry, mapEntry } from '@/common/object';
 import options from '@/common/options';
@@ -177,11 +177,7 @@ const items = {
     normalize: value => Math.max(0, Math.min(365, +value || 0)),
   },
   defaultInjectInto: {
-    enum: {
-      [AUTO]: '',
-      [PAGE]: '',
-      [CONTENT]: '',
-    },
+    enum: KNOWN_INJECT_INTO,
   },
   showAdvanced: {
     normalize: value => value,